Python 2.6에서 클로저를 구현하려고하는데 비 지역 변수에 액세스해야하지만이 키워드는 Python 2.x에서 사용할 수없는 것 같습니다. 이 파이썬 버전에서 클로저의 비 지역 변수에 어떻게 액세스해야합니까?
답변
내부 함수는 2.x에서 비 지역 변수를 읽을 수 있으며 리 바인드 할 수 없습니다 . 이것은 성가신 일이지만 해결할 수 있습니다. 사전을 만들고 그 안에 요소로 데이터를 저장하십시오. 내부 함수는 비 지역 변수가 참조하는 객체를 변경 하는 것을 금지하지 않습니다 .
Wikipedia의 예제를 사용하려면 :
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
답변
다음 솔루션은 Elias Zamaria 의 답변에서 영감을 얻었 지만 그 답변과는 반대로 외부 함수의 여러 호출을 올바르게 처리합니다. “변수” 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)
답변
사전보다는 로컬이 아닌 클래스 가 덜 복잡합니다 . @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
각 outer () 호출은 context라는 새롭고 고유 한 클래스를 만듭니다 (단순히 새 인스턴스가 아님). 따라서 공유 컨텍스트에 대한 @Nathaniel의주의를 피 합니다.
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
답변
여기서 핵심은 “액세스”라는 뜻입니다. 클로저 범위 밖의 변수를 읽는 데 문제가 없어야합니다. 예 :
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에 대한 나의 이해에서 이것은 비 로컬 키워드가 다루려는 의미입니다. PEP에서 언급했듯이 클래스를 사용하여 동일한 작업을 수행 할 수 있습니다 (지저분 함).
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
답변
어떤 이유로 든 여기에 대한 답변이 바람직하지 않은 경우 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
변수의 할당 문에서 함수의 이름을 사용하는 것은 중복되지만 변수를 사전에 넣는 것보다 더 간단하고 깔끔해 보입니다. 값은 Chris B.의 답변에서와 같이 한 호출에서 다른 호출로 기억됩니다.
답변
다음은 Alois Mahdal이 다른 답변 에 대한 의견 에서 만든 제안에서 영감을 얻은 것입니다 .
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에서 모두 작동합니다.
답변
파이썬의 범위 지정 규칙에는 사마귀가 있습니다. 할당은 변수를 즉시 둘러싸는 함수 범위에 로컬로 만듭니다. 전역 변수의 경우 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 결합자를 사용해야합니다.