source

처음 사용한 후 재할당 시 로컬 변수의 UnboundLocalError

gigabyte 2022. 9. 15. 22:52
반응형

처음 사용한 후 재할당 시 로컬 변수의 UnboundLocalError

다음 코드는 Python 2.5와 3.0 모두에서 정상적으로 동작합니다.

a, b, c = (1, 2, 3)

print(a, b, c)

def test():
    print(a)
    print(b)
    print(c)    # (A)
    #c+=1       # (B)
test()

단, 코멘트 해제 행(B)에서는,UnboundLocalError: 'c' not assigned(A)에 있습니다.의 가치a ★★★★★★★★★★★★★★★★★」b이치노이것은 두 가지 이유로 나를 완전히 당황하게 했다.

  1. 회선(B)의 후속 문장으로 인해 회선(A)에 런타임에러가 발생하는 이유는 무엇입니까?

  2. 「」가 되는 는 무엇입니까?a ★★★★★★★★★★★★★★★★★」b된 반면, ★★★★★★★★★★★★★★★★★★★★★★★★」c류가가발?발 생??

제가 생각할 수 있는 유일한 설명은 지역 변수가c됩니다.c+=1글로벌'보다 '글로벌' 합니다.c로컬 변수가 생성되기 전에도 마찬가지입니다.물론 변수가 존재하기 전에 범위를 "도용"하는 것은 의미가 없습니다.

누가 이 행동을 설명해 주시겠어요?

Python은 함수의 변수를 함수 내부에서 값을 할당할지 외부에서 값을 할당할지 여부에 따라 다르게 처리합니다.함수 내에 변수가 할당되어 있는 경우 기본적으로는 로컬 변수로 처리됩니다. 변수인 '코멘트 해제'를 .c값이 할당되기 전에 미리 설정합니다.

cc = 3앞에 "put" "put" "put"

global c

함수의 첫 번째 행으로 표시됩니다.

python 3에 대해서 말하자면, 지금은

nonlocal c

때 할 수 .c★★★★★★ 。

Python은 다양한 범위를 위해 모든 것을 사전에 보관한다는 점에서 조금 이상하다.원본 a, b, c는 가장 높은 범위에 있고, 따라서 가장 높은 사전에도 있다.함수에 자체 사전이 있습니다. 했을 때print(a) ★★★★★★★★★★★★★★★★★」print(b)Python은 사전에는 해당 이름을 가진 문장이 없기 때문에 목록을 검색하여 글로벌 사전에서 찾습니다.

, 이제 ㅇㅇㅇ, ㅇㅇ에 .c+=1은 물론, 이 이 말은, 이 말은, 이 말은, 이랬다, 이 말이죠c=c+1Python이 이 행을 스캔하면 "아하, c라는 변수가 있는데 로컬 스코프 사전에 넣을게요."라고 표시됩니다.그런 다음 할당 오른쪽에 있는 c에 대한 c 값을 검색하면 c라는 이름의 로컬 변수가 발견되고 아직 값이 없기 때문에 오류가 발생합니다.

문장은 '이이 the the the 。global c한 내용은 파서에게 ''를 사용한다는 입니다.c이치노

회선에 문제가 있다고 하는 이유는 코드를 생성하기 전에 효과적으로 이름을 찾고 있기 때문입니다.그래서 어떤 의미에서는 아직 이 회선이 실제로 동작하고 있다고는 생각하지 않습니다.조작성의 버그라고 생각합니다만, 컴파일러의 메세지를 너무 진지하게 받아들이지 않는 것을 배우는 것은 일반적으로 좋은 방법입니다.

위안이 된다면, 나는 아마도 Guido가 모든 것을 설명하는 사전들에 대해 쓴 것을 발견하기 전에 이 같은 문제를 파헤치고 실험하는 데 하루를 보냈을 것이다.

업데이트, 코멘트 참조:

코드를 두 번 스캔하지는 않지만, 렉싱과 파싱의 두 단계로 나누어 스캔합니다.

이 코드행의 해석은 어떻게 동작하는지를 검토합니다.어휘기는 원문을 읽고 문법의 가장 작은 구성요소인 어휘소로 나눕니다.그래서 그게 선을 긋는다면

c+=1

그것은 그것을 다음과 같은 것으로 분해한다.

SYMBOL(c) OPERATOR(+=) DIGIT(1)

파서는 최종적으로 이것을 해석 트리로 만들어 실행하려고 합니다만, 할당이 되기 전에 로컬 사전에서 c라는 이름을 검색하여 표시하지 않고 사전에 삽입하여 초기화되지 않은 것으로 표시합니다.완전히 컴파일된 언어에서는 심볼 테이블로 들어가 파스를 기다리지만 두 번째 패스의 사치를 누릴 수 없기 때문에 렉서는 나중에 생활을 편하게 하기 위해 약간의 추가 작업을 합니다.OPTERERATOR를 보고 "연산자 +=가 있는 경우 왼쪽이 초기화되었을 것입니다"라는 규칙과 "우와!"라고 말하는 것만 확인합니다.

여기서 중요한 것은 아직 라인의 해석을 시작하지 않았다는 것입니다.이 모든 것은 실제 해석에 대한 일종의 준비 작업이기 때문에 라인 카운터는 다음 행으로 진행되지 않습니다.그 때문에, 에러를 시그널링 했을 때에도, 이전 회선에 있는 것으로 간주됩니다.

