Закрытие Python с побочными эффектами

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

def closureMaker():
  x = 0
  def closure():
    x+=1
    print x
  return closure

a = closureMaker()
a()
1
a()
2

Очевидно, что то, что я надеюсь сделать, сложнее, но этот пример иллюстрирует то, о чем я говорю.


person Mike    schedule 03.07.2011    source источник
comment
Я бы отметил это как дубликат других вопросов, за исключением того, что другие вопросы действительно должны быть помечены как дубликат этого; этот вопрос довольно прост, по существу и хорошо написан. Но также см. stackoverflow.com/questions/141642/   -  person ninjagecko    schedule 04.07.2011
comment
Значит, нет возможности объединить два или более вопросов? ;п   -  person André Laszlo    schedule 04.07.2011


Ответы (2)


Вы не можете сделать именно это в Python 2.x, но вы можете использовать трюк, чтобы получить тот же эффект: использовать изменяемый объект, такой как список.

def closureMaker():
    x = [0]
    def closure():
        x[0] += 1
        print x[0]
    return closure

Вы также можете сделать x объектом с именованным атрибутом или словарем. Это может быть более удобочитаемым, чем список, особенно если вам нужно изменить более одной такой переменной.

В Python 3.x вам просто нужно добавить nonlocal x к вашей внутренней функции. Это приводит к тому, что назначения x переходят во внешнюю область.

person interjay    schedule 03.07.2011
comment
Пожалуйста, объясните, почему? Я пытаюсь найти что-нибудь об этом в документации по python. - person André Laszlo; 04.07.2011
comment
@André: PEP 3104 информативен, если технический: python.org/dev/peps/ pep-3104 - person Thomas K; 04.07.2011
comment
Мне кажется, что это должно работать в любом случае. Я только что прочитал это: Если определение встречается в функциональном блоке область действия распространяется на любые блоки, содержащиеся в определяющем блоке, если только содержащийся блок не вводит другую привязку для имени. - person André Laszlo; 04.07.2011
comment
Ах, значит, когда вы пытаетесь присвоить x в примере, приведенном в вопросе, это не работает, потому что вы пытаетесь перепривязать переменную? Подлый. - person André Laszlo; 04.07.2011
comment
@André: Когда переменная присваивается в функции (включая расширенные операторы присваивания, такие как +=), она становится локальной переменной этой функции. Таким образом, в исходном коде x является локальной переменной closureMaker (из-за x=0), а еще один x становится локальной переменной closure (из-за x+=1). При использовании вместо этого списка нет прямого присвоения x в closure, поэтому используется та же переменная, что и closureMaker. - person interjay; 04.07.2011
comment
Да, я наконец понял. :) Спасибо! - person André Laszlo; 04.07.2011