Наргоут в 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 значения.

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

6 ответов

Функция не может знать, что будет сделано с возвращаемым значением, поэтому она не может знать, сколько нужно. Что вы могли бы сделать, это пройти 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) и хочу получить кортеж из двух элементов в.

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

Я не могу говорить за 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 или каждого из элементов кортежа и работать соответственно.

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

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

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

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

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

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)

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

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

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

Вдохновившись идеей @yiping jiao, я сделал декоратор:

      from functools import wraps
from traceback import extract_stack

def with_nargout(func):
    @wraps(func)
    def _with_nargout(*args, **kwargs):
        global nargout
        call_line = extract_stack()[-2].line
        if '=' in call_line:
            nargout = len(call_line.split('=')[0].split(','))
        else:
            nargout = 0
        result = func(*args, **kwargs)
        del nargout
        return result
    return _with_nargout

пример:

      @with_nargout
def func_1(x, y):
    print('nargout is', nargout)
    if nargout == 1:
        return x + y
    elif nargout == 2:
        return x + y, nargout
    print('returning None')

a = func_1(14, 53)
a, b = func_1(15, 43)
print(func_1(14, 39))
Другие вопросы по тегам