Как узнать количество выходов, возвращаемых функцией python?

Представьте, что есть две функции Python:

def f1(x):
 return x

def f2(x):
 return x,x**2

Я хотел бы узнать, сколько выходов возвращается f1 и сколько выходов возвращается f2.

Я хотел бы избежать следующего решения:

result = f1(1)
no_outputs = len(result) if type(result) == tuple else 1

Поскольку это решение терпит неудачу, когда функция возвращает кортеж.

Я начал играть с библиотекой python ast (абстрактное синтаксическое дерево) и задавался вопросом, есть ли способ проанализировать дерево синтаксиса для функции, чтобы выяснить количество возвращенных результатов?

Что-то вроде:

import inspect
import ast

src = inspect.getsourcelines(f1)[0]

string = ''.join(src)
ast_ = ast.parse(string)

Я исследовал ast_ но не могу найти, что такое возврат.

2 ответа

Технически функция возвращает значение ONE. Это одно значение может быть кортежем, словарем или любым произвольно сложным объектом (включая объекты коллекции).

Появление нескольких возвращаемых значений является просто результатом некоторого синтаксического сахара вокруг автоматической распаковки и того факта, что оператор кортежа в Python на самом деле является (запятая, скобки не нужны в операторе возврата и необходимы только в тех случаях, когда, (запятая) будет неоднозначной).

Поскольку Python динамически вводится, функция может возвращать разные типы значений (объектов) в зависимости от своих аргументов или глобального программного состояния программы при ее вызове. Он может возвращать различные типы объектов для разных вызовов во время сеанса одной и той же программы (интерпретатор/виртуальная машина).

Вы должны учитывать, что возвращаемый тип функции относится к документации... любая хорошо написанная функция Python будет документировать ее возвращаемый тип (предпочтительно в строке документа).

Это довольно распространено для методов классов, которые обернуты вокруг некоторой структуры данных, чтобы вернуть self..., что позволяет использовать шаблон "цепного использования": Foo.fiddle(). Faddle(). Spritz(). Output().. где мы выполняем целую серию операций над объектом в едином, сжатом выражении.


Я согласен с комментариями, что это несколько любопытный вопрос... Однако, чтобы ответить на ваш вопрос в соответствии с предложенным вами подходом, давайте начнем со следующих упрощающих предположений:

  1. Существует только один оператор возврата, который встречается в самом конце каждой функции

  2. Запятые могут использоваться для определения "количества выходов", то есть (x, 2) и x, 2 считаются двумя "выходами",

  3. Следуя из пункта 2, операторы return состоят из ряда переменных (возможно, как списки или кортежи) и никогда более сложных выражений, таких как генераторы или списки

Мы предполагаем это выше, потому что в противном случае вам почти наверняка необходимо правильно разобрать подмножество действительного кода Python, например, путем создания AST. Учитывая вышеизложенное, вы берете последнюю строку из списка, возвращаемого inspect.getsourcelines() и сопоставляете регулярное выражение:

In [47]: def f2(x):
 ....: return x, x**2

In [48]: ret2 = inspect.getsourcelines(f2)

In [49]: ret2
Out[49]: ([u'def f2(x):\n', u' return x, x**2\n'], 1)

In [55]: ret_pattern = r'return\s*(.*)\n*$'

In [56]: out2 = re.findall(ret_pattern, ret2[0][-1])

In [57]: out2
Out[57]: [u'x, x**2']

In [58]: len(out2[0].split(','))
Out[58]: 2

Тестирование с возвратом кортежа переменных:

In [59]: def f3(x):
 ....: return (x, x**2, x**3)
 ....:

In [62]: out3 = re.findall(ret_pattern, inspect.getsourcelines(f3)[0][-1])

In [63]: len(out3[0].split(','))
Out[63]: 3

licensed under cc by-sa 3.0 with attribution.