처음 사용한 후에도 할당된(재) 변수(글로벌이어야 함)를 사용하려고 하는 UnboundLocalError
이 코드를 시도할 때:
a, b, c = (1, 2, 3)
def test():
print(a)
print(b)
print(c)
c += 1
test()
다음에서 오류가 발생했습니다.print(c)
다음과 같은 줄:
UnboundLocalError: local variable 'c' referenced before assignment
최신 버전의 Python 또는
UnboundLocalError: 'c' not assigned
일부 구 버전에서는
.c += 1
,둘다요.print
s는 성공적입니다.
안 가요: 왜 거죠?a
그리고.b
일을 하다, 일을 하다, 일을 하다c
그렇지 않나요?어떻게 했습니까?c += 1
이 되다print(c)
그것이 코드의 후반부에 올 때에도 실패하는 것?
숙제가 있는 것 같습니다.c += 1
로컬 변수 생성c
보다 우선합니다.c
하지만 변수가 존재하기 전에 어떻게 범위를 "도둑질"할 수 있습니까? 왜?c
여기서 분명히 로컬인가요?
참고 항목함수 내에서 전역 변수를 재할당하는 방법과 관련된 질문에 대해 함수에서 전역 변수 사용 및 전역이 아닌 외부(포함) 범위에 있는 python에서 변수를 수정할 수 있습니까?인클로저 함수(폐쇄)에서 재할당할 수 있습니다.
글로벌 변수에 액세스하는 데 '글로벌' 키워드가 필요하지 않은 이유를 참조하십시오.OP가 오류를 예상했지만 오류를 얻지 못한 경우, 오류 없이 단순히 글로벌에 액세스하는 것으로부터.global
키워드
자세한 내용은 Python에서 이름을 "unbound"로 지정하는 방법을 참조하십시오. 어떤 코드가 'Unbound Local Error'를 발생시킬 수 있습니까?OP가 변수가 로컬일 것으로 예상했지만 모든 경우에 할당을 방해하는 논리적 오류가 있는 경우.
Python은 함수의 변수를 함수 내부에서 값을 할당하는지 외부에서 값을 할당하는지에 따라 다르게 처리합니다.함수 내에 변수가 할당된 경우 기본적으로 로컬 변수로 처리됩니다.할 때 로컬 인 따서선주을달석때로변참합조고니려다를 .c
값이 할당되기 전에.
이 변수를 ,c
세계를 언급하기 위해c = 3
, put , put
global c
함수의 첫 번째 줄로 표시됩니다.
파이썬 3에 관해서는, 현재 있습니다.
nonlocal c
가장 가까운 인클로저 함수 범위를 참조하는 데 사용할 수 있습니다.c
변수.
파이썬은 다양한 범위를 위한 사전에 모든 것을 보관한다는 점에서 약간 이상합니다.원본 a,b,c는 최상단의 범위에 있고 최상단의 사전에도 있습니다.함수에는 자체 사전이 있습니다.다음 단계에 도달할 때까지print(a)
그리고.print(b)
사전에 그런 이름의 문장이 없기 때문에 Python은 목록을 찾아 글로벌 사전에서 찾습니다.
이제 우리는.c+=1
물론, 그은것, ▁to에 합니다.c=c+1
파이썬이 그 라인을 스캔하면 "아하, c라는 변수가 있어, 내 로컬 스코프 사전에 넣을게."라고 나옵니다.그런 다음 할당의 오른쪽에 있는 c에 대한 c 값을 찾을 때 c라는 로컬 변수를 찾으며, 이 변수는 아직 값이 없기 때문에 오류를 던집니다.
global c
위에서 언급된 것은 단순히 파서에게 그것이 사용한다고 말합니다.c
글로벌 범위에서 볼 수 있기 때문에 새로운 것이 필요하지 않습니다.
그것이 행에 문제가 있다고 말하는 이유는 코드를 생성하려고 하기 전에 효과적으로 이름을 찾고 있기 때문입니다. 그래서 어떤 의미에서 그것이 아직 행을 하고 있다고 생각하지 않습니다.저는 그것이 유용성 버그라고 주장하고 싶지만, 컴파일러의 메시지를 너무 심각하게 받아들이지 않는 것을 배우는 것이 일반적으로 좋은 관행입니다.
조금이라도 위안이 된다면, 저는 귀도가 모든 것을 설명하는 사전에 대해 쓴 것을 발견하기 전에 아마 하루 동안 이 같은 문제를 파헤치고 실험하면서 보냈을 것입니다.
업데이트, 주석 참조:
코드를 두 번 스캔하는 것이 아니라 렉싱과 구문 분석의 두 단계로 코드를 스캔합니다.
이 코드 줄의 구문 분석이 어떻게 작동하는지 고려합니다.어휘 사용자는 원문을 읽고 문법의 "가장 작은 구성 요소"인 어휘소로 나눕니다.그래서 그것이 선에 닿았을 때
c+=1
그것은 그것을 다음과 같은 것으로 나눕니다.
SYMBOL(c) OPERATOR(+=) DIGIT(1)
파서는 결국 이것을 parse tree로 만들고 실행하려고 하지만, 그것이 할당이기 때문에, 그것이 실행되기 전에, 그것은 로컬 사전에서 c 이름을 찾고, 그것을 보지 않고, 사전에 삽입하여, 초기화되지 않은 것으로 표시합니다.완전히 컴파일된 언어로, 그것은 단지 기호 테이블에 들어가서 구문 분석을 기다리지만, 그것은 두 번째 패스의 사치를 갖지 않을 것이기 때문에, 렉서는 나중에 삶을 더 쉽게 만들기 위해 약간의 추가 작업을 합니다.그런 다음 OPECTER를 보고 규칙에 "Operator +=가 있는 경우 왼쪽이 초기화되어 있어야 합니다"라고 말하고 "우웅!"라고 말합니다.
여기서 요점은 아직 선의 구문 분석을 시작하지 않았다는 것입니다.이 모든 것이 실제 구문 분석에 대한 준비 단계에서 발생하기 때문에 라인 카운터가 다음 줄로 진행되지 않았습니다.따라서 오류 신호를 보내도 이전 라인에 있다고 생각합니다.
제가 말씀드렸듯이, 여러분은 이것이 유용성 버그라고 주장할 수 있지만, 사실 그것은 꽤 흔한 일입니다.일부 컴파일러는 이에 대해 더 솔직하고 "XXX 줄 또는 줄 주변의 오류"라고 말하지만, 이 컴파일러는 그렇지 않습니다.
분해 과정을 살펴보면 다음과 같은 현상이 명확하게 나타날 수 있습니다.
>>> def f():
... print a
... print b
... a = 1
>>> import dis
>>> dis.dis(f)
2 0 LOAD_FAST 0 (a)
3 PRINT_ITEM
4 PRINT_NEWLINE
3 5 LOAD_GLOBAL 0 (b)
8 PRINT_ITEM
9 PRINT_NEWLINE
4 10 LOAD_CONST 1 (1)
13 STORE_FAST 0 (a)
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
바와 , 는 " 시피보다는", " 에기위드바코트이한하접속▁a는"입니다.LOAD_FAST
그리고 b의 경우,LOAD_GLOBAL
컴파일러가 함수 내에서 a가 할당된 것을 식별하고 로컬 변수로 분류했기 때문입니다.로컬에 대한 액세스 메커니즘은 글로벌에 대해 근본적으로 다릅니다. 프레임의 변수 테이블에 정적으로 오프셋이 할당됩니다. 즉, 글로벌에 대한 것처럼 더 비싼 딕트 룩업이 아니라 룩업이 빠른 인덱스임을 의미합니다.이 때문에 Python은 다음을 읽고 있습니다.print a
은 "0에 변수 '로 지정하고 이 초기화되지 않은 시킵니다. line " " " 롯 0 저 " 은 'a ' 의 " 값 " 가 " 에 " 을 " 와 " 져 " 로 " 슬 " 변 " 수 " 인 " 화 " 변 " 아 " 가 " 수 " 되 " 직 " 초 " 기 " 이 " 지 " 않 " 것 " 은 " 을 " 감 " 생 " 시 "
Python은 전통적인 글로벌 변수 의미론을 시도할 때 다소 흥미로운 동작을 합니다.내용은 '수자한내글기않 '로벌만'를 .global
변경해 보세요.test()
대상:
def test():
global c
print(a)
print(b)
print(c) # (A)
c+=1 # (B)
또한 이 오류가 발생하는 이유는 함수 내부에 '글로벌' 변수와 동일한 이름을 가진 새 변수를 선언할 수 있기 때문입니다.이 이 에서 인프리당신이범새변만생한다라고 불리는 새로운 합니다.c
그리고 이 새로운 것 때문에 파이썬에서는 허용되지 않는 한 가지 작업으로 모든 것을 수정합니다.c
초기화되지 않았습니다.
이를 명확히 하는 가장 좋은 예는 다음과 같습니다.
bar = 42
def foo():
print bar
if False:
bar = 0
을 부를 때.foo()
이것 또한 증가합니다. UnboundLocalError
로 선에 하지 못할 입니다.bar=0
따라서 논리적으로 로컬 변수가 생성되어서는 안 됩니다.
수수께끼는 "파이썬은 해석된 언어"와 함수의 선언에 있습니다.foo
단일 문(즉, 복합 문)으로 해석되며, 이를 벙어리처럼 해석하여 로컬 및 글로벌 범위를 만듭니다.그렇게bar
실행 전 로컬 범위에서 인식됩니다.
이와 같은 예를 더 보려면 이 게시물을 읽으십시오. http://blog.amir.rachum.com/blog/2013/07/09/python-common-newbie-mistakes-part-2/
이 게시물은 변수의 Python 범위 지정에 대한 전체 설명 및 분석을 제공합니다.
여기에 도움이 될 수 있는 두 개의 링크가 있습니다.
1: docs.python.org/3.1/faq/programming.html?highlight=nonlocal#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
2: docs.python.org/3.1/faq/programming.html?highlight=nonlocal#how-do-i-write-a-function-with-output-parameters-call-by-reference
링크 1은 UnboundLocalError 오류를 설명합니다.링크 2는 테스트 기능을 다시 쓰는 데 도움이 됩니다.링크 2를 기준으로 원래 문제를 다음과 같이 다시 작성할 수 있습니다.
>>> a, b, c = (1, 2, 3)
>>> print (a, b, c)
(1, 2, 3)
>>> def test (a, b, c):
... print (a)
... print (b)
... print (c)
... c += 1
... return a, b, c
...
>>> a, b, c = test (a, b, c)
1
2
3
>>> print (a, b ,c)
(1, 2, 4)
Python 인터프리터는 함수를 완전한 단위로 읽습니다.저는 그것을 두 가지 경로로 읽는 것으로 생각합니다. 한 번은 닫힌 부분(로컬 변수)을 수집하고 다시 한 번은 바이트 코드로 변환하기 위해 읽습니다.
이미 알고 계셨겠지만 '=' 왼쪽에 사용되는 이름은 암묵적으로 로컬 변수입니다.+=에 대한 변수 액세스를 변경하여 여러 번 들켰는데 갑자기 다른 변수가 되었습니다.
저는 또한 이것이 구체적으로 글로벌 범위와 관련이 없다는 것을 지적하고 싶었습니다.중첩 함수에서도 동일한 동작을 얻을 수 있습니다.
c+=1
할당합니다.c
python은 할당된 변수가 로컬이라고 가정하지만 이 경우에는 로컬로 선언되지 않았습니다.
다음 중 하나를 사용합니다.global
또는nonlocal
키워드들
nonlocal
python 3에서만 작동하므로 python 2를 사용하고 있고 변수를 전역으로 만들지 않으려면 다음과 같이 변형 가능한 개체를 사용할 수 있습니다.
my_variables = { # a mutable object
'c': 3
}
def test():
my_variables['c'] +=1
test()
이것은 당신의 질문에 대한 직접적인 대답은 아니지만, 증강된 할당과 기능 범위 사이의 관계로 인해 발생하는 또 다른 혼란이기 때문에 밀접한 관련이 있습니다.
은 증강 과제를 .a += b
) 한 할당 )과 ( )a = a + b
한 로 인해 의 문제가 할 수 있습니다. 하지만 한 구석의 경우 이 문제로 약간의 문제가 발생할 수 있습니다.설명하겠습니다.
파이썬의 간단한 할당이 작동하는 방식은 다음과 같은 경우를 의미합니다.a
예:func(a)
됨), 그 다음 Python은 참조로 전달됩니다.a = a + b
에서는 수하지않다니를 하지 않습니다.a
그것은 전해집니다.대신 로컬 포인터를 다음으로 수정합니다.a
.
하지만 만약 당신이a += b
다음과 같이 구현되기도 합니다.
a = a + b
또는 때때로(메소드가 있는 경우) 다음과 같이 지정합니다.
a.__iadd__(b)
번째 경우 ( 첫번한경우더번(째만한번더)▁as▁long(우경▁ina
글로벌로 ), 에 할당된 범위 밖에서는 .a
포인터 업데이트일 뿐입니다.
두번째경는우에,는에,a
실제로 수정될 것이므로 모든 참조는a
수정된 버전을 가리킵니다.이는 다음 코드로 설명됩니다.
def copy_on_write(a):
a = a + a
def inplace_add(a):
a += a
a = [1]
copy_on_write(a)
print a # [1]
inplace_add(a)
print a # [1, 1]
b = 1
copy_on_write(b)
print b # [1]
inplace_add(b)
print b # 1
따라서 비결은 함수 인수에 대한 증강된 할당을 피하는 것입니다(로컬/루프 변수에만 사용하려고 합니다).간단한 과제를 사용하면 모호한 행동으로부터 안전할 수 있습니다.
요약
Python은 변수의 범위를 미리 결정합니다.또는 (3.x) 키워드를 사용하여 명시적으로 재정의하지 않는 한 변수는 이름 바인딩을 변경하는 모든 작업의 존재에 따라 로컬로 인식됩니다.여기에는 일반적인 과제, 다음과 같은 증강된 과제가 포함됩니다.+=
하고 덜 (.for
및 생성, 중함수및클,import
문장...) 및 바인딩 해제(사용)del
합니다.) 이러한 코드의 실제 실행은 관련이 없습니다.
이에 대해서는 설명서에도 설명되어 있습니다.
논의
일반적인 믿음과는 달리, 파이썬은 어떤 의미에서 "해석된" 언어가 아닙니다.(그것들은 이제 거의 사라졌습니다.)Python의 레퍼런스 구현은 Java 또는 C#과 거의 동일한 방식으로 Python 코드를 컴파일합니다. 가상 머신의 opcode("바이트 코드")로 변환된 다음 에뮬레이트됩니다.다른 구현체들도 코드를 컴파일해야 합니다 - 그래서SyntaxError
s는 실제로 코드를 실행하지 않고 표준 라이브러리의 "컴파일 서비스" 부분을 구현하기 위해 탐지될 수 있습니다.
Python이 변수 범위를 결정하는 방법
컴파일 중(참조 구현 여부에 관계없이) Python은 함수의 변수 범위에 대한 결정을 위한 간단한 규칙을 따릅니다.
함수에 이름에 대한 또는 선언이 포함된 경우 해당 이름은 각각 이름을 포함하는 글로벌 범위 또는 첫 번째 포함 범위를 참조하는 것으로 간주됩니다.
그렇지 않으면 코드가 런타임에 실제로 바인딩을 변경하지 않더라도 이름의 바인딩(할당 또는 삭제)을 변경하는 구문이 포함된 경우 이름은 로컬입니다.
그렇지 않은 경우 이름이 포함된 첫 번째 동봉 범위를 가리키거나 그렇지 않은 경우 전역 범위를 가리킵니다.
중요한 것은 컴파일 시 범위가 해결된다는 것입니다.생성된 바이트 코드는 찾을 위치를 직접 나타냅니다.예를 들어 CPython 3.8에는 별도의 운영 코드가 있습니다.LOAD_CONST
잘 있음), (에),있▁(음▁at▁(컴),LOAD_FAST
(으), (계속)LOAD_DEREF
)로 표시됩니다.nonlocal
"셀" 객체의 튜플로 구현되는 폐쇄를 살펴봄으로써 조회합니다.LOAD_CLOSURE
함수에 .)LOAD_GLOBAL
(글로벌 네임스페이스 또는 기본 제공 네임스페이스에서 검색).
이러한 이름에는 "기본" 값이 없습니다.검색하기 전에 할당되지 않은 경우,NameError
일다나어. 특, 검을위, 지역해색.UnboundLocalError
은 생발의 입니다; 이은의하유니입다형위것다니.NameError
.
특별한(특별하지 않은) 경우
여기에는 구문 규칙이 정적 분석 없이 컴파일 시 구현된다는 점을 염두에 두고 몇 가지 중요한 고려 사항이 있습니다.
- 글로벌 변수가 명시적으로 생성된 글로벌이 아니라 내장 함수 등인지 여부는 중요하지 않습니다.
(물론, 이렇게 만들어진 이름을 그림자로 만드는 것은 나쁜 생각입니다.def x(): int = int('1') # `int` is local!
global
어쩔 수 없습니다 - 함수 외부에서 동일한 코드를 사용하는 것과 마찬가지로 여전히 문제가 발생합니다.) - 코드에 도달할 수 없는 경우에는 문제가 없습니다.
y = 1 def x(): return y # local! if False: y = 0
- 할당이 인플레이스 수정(예: 목록 확장)으로 최적화되는지 여부는 중요하지 않습니다. 개념적으로, 값은 여전히 할당되며, 이는 참조 구현에서 바이트 코드에 동일한 개체에 이름을 쓸모없는 재할당으로 반영됩니다.
y = [] def x(): y += [1] # local, even though it would modify `y` in-place with `global`
- 그러나 인덱스/슬라이스 할당을 대신 수행하는 것이 중요합니다.(이것은 컴파일 시에 다른 opcode로 변환되며, 이는 차례로 호출됩니다.
__setitem__
.)y = [0] def x(): print(y) # global now! No error occurs. y[0] = 1
- 다른 형태의 과제가 있습니다. 예가 있습니다.
for
및 프루및import
s:import sys y = 1 def x(): return y # local! for y in []: pass def z(): print(sys.path) # `sys` is local! import sys
- 문제를 일으키는 또 다른 일반적인 방법
import
이와 다음과 같이 사용합니다.
다시.import random def x(): random = random.choice(['heads', 'tails'])
import
변수 할이므변글있수습니다가가 있습니다.random
그러나 이 글로벌 변수는 특별하지 않습니다. 로컬에 의해 쉽게 그림자가 드리워질 수 있습니다.random
. - 삭제 시 이름 바인딩도 변경됩니다. 예:
y = 1 def x(): return y # local! del y
있는 독자는 하여 이러한 을 권장합니다.dis
표준 라이브러리 모듈.
와 스와코를 합니다.nonlocal
(3.x)
이 문제는 두 가지 모두에 대해 동일한 방식으로 작동합니다.global
그리고.nonlocal
키워드. (Python 2.x에는 .가 없습니다.) 어느 쪽이든, 키워드는 외부 범위에서 변수를 할당하는 데 필요하지만, 단순히 검색하거나 검색된 개체를 변형시키는 데 필요하지 않습니다. (다시:+=
목록에 있으면 목록이 변형되지만 이름도 동일한 목록에 재할당됩니다.)
글로벌 및 기본 제공 기능에 대한 특별 참고 사항
위에서 본 것처럼, 파이썬은 어떤 이름도 "범위 내에서 내장된" 이름으로 취급하지 않습니다.대신, 내장된 기능은 글로벌 범위 검색에서 사용되는 예비 기능입니다.이러한 변수에 할당하면 글로벌 범위만 업데이트되고 기본 제공 범위는 업데이트되지 않습니다.그러나 참조 구현에서는 기본 제공 범위를 수정할 수 있습니다. 이 범위는 글로벌 네임스페이스의 변수로 표시됩니다.__builtins__
모듈 를 보유하고 (C에서 모듈로 ).builtins
미리 할당되어 해당 글로벌 이름에 할당됩니다.흥미롭게도, 이 모듈 객체는 다른 많은 내장 객체들과 달리, 그것의 속성들을 수정할 수 있고 그리고del
것은 할 수 없는 세부사항으로 , 까지 꽤 해 왔습니다
클래스 변수에 액세스하는 가장 좋은 방법은 클래스 이름으로 직접 액세스하는 것입니다.
class Employee:
counter=0
def __init__(self):
Employee.counter+=1
이 문제는 다음과 같은 경우에도 발생할 수 있습니다.del
키워드는 초기화 후 일반적으로 루프 또는 조건부 블록에서 사용됩니다.
n = num
아래,n
이고 는로 변이고수입니다.num
변수입니다.
num = 10
def test():
# ↓ Local variable
n = num
# ↑ Global variable
print(n)
test()
따라서 오류가 없습니다.
10
이 에는 하만이경는에우지▁of는▁but.num = num
아래,num
와 양에는지변이있고들수역쪽,num
오른쪽은 아직 정의되지 않았습니다.
num = 10
def test():
# ↓ Local variable
num = num
# ↑ Local variable not defined yet
print(num)
test()
따라서 다음과 같은 오류가 있습니다.
UnboundLocalError: 로컬 변수 'num'이(가) 할당 전에 참조되었습니다.
또한을 하더라도, 더하라도는num = 10
아래와 같이:
# num = 10 # Removed
def test():
# ↓ Local variable
num = num
# ↑ Local variable not defined yet
print(num)
test()
다음과 같은 오류가 있습니다.
UnboundLocalError: 로컬 변수 'num'이(가) 할당 전에 참조되었습니다.
위의 위의오를해위해기하결류,위해,global num
앞에num = num
아래와 같이:
num = 10
def test():
global num # Here
num = num
print(num)
test()
그러면 다음과 같이 위의 오류가 해결됩니다.
10
로컬 변수 또로변를정의다를 합니다.num = 5
앞에num = num
아래와 같이:
num = 10
def test():
num = 5 # Here
num = num
print(num)
test()
그러면 다음과 같이 위의 오류가 해결됩니다.
5
메서드와 이름이 같은 변수를 정의하는 경우에도 이 메시지가 표시될 수 있습니다.
예:
def teams():
...
def some_other_method():
teams = teams()
을 바꾸는 입니다.teams()
다른 것과 마찬가지로get_teams()
.
로컬에서만 사용하기 때문에, 파이썬 메시지는 오히려 오해의 소지가 있습니다!
결국 이런 상황을 모면할 수 있습니다.
def get_teams():
...
def some_other_method():
teams = get_teams()
언급URL : https://stackoverflow.com/questions/370357/unboundlocalerror-trying-to-use-a-variable-supposed-to-be-global-that-is-rea
'programing' 카테고리의 다른 글
현재 표시된 조각을 가져오려면 어떻게 해야 합니까? (0) | 2023.06.04 |
---|---|
RichTextBox(WPF)에 "Text" 문자열 속성이 없습니다. (0) | 2023.06.04 |
사용자가 목록 항목을 마우스로 가리킬 때 커서를 손으로 바꾸는 방법은 무엇입니까? (0) | 2023.06.04 |
Angular 6에서 ng serve를 통해 환경을 설정하는 방법 (0) | 2023.06.04 |
Xcode 파생 데이터 폴더의 내용을 안전하게 삭제할 수 있습니까? (0) | 2023.06.04 |