Итак, мне любопытно мнение более опытных программистов-питонов по следующему вопросу стиля. Предположим, что я создаю функцию, которая собирается итерации по строкам через фреймворк pandas или любую аналогичную usecase, где функция требует доступа к ее предыдущему состоянию. По-видимому, существует как минимум четыре способа реализовать это в python:
1) Закрытие:
def outer():
previous_state = None
def inner(current_state) :
nonlocal previous_state
#do something
previous_state=current_state
return something
Итак, если вы исходите из фона javascript, это, несомненно, будет для вас естественным. В python это тоже довольно естественно, вплоть до того, что вам нужно получить доступ к охватывающей области, когда вы в конце концов сделаете что-то вроде inner.__code__.co_freevars
, которое даст вам имена ваших охватывающих переменных как кортеж и найдет индекс тот, который вы хотите, а затем перейдите к inner.__closure__[index].cell_contents
, чтобы получить его значение. Не совсем элегантный, но я полагаю, что точка часто скрывает область видимости, поэтому имеет смысл, что ее трудно достичь. С другой стороны, также кажется немного странным, что python делает закрывающую функцию частной, когда она почти полностью покончила с частной переменной по сравнению с языками ООП.
2) Функтор
def outer():
def inner(current_state):
#do something
inner.previous_state=current_state
return something
ret = inner
ret.previous_state=None
return ret
Это "открывает закрытие", поскольку теперь охватывающее состояние полностью отображается как атрибут функции. Это работает, потому что функции на самом деле просто маскируют объекты. Я склоняюсь к этому как к самому пифоническому. Его ясная, краткая и читаемая.
3) Объекты Это, вероятно, наиболее знакомый программистам ООП
class Calculator(Object) :
def __init__(self):
self.previous_state=None
def do_something(self, current_state) :
#do_something
self.previous_state = current_state
return something
Самая большая проблема заключается в том, что вы, как правило, получаете множество определений классов. Это хорошо на полностью OOP-языке, таком как Java, где у вас есть интерфейсы и т.д., Чтобы управлять этим, но на утином языке кажется немного странным, чтобы иметь много простых классов, чтобы нести вокруг функции, которая нуждается в немного состоянии.
4) globals - я не буду демонстрировать это, поскольку я специально хочу избежать загрязнения глобального пространства имен
5) Декораторы - это немного криволинейный шар, но вы можете использовать декораторы для хранения информации о частичном состоянии.
@outer
def inner(previous_state, current_state):
#do something
return something
def outer(inner) :
def wrapper(current_state) :
result = inner(wrapper.previous_state, current_state)
wrapper.previous_state = current_state
return result
ret = wrapper
ret.previous_state=None
return result
Этот тип синтаксиса менее всего мне знаком, но если я теперь позвоню
func = inner
Я действительно получаю
func = outer(inner)
а затем повторное вызов func()
действует так же, как пример функтора. На самом деле я действительно ненавижу этот путь больше всего. Мне кажется, что у меня действительно непрозрачный синтаксис в том, что не ясно, будет ли вызов внутреннего (current_state) много раз давать вам тот же результат или если он даст вам новую функцию, каждый раз, так что это кажется плохой практикой для создания декораторов, которые таким образом добавляют состояние к функции.
Так что это правильный способ? Какие плюсы и минусы я пропустил здесь?