Возврат промежуточных результатов из функции в Python
представьте, что у меня есть модуль Python с некоторой функцией в нем:
def sumvars(x, y, z):
s = x
s += y
s += z
return s
но иногда я хочу получить результаты некоторых промежуточных вычислений (например, у меня может быть функция, которая меняет матрицу и хотела бы знать определитель, который был вычислен как промежуточный шаг). Очевидно, я не хотел бы снова переделывать эти вычисления, если они уже были сделаны в этой функции.
моя первая идея-вернуть дикт:
def sumvars(x, y, z):
d = {}
s = x
d['first_step'] = s
s += y
d['second_step'] = s
s += z
d['final'] = s
return d
но я не помню никаких функций в numpy или scipy, которые возвращают дикты, и поэтому кажется, что это может быть не очень хорошая идея. (Почему?) Также обычно мне всегда придется печатать sumvars(x,y,z)['final']
для возвращаемого значения по умолчанию...
другой вариант, который я вижу, - это создание глобальных переменных, но кажется неправильным иметь кучу их в моем модуле, мне нужно будет запомнить их имена и, кроме того, не быть прикрепленным к самой функции, выглядит как плохой выбор дизайна.
что было бы правильной конструкцией функции для такой ситуации?
7 ответов
обычно, когда у вас есть два разных способа возврата данных, продолжайте и сделайте две разные функции. "Квартира лучше, чем вложенная", в конце концов. Просто позвоните друг другу, чтобы не повторяться.
например, в стандартной библиотеке, urllib.parse
и parse_qs
(который возвращает dict
) и parse_qsl
(который возвращает list
). parse_qs
как раз тогда звонит другой:
def parse_qs(...):
parsed_result = {}
pairs = parse_qsl(qs, keep_blank_values, strict_parsing,
encoding=encoding, errors=errors)
for name, value in pairs:
if name in parsed_result:
parsed_result[name].append(value)
else:
parsed_result[name] = [value]
return parsed_result
довольно проста. Так в вашем примере кажется прекрасно иметь
def sumvars(x, y, z):
return sumvars_with_intermediates(x, y, z).final
def sumvars_with_intermediates(x, y, z):
...
return my_namedtuple(final, first_step, second_step)
(Я за возвращение namedtuple
s вместо dict
s от моего APIs, это просто красивее)
еще один очевидный пример-в re
: re.findall
своя собственная функция, не некоторый флаг конфигурации к search
.
теперь стандартная библиотека-это растянутая вещь, сделанная многими авторами, поэтому вы найдете контрпримеры к каждому примеру. Вы гораздо чаще будете видеть вышеуказанный шаблон, а не одну функцию omnibus, которая принимает некоторые однако флаги конфигурации, и я нахожу его гораздо более читаемым.
поместите общий расчет в свою собственную функцию, как рекомендовал Jayanth Koushik, если этот расчет можно назвать соответствующим образом. Если вы хотите вернуть много значений (промежуточный результат и конечный результат) из одной функции, то дикт может быть излишним в зависимости от вашей цели, но в python гораздо более естественно просто вернуть кортеж, если ваша функция имеет много значений для возврата:
def myfunc():
intermediate = 5
result = 6
return intermediate, result
# using the function:
intermediate, result = myfunc()
Не уверен, что атрибуты функций-хорошая идея:
In [569]: def sumvars(x, y, z):
...: s = x
...: sumvars.first_step = s
...: s += y
...: sumvars.second_step = s
...: s += z
...: return s
In [570]: res=sumvars(1,2,3)
...: print res, sumvars.first_step, sumvars.second_step
...:
6 1 3
Примечание: как упоминал @BrenBarn, эта идея так же, как глобальные переменные, ранее рассчитанных "промежуточные результаты" не могут быть сохранены, когда вы хотите использовать их.
просто придумал эту идею, которая может быть лучшим решением:
def sumvars(x, y, z, mode = 'default'):
d = {}
s = x
d['first_step'] = s
s += y
d['second_step'] = s
s += z
d['final'] = s
if mode == 'default':
return s
else:
return d
Я считаю, что правильное решение-использовать класс, чтобы лучше понять, что вы моделируете. Например, в случае матрицы вы можете просто сохранить определитель в атрибуте "определитель".
вот пример использования примера матрицы.
class Matrix:
determinant = 0
def calculate_determinant(self):
#calculations
return determinant
def some_method(self, args):
# some calculations here
self.determinant = self.calculate_determinant()
# other calculations
matrix = Matrix()
matrix.some_method(x, y, z)
print matrix.determinant
Это также позволяет разделить ваш метод на более простые методы, например, для вычисления определителя вашей матрицы.
другой вариант:
def sumvars(x, y, z, d=None):
s = x
if not d is None:
d['first_step'] = s
s += y
if not d is None:
d['second_step'] = s
s += z
return s
функция всегда возвращает нужное значение без упаковки его в кортеж или словарь. Промежуточных результатов по-прежнему доступны, но только по запросу. Зов
sumvars(1, 2, 3)
просто возвращает 6
без хранения промежуточных значений. Но зов! .. --6-->
d = {}
sumvars(1, 2, 3, d)
возвращает один и тот же ответ 6
и вставляет промежуточные вычисления в исходном словаре.
1. сделать две отдельные функции.
2. использовать генератор:
>>> def my_func():
... yield 1
... yield 2
...
>>> result_gen = my_func()
>>> result_gen
<generator object my_func at 0x7f62a8449370>
>>> next(result_gen)
1
>>> next(result_gen)
2
>>> next(result_gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>