Методы возврата структур из функций Python подробно обсуждались в различных сообщениях. Два хороших здесь и здесь.
Однако, если я не пропустил это, ни одно из предложенных решений не определяет структуру в том же месте, где установлены ее элементы, и вместо этого либо повторяют список элементов при назначении (не DRY), либо полагаются на позицию (подвержено ошибкам).
Я ищу СУХОЙ способ сделать это как для скорости записи, так и для того, чтобы избежать ошибок несовпадения аргументов, распространенных, когда вы повторяетесь.
В приведенном ниже фрагменте кода показаны три попытки сделать это. Для краткости структура примера содержит только один элемент, но намерение, очевидно, состоит в том, чтобы структуры содержали несколько элементов.
Эти три метода являются СУХИМИ, встраивая определение структуры с инициализацией возвращаемого экземпляра.
Метод 1 подчеркивает потребность в лучшем способе, но иллюстрирует искомый синтаксис DRY, где структура и способ ее заполнения (решается во время выполнения) находятся в одном и том же месте, а именно в вызове dict()
.
Метод 2 использует typing.NamedTuple
и, кажется, работает. Однако для этого используются изменяемые значения по умолчанию.
Метод 3 следует подходу метода 2, используя dataclasses.dataclass
, а не typing.NamedTuple
. Это терпит неудачу, потому что первый явно запрещает изменяемые значения по умолчанию, повышая ValueError: mutable default is not allowed
from collections import namedtuple
from dataclasses import dataclass
from typing import NamedTuple, List, Tuple
# Method 1
def ret_dict(foo_: float, bar_: float) -> Tuple:
return_ = dict(foo_bar=[foo_, bar_])
_ = namedtuple('_', return_.keys())
return _(*return_.values())
# Method 2
def ret_nt(foo_: float, bar_: float) -> 'ReturnType':
class ReturnType(NamedTuple):
foo_bar: List[float] = [foo_, bar_] # Mutable default value allowed
return ReturnType()
# Method 3
def ret_dc(foo_: float, bar_: float) -> 'ReturnType':
@dataclass
class ReturnType:
foo_bar: List[float] = [foo_, bar_] # raises ValueError: mutable default is not allowed
return ReturnType()
def main():
rt1 = ret_dict(1, 0)
rt1.foo_bar.append(3)
rt2 = ret_dict(2, 0)
print(rt1)
print(rt2)
rt1 = ret_nt(1, 0)
rt1.foo_bar.append(3) # amending the mutable default does not affect subsequent calls
rt2 = ret_nt(2, 0)
print(rt1)
print(rt2)
rt1 = ret_dc(1, 0)
rt1.foo_bar.append(3) # amending the default does not affect subsequent calls
rt2 = ret_dc(2, 0)
print(rt1)
print(rt2)
if __name__ == "__main__":
main()
Возникают следующие вопросы:
Является ли метод 2 разумным питоническим подходом?
Одна из проблем заключается в том, что изменяемые значения по умолчанию являются своего рода табу, особенно для аргументов функций. Однако мне интересно, допустимо ли их использование здесь, учитывая, что прилагаемый код предполагает, что эти значения по умолчанию NamedTuple
(и, возможно, все определение ReturnType
) оцениваются при каждом вызове функции, в отличие от значений аргументов функции по умолчанию, которые, как мне кажется, оцениваются только один раз и сохраняться вечно (отсюда и проблема).
Еще одна проблема заключается в том, что модуль dataclasses, похоже, явно запретил такое использование. Было ли это решение чрезмерно догматичным в данном случае? или защита от метода 2 оправдана?
Это неэффективно?
Я был бы счастлив, если бы синтаксис метода 2 означал:
1 - определить ReturnType
один раз только при первом проходе
2 - вызывать __init__()
с заданной (динамически заданной) инициализацией на каждом проходе
Однако я боюсь, что вместо этого это может означать следующее:
1 - Определить ReturnType
и его значения по умолчанию при каждом проходе
2 - вызывать __init__()
с заданной (динамически установленной) инициализацией на каждом проходе
Следует ли беспокоиться о неэффективности переопределения коротких ReturnType
на каждом проходе, когда вызов зациклен? Разве эта неэффективность не проявляется всякий раз, когда класс определяется внутри функции? Должны ли классы определяться внутри функций?
Есть ли способ (надеюсь, хороший) для создания экземпляра определения DRY с использованием нового модуля dataclasses
(python 3.7)?
И наконец, есть ли лучший синтаксис определения DRY?