답변:
내부 함수는 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.y
outer
outer.y
. 이름 을 사용하는 대신 inner.y
( 우리가 원하는 범위 인 inner
call 내부에 바인딩되어 있기 때문에 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
매개 변수 이름을으로 가져 와서 outer
varname으로 전달하는 몇 가지 속임수를 알아낼 수 있지만 이름에 의존하지 않고 outer
Y 결합자를 사용해야합니다.
nonlocal
. 정의 된 시간 locals()
에 outer()
s 지역 의 사전을 생성 inner()
하지만 해당 사전을 변경해도 in은 변경 되지 않습니다 . 닫힌 오버 변수를 공유하려는 더 많은 내부 함수가 있으면 더 이상 작동하지 않습니다. 발언권 과 그 증가 및 공유 카운터를 감소시킵니다. v
outer()
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
할 수 있지만 할당 할 수는 없지만 키와 값을 수정할 수 있습니다. 이것은 전역 변수의 사용을 피합니다.