Ответы:
Внутренние функции могут читать нелокальные переменные в 2.x, но не повторно связывать их. Это раздражает, но это можно обойти. Просто создайте словарь и сохраните в нем свои данные как элементы. Внутренние функции не запрещаются мутируют объекты, нелокальные переменные относятся к.
Чтобы использовать пример из Википедии:
def outer():
d = {'y' : 0}
def inner():
d['y'] += 1
return d['y']
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
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
функции), но кажется эффективным.
def outer():
def inner():
inner.y += 1
return inner.y
inner.y = 0
return inner
f = outer()
g = outer()
print(f(), f(), g(), f(), g()) #prints (1, 2, 1, 3, 2)
inc()
и a, dec()
возвращенные из внешнего, увеличивают и уменьшают общий счетчик. Затем вам нужно решить, к какой функции присоединить текущее значение счетчика, и ссылаться на эту функцию из другой (-ых). Что выглядит несколько странно и асимметрично. Например, в dec()
строке вроде inc.value -= 1
.
Вместо словаря в нелокальном классе меньше беспорядка . Изменение @ ChrisB в примере :
def outer():
class context:
y = 0
def inner():
context.y += 1
return context.y
return inner
затем
f = outer()
assert f() == 1
assert f() == 2
assert f() == 3
assert f() == 4
Каждый вызов external () создает новый и отличный класс, называемый контекстом (а не просто новый экземпляр). Таким образом, @Nathaniel избегает опасений по поводу общего контекста.
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
__slots__ = ()
и создавая объект вместо использования класса, например context.z = 3
, поднимет AttributeError
. Это возможно для всех классов, если они не наследуются от класса, не определяющего слоты.
Я думаю, что главное здесь то, что вы подразумеваете под «доступом». Не должно быть проблем с чтением переменной за пределами области закрытия, например,
x = 3
def outer():
def inner():
print x
inner()
outer()
должен работать должным образом (печать 3). Однако переопределение значения x не работает, например,
x = 3
def outer():
def inner():
x = 5
inner()
outer()
print x
по-прежнему будет печатать 3. Насколько я понимаю, PEP-3104 это то, что должно охватывать ключевое слово nonlocal. Как упоминалось в PEP, вы можете использовать класс для выполнения того же самого (что-то вроде беспорядка):
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
def ns(): pass
последующим ns.x = 3
. Это некрасиво, но на мой взгляд немного менее уродливо.
class Namespace: x = 3
?
ns
- это глобальный объект, поэтому вы можете ссылаться ns.x
на уровень модуля в print
инструкции в самом конце ,
Есть еще один способ реализовать нелокальные переменные в Python 2, если какой-либо из ответов здесь по какой-либо причине нежелателен:
def outer():
outer.y = 0
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
Излишне использовать имя функции в операторе присваивания переменной, но мне это кажется проще и чище, чем помещать переменную в словарь. Значение запоминается от одного вызова к другому, как и в ответе Криса Б.
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
?
Вот что-то, вдохновленное предложением, которое Алоис Махдал сделал в комментарии относительно другого ответа :
class Nonlocal(object):
""" Helper to implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def outer():
nl = Nonlocal(y=0)
def inner():
nl.y += 1
return nl.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
Обновить
Оглядываясь на это недавно, я был поражен тем, насколько он похож на декоратор - когда меня осенило, что реализация его как одного из вариантов сделает его более универсальным и полезным (хотя, возможно, это в некоторой степени ухудшает его читабельность).
# Implemented as a decorator.
class Nonlocal(object):
""" Decorator class to help implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self._vars = kwargs
def __call__(self, func):
for k, v in self._vars.items():
setattr(func, k, v)
return func
@Nonlocal(y=0)
def outer():
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
Обратите внимание, что обе версии работают как в Python 2, так и в 3.
В правилах определения области видимости Python есть бородавка - назначение делает переменную локальной по отношению к непосредственно охватывающей ее области функции. Для глобальной переменной это можно решить с помощью global
ключевого слова.
Решение состоит в том, чтобы ввести объект, который является общим для двух областей действия, который содержит изменяемые переменные, но сам ссылается на переменную, которая не назначена.
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
Альтернативой является взлом некоторых областей:
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
Возможно, вам удастся придумать некоторые уловки, чтобы получить имя параметра outer
, а затем передать его как varname, но не полагаясь на имя, которое outer
вам нужно, чтобы использовать комбинатор Y.
nonlocal
. locals()
создает словарь outer()
s locals в то время, когда inner()
определено, но изменение этого словаря не меняет v
in outer()
. Это больше не будет работать, когда у вас будет больше внутренних функций, которые хотят поделиться закрытой переменной. Замолвите inc()
и dec()
что увеличение и уменьшение общего счетчика.
nonlocal
- это функция Python 3.
nonlocal
в Python 2 в целом . Ваши идеи не охватывают общий случай, а только те, у которых есть одна внутренняя функция. Взглянем на эту суть для примера. Обе внутренние функции имеют свой собственный контейнер. Вам нужен изменяемый объект в области внешней функции, как уже предлагали другие ответы.
nonlocal
ключевого слова, введенного в Python 3.
Другой способ сделать это (хотя и слишком многословно):
import ctypes
def outer():
y = 0
def inner():
ctypes.pythonapi.PyCell_Set(id(inner.func_closure[0]), id(y + 1))
return y
return inner
x = outer()
x()
>> 1
x()
>> 2
y = outer()
y()
>> 1
x()
>> 3
Расширяя элегантное решение Мартино выше до практичного и несколько менее элегантного варианта использования, я получаю:
class nonlocals(object):
""" Helper to implement nonlocal names in Python 2.x.
Usage example:
def outer():
nl = nonlocals( n=0, m=1 )
def inner():
nl.n += 1
inner() # will increment nl.n
or...
sums = nonlocals( { k:v for k,v in locals().iteritems() if k.startswith('tot_') } )
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, a_dict):
self.__dict__.update(a_dict)
Используйте глобальную переменную
def outer():
global y # import1
y = 0
def inner():
global y # import2 - requires import1
y += 1
return y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
Лично мне не нравятся глобальные переменные. Но мое предложение основано на ответе https://stackoverflow.com/a/19877437/1083704
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
где пользователю нужно объявить глобальную переменную ranks
, каждый раз, когда вам нужно вызывать report
. Мое улучшение устраняет необходимость инициализировать переменные функции от пользователя.
inner
, но не можете назначать ему, но вы можете изменять его ключи и значения. Это позволяет избежать использования глобальных переменных.