말씀드렸듯이, 조작성의 버그라고 할 수 있습니다만, 실제로는 매우 일반적인 것입니다.일부 컴파일러는 이에 대해 더 솔직하게 "XXX 라인 또는 XX 라인 주변 오류"라고 말하지만, 이 컴파일러는 그렇지 않습니다.

분해 과정을 보면 무슨 일이 일어나고 있는지 명확하게 알 수 있습니다.

>>> 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

액세스하기 는 다음과 같습니다.LOAD_FAST및 b의 , "b"의 경우,LOAD_GLOBAL이는 컴파일러가 함수 내에서 가 할당되어 있는 것을 식별하고 로컬 변수로 분류했기 때문입니다.로컬 액세스 메커니즘은 글로벌에 따라 근본적으로 다릅니다.- 프레임 변수 테이블에는 오프셋이 정적으로 할당되어 있습니다.즉, 룩업은 글로벌과 같이 고가의 dict 룩업이 아닌 빠른 인덱스입니다.이것 때문에, Python은 다음을 읽고 있습니다.print a행은 "슬롯 0에 있는 로컬 변수 'a'의 값을 가져오고 인쇄합니다"로 표시되며, 이 변수가 아직 초기화되지 않은 것을 감지하면 예외가 발생합니다.

Python은 전통적인 글로벌 변수 시멘틱스를 시도할 때 상당히 흥미로운 동작을 합니다.내용은 '은 잘 수 하려면 '글로벌'을 .global키워드를 지정합니다..test()뭇매를 맞다

def test():
    global c
    print(a)
    print(b)
    print(c)    # (A)
    c+=1        # (B)

또, 이 에러가 발생하는 것은, 그 함수내에 「글로벌」과 같은 이름의 새로운 변수를 선언할 수도 있기 때문입니다.이 변수는 완전히 분리되어 있습니다.이 이 합니다.c조작으로 할 수 은 Python에서는되지 않습니다.이 조작은 Python에서는 허용되지 않습니다.파이썬이 새로운 기능은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은 Unbound Local Error 오류를 나타내고 있습니다.링크 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)

이것은 당신의 질문에 대한 직접적인 답변은 아니지만, 증강된 할당과 함수 범위 간의 관계에 의해 야기되는 또 다른 문제이기 때문에 밀접하게 관련되어 있습니다.

증강 과제 즉 증강 과제)를 경우가 많습니다.a += b ( )과 동등합니다.a = a + b). 단,할 수 단, 한쪽 구석에서 문제가 발생할 수 있습니다.설명하겠습니다.

Python의은 Python이 Python을 할 a: like is is로전((((((((((((:func(a)입니다. ; Python은 pass-by-reference입니다.a = a + b.a과된것것 것것것다다로컬 를 로컬 포인터로 합니다.a

,을 a += b을 사용하다

a = a + b

또는 다음과 같은 경우도 있습니다(메서드가 존재하는 경우).

a.__iadd__(b)

번째 경우(「」의 )a되지 않음)에되어 있기 에 로컬 이외의 .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 인터프리터는 함수를 완전한 단위로 읽습니다.저는 그것을 두 번의 패스로 읽는 것으로 생각합니다.한 번은 닫힘(로컬 변수)을 모으고, 또 한 번은 바이트 코드로 변환합니다.

이미 알고 계셨겠지만, '='의 왼쪽에 사용되는 이름은 암묵적으로 로컬 변수입니다.여러 번 +=에 대한 변수 액세스를 변경하다가 갑자기 다른 변수가 되었습니다.

또, 특히 글로벌한 범위와는 전혀 관계가 없는 것을 지적하고 싶습니다.중첩된 함수에서도 동일한 동작이 발생합니다.

c+=1cpython은 할당된 변수가 로컬이라고 가정하지만 이 경우 로컬로 선언되지 않았습니다.

하다, 쓰다, 쓰다, 쓰다, 쓰다, , 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다.global ★★★★★★★★★★★★★★★★★」nonlocal키워드를 지정합니다.

nonlocal글로벌하게 않은과 같이 할 수 있습니다.

my_variables = { # a mutable object
    'c': 3
}

def test():
    my_variables['c'] +=1

test()

클래스 변수에 도달하는 가장 좋은 방법은 클래스 이름으로 직접 액세스하는 것입니다.

class Employee:
    counter=0

    def __init__(self):
        Employee.counter+=1

메서드와 이름이 같은 변수를 정의하는 경우에도 이 메시지를 받을 수 있습니다.

예를 들어 다음과 같습니다.

def teams():
    ...

def some_other_method():
    teams = teams()

하다.teams() 것, 다른 것)get_teams().

로컬에서만 사용되기 때문에 Python 메시지는 다소 오해의 소지가 있습니다!

이 문제를 피하기 위해 다음과 같은 결과가 초래됩니다.

def teams():
    ...

def some_other_method():
    teams = get_teams()

는 또한 '이러다'가 '이러다'일부러 ''del키워드는 초기화 후 보통 루프 또는 조건부 블록에서 행의 아래쪽 변수에서 사용됩니다.

같은 문제가 나를 괴롭힌다.「」를 사용합니다.nonlocal ★★★★★★★★★★★★★★★★★」global그 문제를 풀 수 있다.
합니다.nonlocal, 중첩 기능에서 일한다.하지만 모듈 수준에서 그것은 작동하지 않는다.여기 예를 참조하십시오.

언급URL:https://stackoverflow.com/questions/370357/unboundlocalerror-on-local-variable-when-reassigned-after-first-use

반응형