Я пытаюсь реализовать закрытие в Python 2.6, и мне нужно получить доступ к нелокальной переменной, но похоже, что это ключевое слово недоступно в python 2.x. Как получить доступ к нелокальным переменным в замыканиях в этих версиях Python?
117
def inner(): print d; d = {'y': 1}
. Здесьprint d
читается внешнее,d
создавая нелокальную переменнуюd
во внутренней области.X = 1
просто связывает имяX
с конкретным объектом (int
значением1
).X = 1; Y = X
связывает два имени с одним и тем же точным объектом. Во всяком случае, некоторые объекты являются изменяемыми и вы можете изменить их значение.Следующее решение основано на ответе Элиаса Замарии , но, в отличие от этого ответа, правильно обрабатывает несколько вызовов внешней функции. «Переменная»
inner.y
является локальной для текущего вызоваouter
. Только это не переменная, поскольку это запрещено, а атрибут объекта (объект является самой функциейinner
). Это очень некрасиво (обратите внимание, что атрибут может быть создан только после определенияinner
функции), но кажется эффективным.источник
inc()
и a,dec()
возвращенные из внешнего, увеличивают и уменьшают общий счетчик. Затем вам нужно решить, к какой функции присоединить текущее значение счетчика, и ссылаться на эту функцию из другой (-ых). Что выглядит несколько странно и асимметрично. Например, вdec()
строке вродеinc.value -= 1
.Вместо словаря в нелокальном классе меньше беспорядка . Изменение @ ChrisB в примере :
затем
Каждый вызов external () создает новый и отличный класс, называемый контекстом (а не просто новый экземпляр). Таким образом, @Nathaniel избегает опасений по поводу общего контекста.
источник
__slots__ = ()
и создавая объект вместо использования класса, напримерcontext.z = 3
, подниметAttributeError
. Это возможно для всех классов, если они не наследуются от класса, не определяющего слоты.Я думаю, что главное здесь то, что вы подразумеваете под «доступом». Не должно быть проблем с чтением переменной за пределами области закрытия, например,
должен работать должным образом (печать 3). Однако переопределение значения x не работает, например,
по-прежнему будет печатать 3. Насколько я понимаю, PEP-3104 это то, что должно охватывать ключевое слово nonlocal. Как упоминалось в PEP, вы можете использовать класс для выполнения того же самого (что-то вроде беспорядка):
источник
def ns(): pass
последующимns.x = 3
. Это некрасиво, но на мой взгляд немного менее уродливо.class Namespace: x = 3
?ns
- это глобальный объект, поэтому вы можете ссылатьсяns.x
на уровень модуля вprint
инструкции в самом конце ,Есть еще один способ реализовать нелокальные переменные в Python 2, если какой-либо из ответов здесь по какой-либо причине нежелателен:
Излишне использовать имя функции в операторе присваивания переменной, но мне это кажется проще и чище, чем помещать переменную в словарь. Значение запоминается от одного вызова к другому, как и в ответе Криса Б.
источник
f = outer()
а затем сделаете это позжеg = outer()
,f
счетчик будет сброшен. Это связано с тем, что у них обоих однаouter.y
переменная, а не каждая собственная независимая переменная. Хотя этот код выглядит более эстетично, чем ответ Криса Б., его способ кажется единственным способом имитировать лексическую область видимости, если вы хотите вызватьouter
более одного раза.outer.y
не включает ничего локального по отношению к вызову функции (экземпляру)outer()
, но присваивается атрибуту объекта функции, который привязан к имениouter
в его охватывающей области. И поэтому можно было бы одинаково хорошо использовали, в письменном видеouter.y
, любое другое имя вместоouter
, при условии , что , как известно, связаны в этой области. Это верно?outer.y
использования имениinner.y
(посколькуinner
оно связано внутри вызоваouter()
, что является именно той областью, которую мы хотим), но поместив инициализацияinner.y = 0
после определения внутреннего (поскольку объект должен существовать при создании его атрибута), но, конечно, доreturn inner
?Вот что-то, вдохновленное предложением, которое Алоис Махдал сделал в комментарии относительно другого ответа :
Обновить
Оглядываясь на это недавно, я был поражен тем, насколько он похож на декоратор - когда меня осенило, что реализация его как одного из вариантов сделает его более универсальным и полезным (хотя, возможно, это в некоторой степени ухудшает его читабельность).
Обратите внимание, что обе версии работают как в Python 2, так и в 3.
источник
В правилах определения области видимости Python есть бородавка - назначение делает переменную локальной по отношению к непосредственно охватывающей ее области функции. Для глобальной переменной это можно решить с помощью
global
ключевого слова.Решение состоит в том, чтобы ввести объект, который является общим для двух областей действия, который содержит изменяемые переменные, но сам ссылается на переменную, которая не назначена.
Альтернативой является взлом некоторых областей:
Возможно, вам удастся придумать некоторые уловки, чтобы получить имя параметра
outer
, а затем передать его как varname, но не полагаясь на имя, котороеouter
вам нужно, чтобы использовать комбинатор Y.источник
nonlocal
.locals()
создает словарьouter()
s locals в то время, когдаinner()
определено, но изменение этого словаря не меняетv
inouter()
. Это больше не будет работать, когда у вас будет больше внутренних функций, которые хотят поделиться закрытой переменной. Замолвитеinc()
иdec()
что увеличение и уменьшение общего счетчика.nonlocal
- это функция Python 3.nonlocal
в Python 2 в целом . Ваши идеи не охватывают общий случай, а только те, у которых есть одна внутренняя функция. Взглянем на эту суть для примера. Обе внутренние функции имеют свой собственный контейнер. Вам нужен изменяемый объект в области внешней функции, как уже предлагали другие ответы.nonlocal
ключевого слова, введенного в Python 3.Другой способ сделать это (хотя и слишком многословно):
источник
Расширяя элегантное решение Мартино выше до практичного и несколько менее элегантного варианта использования, я получаю:
источник
Используйте глобальную переменную
Лично мне не нравятся глобальные переменные. Но мое предложение основано на ответе https://stackoverflow.com/a/19877437/1083704
где пользователю нужно объявить глобальную переменную
ranks
, каждый раз, когда вам нужно вызыватьreport
. Мое улучшение устраняет необходимость инициализировать переменные функции от пользователя.источник
inner
, но не можете назначать ему, но вы можете изменять его ключи и значения. Это позволяет избежать использования глобальных переменных.