Nargout в Python - программирование
Подтвердить что ты не робот

Nargout в Python

Есть ли у Python эквивалент nargout в MATLAB? Я нахожу nargout очень аккуратным подходом, если мы хотим, чтобы количество возвращаемых параметров было гибким. Есть ли способ узнать, сколько запросов было задано? Что-то вроде следующего псевдо-питон-кода:

def var_returns_func(a):
  """
  a is a 1D numpy array
  """
  if nargout==1: return a.sum()
  elif nargout==2: return a.sum(),a.std()

Поэтому, если я вызываю эту функцию как mean = var_returns_func(someNumPyarray), она должна вернуть одно значение. Но если я назову его mean,std = var_returns_func(someNumPyarray), он должен вернуть 2 значения.

Есть ли питонический способ сделать это? Или хакерский способ?

4b9b3361

Ответ 1

Функция не может знать, что будет сделано с возвращаемым значением, поэтому она не может знать, сколько из них требуется. Что вы могли бы сделать, это передать nargout в качестве аргумента вашей функции и использовать это, чтобы решить, что вернуть:

def f(a, nargout=1):
    if nargout == 1:
        return "one value"
    elif nargout == 2:
        return "two", "values"
    else:
        raise ValueError, "Invalid nargout!"

Это пример "явный лучше, чем неявный", как философия Python. Если вам нужны два аргумента, вы должны явно указать, что вам нужно два аргумента. Непосредственное решение на основе поиска в будущем, чтобы увидеть, что будет сделано с результатом, не рекомендуется в Python. Совершенно можно сделать a = f(x) и получить двухэлементный кортеж в a.

Для таких примеров, как ваш, есть много лучших способов. Один из них заключается в том, чтобы просто сделать mean, std = a.mean(), a.std() или, в более общем плане, x, y = someFunc(a), otherFunc(a). Если эта конкретная комбинация значений обычно необходима, и есть несколько дорогостоящих вычислений, разделяемых обеими операциями, которые вы не хотите дублировать, создайте третью функцию, которая явно возвращает оба и делает x, y = funcThatReturnsTwoThings(a). Все это явные способы сохранения функций отдельно, если они делают разные вещи.

Ответ 2

Я не могу говорить за nargout в MATLAB, поскольку я этого не знаю, и я не могу представить, как его следует использовать правильно. Однако вы можете изменить свое представление на то, что действительно делает функция (или метод) Python).

Собственно, Python всегда возвращает ровно одно значение. Или это None, или это одно значение какого-либо определенного типа, или это единственный объект типа tuple.

Если нет команды return, функция возвращает None, если тело функции заканчивается. Это то же самое, что если вы явно написали return без аргументов в конце тела или return None в конце тела.

Если вы используете return None (None может быть сохранен в переменной) или return без аргументов, None возвращается к вызывающему.

Если вы return single, объект single возвращается к вызывающему.

Если вы return v1, v2, v3, вы фактически вернете кортеж (v1, v2, v3). Это просто синтаксический сахар, что вам не нужно писать круглые скобки.

result1, result2, result3 = f() в этом случае является еще одним синтаксическим сахаром. f() возвращает кортеж, и его элементы автоматически извлекаются в заданные переменные. Вы на самом деле:

result1, result2, result3 = (v1, v2, v3)

или

t = f()                           # where t is the (v1, v2, v3)
result1, result2, result3 = t

Собственно, Python не позволяет определять выходные аргументы, как это обычно бывает на других языках. Вы можете думать, что вы передаете адреса объектов аргумента, и если переданный объект позволяет изменять, вы можете его изменить. Но вы никогда не сможете получить новый результат для переданного аргумента, который первоначально имел значение None, например. Вы не можете назначить переменную Python через выходной аргумент функции - нет такого механизма в Python.

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

Если вы хотите протестировать в коде вызывающего абонента то, что было фактически возвращено, вы можете написать свою собственную функцию, которая делает что-то особенное в случаях, когда были возвращены следующие значения: None, single, кортеж некоторой длины (len(t) можно использовать для получения количества элементов в возвращаемом кортеже t). Ваша функция также может тестировать тип single или каждого из элементов кортежа и работать соответственно.

Ответ 3

В Matlab количество выходных аргументов фактически является одним из входов в функцию. Это не относится к Python, поэтому вы настроили интерфейс функции, чтобы отразить это по-другому.

Например, здесь функция, которая применяет upper() к группе строк, и пользователь может ожидать, что количество входов равно количеству выходов. Синтаксис также очень похож на Matlab's.

>>> def var_returns_f(*args):
...     return [str.upper() for str in args]
... 
>>> 
>>> a, b = var_returns_f('one', 'two')
>>> 
>>> a
'ONE'
>>> b
'TWO'

Ответ 4

Я стараюсь, чтобы моя функция возвращала все (как элементы кортежа), а затем распаковывала только те, которые мне нужны:

def my_func():
    # do stuff...
    return mean, stddev, otherstat, a, b

mu, sig, _, a, _ = myfunc()

Здесь Im возвращает все, но только назначая 1-ю и 4-ю переменные переменным, которые я буду использовать в области вызова. Переменная _ - это выброс, чтобы действовать в качестве заполнителя для переменных, которые мне не нужны/нужны.

Ответ 5

У меня странная идея...

def nargout():
   import traceback
   callInfo = traceback.extract_stack()
   callLine = str(callInfo[-3].line)
   split_equal = callLine.split('=')
   split_comma = split_equal[0].split(',')
   return len(split_comma)

def fun():
   this = nargout()
   if this == 1:
       return 3
   else:
       return 3, 4

a = fun()
a, b = fun()

Я действительно скучаю по Matlab сейчас: D


Улучшено:

def nargout(*args):
   import traceback
   callInfo = traceback.extract_stack()
   callLine = str(callInfo[-3].line)
   split_equal = callLine.split('=')
   split_comma = split_equal[0].split(',')
   num = len(split_comma)
   return args[0:num] if num > 1 else args[0]

def fun(nparray):
   return nargout(np.mean(nparray), np.std(nparray))

arr = np.array([3, 4, 5])
mean = fun(arr)
mean, std = fun(arr)