Наргоут в 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))