답변:
내부 함수는 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
def inner(): print d; d = {'y': 1}시킵니다. 여기서는 print d외부 d를 읽고 d내부 범위에 비 지역 변수를 만듭니다 .
X = 1단순히 이름 X을 특정 객체 ( int값이있는 1)에 바인딩합니다 . X = 1; Y = X두 이름을 동일한 객체에 바인딩합니다. 어쨌든 일부 객체는 변경 가능 하며 값을 변경할 수 있습니다.
다음 솔루션은 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)
inc()에서 dec()반환 된 a 와 a를 말합니다 . 그런 다음 현재 카운터 값을 첨부 할 함수를 결정하고 다른 함수에서 해당 함수를 참조해야합니다. 다소 이상하고 비대칭으로 보입니다. 예를 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
각 outer () 호출은 context라는 새롭고 고유 한 클래스를 만듭니다 (단순히 새 인스턴스가 아님). 따라서 공유 컨텍스트에 대한 @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에 대한 나의 이해에서 이것은 비 로컬 키워드가 다루려는 의미입니다. 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
변수의 할당 문에서 함수의 이름을 사용하는 것은 중복되지만 변수를 사전에 넣는 것보다 더 간단하고 깔끔해 보입니다. 값은 Chris B.의 답변에서와 같이 한 호출에서 다른 호출로 기억됩니다.
f = outer()한 다음 나중에 수행 g = outer()하면 f의 카운터가 재설정됩니다. 이는 둘 다 자체 독립 변수가 아닌 단일 outer.y 변수를 공유하기 때문 입니다. 이 코드는 Chris B의 대답보다 심미적으로 더 좋아 보이지만 outer두 번 이상 호출하려는 경우 그의 방식이 어휘 범위를 에뮬레이트하는 유일한 방법 인 것 같습니다 .
outer.y은 함수 호출 (인스턴스)에 대한 로컬 항목을 포함하지 않지만 둘러싸는 범위 outer()의 이름 outer에 바인딩 된 함수 개체의 속성에 할당합니다 . 그러므로 사람은 동일하게 서면으로 사용할 수도 , 다른 대신의 이름 이 그 범위에 구속되는 것으로 알려져있다 제공. 이 올바른지? outer.youter
outer.y. 이름 을 사용하는 대신 inner.y( 우리가 원하는 범위 인 innercall 내부에 바인딩되어 있기 때문에 outer()) 내부 정의 inner.y = 0 이후 의 초기화 (속성이 생성 될 때 객체가 존재해야하기 때문에), 물론 이전에는 ? return inner
다음은 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
매개 변수 이름을으로 가져 와서 outervarname으로 전달하는 몇 가지 속임수를 알아낼 수 있지만 이름에 의존하지 않고 outerY 결합자를 사용해야합니다.
nonlocal. 정의 된 시간 locals()에 outer()s 지역 의 사전을 생성 inner()하지만 해당 사전을 변경해도 in은 변경 되지 않습니다 . 닫힌 오버 변수를 공유하려는 더 많은 내부 함수가 있으면 더 이상 작동하지 않습니다. 발언권 과 그 증가 및 공유 카운터를 감소시킵니다. vouter()inc()dec()
nonlocal은 파이썬 3 기능입니다.
nonlocal파이썬 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
위의 Martineau 우아한 솔루션을 실용적이고 다소 덜 우아한 사용 사례로 확장하면 다음과 같습니다.
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할 수 있지만 할당 할 수는 없지만 키와 값을 수정할 수 있습니다. 이것은 전역 변수의 사용을 피합니다.