Python 중첩 함수 변수 범위 지정
이 질문에 이미 답변이 있습니다.
주제에 대한 거의 모든 질문을 읽었지만 내 코드가 여전히 작동하지 않습니다.
파이썬 변수 범위에 대해 뭔가 빠진 것 같습니다.
내 코드는 다음과 같습니다.
PRICE_RANGES = {
64:(25, 0.35),
32:(13, 0.40),
16:(7, 0.45),
8:(4, 0.5)
}
def get_order_total(quantity):
global PRICE_RANGES
_total = 0
_i = PRICE_RANGES.iterkeys()
def recurse(_i):
try:
key = _i.next()
if quantity % key != quantity:
_total += PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
res = recurse(_i)
그리고 나는
"글로벌 이름 '_total'이 정의되지 않았습니다."
_total
과제 에 문제가 있다는 것을 알고 있지만 이유를 이해할 수 없습니다. recurse()
부모 함수의 변수에 대한 액세스 권한 이 없어야합니까 ?
누군가 파이썬 변수 범위에 대해 내가 놓친 것을 설명 할 수 있습니까?
코드를 실행하면이 오류가 발생합니다.
UnboundLocalError: local variable '_total' referenced before assignment
이 문제는 다음 줄로 인해 발생합니다.
_total += PRICE_RANGES[key][0]
범위 및 네임 스페이스에 대한 문서 는 다음 과 같이 말합니다.
파이썬의 특별한 특징은 – 만약 어떤
global
문장도 유효하지 않다면 – 이름에 대한 할당은 항상 가장 안쪽 범위에 들어간다는 것 입니다. 할당은 데이터를 복사하지 않고 개체에 이름을 바인딩 할뿐입니다.
따라서 라인이 효과적으로 다음과 같이 말하고 있기 때문에 :
_total = _total + PRICE_RANGES[key][0]
_total
의 네임 스페이스에 생성 됩니다 recurse()
. _total
그러면 새롭고 할당되지 않았 으므로 추가로 사용할 수 없습니다.
다음은 David의 대답의 본질을 보여주는 그림입니다.
def outer():
a = 0
b = 1
def inner():
print a
print b
#b = 4
inner()
outer()
명령문을 b = 4
주석 처리하면이 코드는 0 1
예상 한대로를 출력 합니다.
그러나 해당 줄의 주석 처리를 제거하면 줄 print b
에서 오류가 발생합니다.
UnboundLocalError: local variable 'b' referenced before assignment
앞선 선에서 b = 4
어쩐지 그 존재가 b
사라지는 것은 신비한 것 같다 . 그러나 데이비드가 인용 한 텍스트는 정적 분석 중에 인터프리터가 b가 in에 할당되어 inner
있으므로의 지역 변수라고 결정하는 이유를 설명합니다 inner
. 인쇄 줄 b
은 할당되기 전에 해당 내부 범위에서 인쇄를 시도합니다 .
Python 3에서는 nonlocal
문 을 사용하여 로컬이 아닌 글로벌 범위에 액세스 할 수 있습니다 .
특별한 객체 나 맵 또는 배열을 선언하는 대신 함수 속성을 사용할 수도 있습니다. 이것은 변수의 범위를 정말로 명확하게합니다.
def sumsquares(x,y):
def addsquare(n):
sumsquares.total += n*n
sumsquares.total = 0
addsquare(x)
addsquare(y)
return sumsquares.total
물론이 속성은 함수 호출이 아니라 함수 (정의)에 속합니다. 따라서 스레딩과 재귀를 염두에 두어야합니다.
이것은 redman 솔루션의 변형이지만, 배열 대신 적절한 네임 스페이스를 사용하여 변수를 캡슐화합니다.
def foo():
class local:
counter = 0
def bar():
print(local.counter)
local.counter += 1
bar()
bar()
bar()
foo()
foo()
이런 식으로 클래스 객체를 사용하는 것이 python 커뮤니티에서 추악한 해킹이나 적절한 코딩 기술로 간주되는지 확실하지 않지만 python 2.x 및 3.x에서 잘 작동합니다 (2.7.3 및 3.2.3에서 테스트 됨). ). 이 솔루션의 런타임 효율성에 대해서도 확신이 없습니다.
질문에 대한 답을 얻었을 것입니다. 그러나 나는 일반적으로 이것을 우회하는 방법을 나타내고 싶었고 그것은 목록을 사용하는 것입니다. 예를 들어 이렇게하려면 다음을 수행합니다.
X=0
While X<20:
Do something. ..
X+=1
대신 이렇게합니다.
X=[0]
While X<20:
Do something....
X[0]+=1
이런 식으로 X는 결코 지역 변수가 아닙니다.
@redman의 목록 기반 접근 방식을 사용했지만 가독성 측면에서 최적이 아닙니다.
다음은 수정 된 @Hans의 접근 방식입니다. 단, 외부가 아닌 내부 함수의 속성을 사용합니다. 이것은 재귀와 더 잘 호환되어야하며 아마도 멀티 스레딩 일 것입니다 :
def outer(recurse=2):
if 0 == recurse:
return
def inner():
inner.attribute += 1
inner.attribute = 0
inner()
inner()
outer(recurse-1)
inner()
print "inner.attribute =", inner.attribute
outer()
outer()
이것은 다음을 인쇄합니다.
inner.attribute = 3
inner.attribute = 3
inner.attribute = 3
inner.attribute = 3
내가한다면 다음 s/inner.attribute/outer.attribute/g
을 얻습니다.
outer.attribute = 3
outer.attribute = 4
outer.attribute = 3
outer.attribute = 4
따라서 실제로 내부 함수의 속성으로 만드는 것이 더 좋습니다.
또한 가독성 측면에서 의미가있는 것 같습니다. 왜냐하면 변수는 개념적으로 내부 함수와 관련이 있기 때문이며이 표기법은 변수가 내부 및 외부 함수의 범위간에 공유된다는 것을 독자에게 상기시킵니다. 가독성에 대한 약간의 단점 inner.attribute
은 def inner(): ...
.
철학적 관점에서 보면 한 가지 대답은 "네임 스페이스 문제가있는 경우 고유 한 네임 스페이스를 제공하십시오!"일 수 있습니다.
자체 클래스에 제공하면 문제를 캡슐화 할 수있을뿐만 아니라 테스트가 더 쉬워지고, 성가신 전역을 제거하고, 다양한 최상위 함수 사이에 변수를 삽질 할 필요가 줄어 듭니다 (확실히 get_order_total
).
근본적인 변화에 초점을 맞추기 위해 OP의 코드를 보존하고,
class Order(object):
PRICE_RANGES = {
64:(25, 0.35),
32:(13, 0.40),
16:(7, 0.45),
8:(4, 0.5)
}
def __init__(self):
self._total = None
def get_order_total(self, quantity):
self._total = 0
_i = self.PRICE_RANGES.iterkeys()
def recurse(_i):
try:
key = _i.next()
if quantity % key != quantity:
self._total += self.PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
res = recurse(_i)
#order = Order()
#order.get_order_total(100)
PS로서, 다른 답변의 목록 아이디어의 변형이지만 아마도 더 분명한 해킹 하나,
def outer():
order = {'total': 0}
def inner():
order['total'] += 42
inner()
return order['total']
print outer()
>>> def get_order_total(quantity):
global PRICE_RANGES
total = 0
_i = PRICE_RANGES.iterkeys()
def recurse(_i):
print locals()
print globals()
try:
key = _i.next()
if quantity % key != quantity:
total += PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
print 'main function', locals(), globals()
res = recurse(_i)
>>> get_order_total(20)
main function {'total': 0, 'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
get_order_total(20)
File "<pyshell#31>", line 18, in get_order_total
res = recurse(_i)
File "<pyshell#31>", line 13, in recurse
return recurse(_i)
File "<pyshell#31>", line 13, in recurse
return recurse(_i)
File "<pyshell#31>", line 12, in recurse
total += PRICE_RANGES[key][0]
UnboundLocalError: local variable 'total' referenced before assignment
>>>
보시다시피 total은 주 함수의 로컬 범위에 있지만 재귀의 로컬 범위에는 없지만 (분명히) 전역 범위에는 없습니다. 왜냐하면 get_order_total의 로컬 범위에만 정의되어 있기 때문입니다.
내 길은 ...
def outer():
class Cont(object):
var1 = None
@classmethod
def inner(cls, arg):
cls.var1 = arg
Cont.var1 = "Before"
print Cont.var1
Cont.inner("After")
print Cont.var1
outer()
참고 URL : https://stackoverflow.com/questions/5218895/python-nested-functions-variable-scoping
'programing tip' 카테고리의 다른 글
UIView 흔들림 애니메이션 (0) | 2020.10.13 |
---|---|
마우스 오버 또는 호버 vue.js (0) | 2020.10.13 |
입력시 EditText의 텍스트 지우기 (0) | 2020.10.13 |
오늘 날짜가 날짜 범위에 속하는지 확인하는 방법은 무엇입니까? (0) | 2020.10.13 |
uitextview에 html 텍스트 표시 (0) | 2020.10.13 |