PEP 840 – 클래스 네임스페이스의 이름 해석
- Author:
- Jeremy Hylton <jeremy at python.org>, Guido van Rossum <guido at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Draft
- Type:
- Standards Track
- Created:
- 15-Jul-2026
- Python-Version:
- 3.16
- Post-History:
- 16-Jul-2026
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
클래스에서 변수 이름이 해석되는 방식에는 오랜 기간 일관성이 없었습니다. 이러한 불일치를 해소하기 위한 몇 가지 대안을 논의합니다.
동기
클래스 네임스페이스에서 이름 해석은 부분적인 동적 조회를 사용합니다. 이름에 대한 할당이 클래스 본문 실행 중 발생했는지에 따라 이름은 로컬 이름이거나 전역 이름일 수 있습니다. 이름이 할당되기 전에 사용되면 전역 네임스페이스(및 내장 네임스페이스)에서 조회됩니다.
클래스가 함수 스코프 내에 정의되면 이름이 가장 가까운 바깥쪽 스코프로 해석될 수 있습니다. 이름이 바깥쪽 스코프에서 로컬이지만 바인딩되지 않은 경우 NameError가 발생합니다.
겉보기 일관성 문제는 같은 클래스 블록에서 이름이 로컬이면서 전역일 수 있지만, 로컬이면서 자유 변수일 수는 없다는 점입니다. 즉, 클래스가 최상위 수준에 정의되었는지 다른 블록 내부에 정의되었는지에 따라 이름 해석이 다르게 동작합니다. 전역 변수가 존재하면 자유 변수를 해석하는 데 사용할 수 있지만, 해당 변수가 클래스의 바깥쪽 스코프에 바인딩되어 있으면 사용할 수 없습니다.
다음은 클래스에서 자유 변수를 해석하는 예입니다.
>>> x = 0
>>> def f(x):
... class A:
... a = x
... return A
...
>>> A = f(1)
>>> A.a # the value of the parameter x, not the global x
1
변수 x는 함수 x의 매개변수로 해석됩니다. 전역 변수는 무시됩니다. 이 동작은 PEP 227및 PEP 3104에 정의된 표준 이름 해석입니다.
x가 로컬 변수로 사용되면 최상위 수준과 다른 블록 내부에서 동작이 달라집니다. 같은 코드에서 x를 로컬 변수로도 사용하면 x에 대한 초기 참조가 다르게 해석됩니다.
>>> x = 0
>>> def f(x):
... class A:
... a = x
... x = 42
... return A
...
>>> A = f(1)
>>> A.a # the value of the global x, not the parameter x
0
x에 대한 첫 번째 참조는 더 이상 f에 정의된 로컬 변수 x에 바인딩되지 않고, 대신 전역 변수를 사용합니다.
참조되는 네임스페이스 집합은 어휘적으로 결정되지만, 특정 참조가 전역 네임스페이스를 사용하여 해석될지는 정적으로 결정할 수 없습니다.
>>> x = 0
>>> def f(var):
... class A:
... x = 10
... if var in locals():
... del locals()[var]
... a = x
... return A
...
>>> A1 = f("x")
>>> A2 = f("y")
>>> A1.a, A2.a
(0, 10)
배경
Python 2.0 정의에서는 각 이름에 대해 로컬 네임스페이스, 전역 네임스페이스, 내장 네임스페이스라는 정확히 세 개의 네임스페이스를 검사하도록 지정합니다. Python 2.1부터 어휘적 스코핑이 사용되었으며, 자유 변수는 바깥쪽 스코프의 바인딩으로 해석될 수 있었습니다. 클래스 네임스페이스는 특수한 경우였으며, 자유 변수는 바깥쪽 스코프에서 해석되지 않았습니다. 클래스 네임스페이스는 로컬 네임스페이스와 전역 네임스페이스를 명시적으로 검사하는 특수한 LOAD_NAME / STORE_NAME 연산 코드를 사용했습니다.
Python 3.4에서는 클래스의 자유 변수가 바깥쪽 스코프에서 해석되었지만, 할당 전에 사용된 로컬 변수는 여전히 전역 스코프에서 해석되었습니다. 이때 언어 사양은 업데이트되지 않았으므로 여전히 3.4 이전의 동작을 지정합니다.
클래스 정의는 이름을 사용하고 정의할 수 있는 실행 가능한 문장입니다. 이러한 참조는 이름 해석의 일반 규칙을 따르지만, 바인딩되지 않은 로컬 변수는 전역 네임스페이스에서 조회된다는 예외가 있습니다.
클래스 네임스페이스와 최상위 모듈 네임스페이스는 모두 초기 지역 / 전역 / 내장 범위 지정 규칙을 반영하는 LOAD_NAME / STORE_NAME 바이트코드를 사용합니다. 클래스 네임스페이스는 클래스 객체의 속성이 되기 때문에 또한 특별합니다. 이러한 차이점을 고려하지 않았기 때문에 PEP 227이 작성될 때 클래스에 대한 서로 다른 범위 지정 규칙은 고려되지 않았습니다.
2002년에 버그가 보고되었습니다. PEP 작성자는 다음과 같은 의견과 함께 버그를 종료했습니다. “그 결함을 악용하는 코드를 작성하지 마십시오.” https://github.com/python/cpython/issues/36300 2010년에 또 다른 버그가 보고되었고, 이에 따라 같은 작성자가 변경을 제안했습니다. https://github.com/python/cpython/issues/53472 분명히 네임스페이스의 일관되지 않은 동작은 동일한 버그에 대해 일관되지 않은 대응을 초래할 수 있습니다.
제안
선택할 수 있는 대안은 여러 가지입니다. 몇 가지 단순한 대안은 잘못된 것처럼 보입니다. 3.4 이전의 동작으로 되돌릴 수도 있지만, 클래스 범위에서 자유 변수가 작동하지 않았다는 점은 놀라운 일이었습니다. 현재 동작을 유지할 수도 있지만, 일관되지 않아 보입니다.
한 가지 접근법은 클래스 네임스페이스가 함수 네임스페이스와 더 비슷하게 작동하도록 만드는 것입니다. 지역 변수가 바인딩되지 않은 시점에 사용되면 NameError가 발생합니다. 이 규칙은 단순하고 일관적입니다. 이 접근법의 가장 큰 단점은 코드가 작동하지 않게 된다는 점입니다. 이 기능에 의존하는 코드는 독자가 지역 참조를 의도했는지 전역 참조를 의도했는지 이해해야 하므로 파악하기 어렵습니다. 하지만 파악하기 어렵다는 것은 정의되지 않았거나 작동하지 않는 것과 같지는 않습니다.
또 다른 접근법은 이름이 바인딩되지 않았을 때 자유 변수 확인이 전역 네임스페이스를 사용하도록 동작을 변경하는 것입니다. 변수가 둘러싼 범위에서 바인딩되지 않아 현재 코드에서 NameError가 발생하는 경우, 해당 변수는 전역 네임스페이스에서 확인됩니다.
세 번째 접근법은 두 번째 접근법의 미묘한 변형입니다. 지역 변수가 바인딩되지 않았고 해당 변수가 둘러싼 범위에 바인딩되어 있다면 전역 네임스페이스 대신 해당 네임스페이스를 사용합니다.
>>> x = 0
>>> def f():
... x = 1
... class A:
... x = 2
... del x
... a = x # Should this use f's local or global?
... return A
...
>>> A = f()
>>> A.a
?
지역 변수가 바인딩되지 않았지만 어차피 다른 네임스페이스에서 확인할 수 있다면, 왜 전역 네임스페이스만 사용합니까? 표준 이름 확인 규칙 전체를 사용하지 않는 이유는 무엇입니까?
이 선택 사항이 가장 일관적인 것처럼 보이지만 구현을 더 복잡하게 만듭니다. 특정 지점에서 변수가 바인딩되어 있는지를 정적으로 판단할 수 없으므로, 둘러싼 범위의 지역 변수를 가리는 클래스의 모든 지역 변수에 대해 셀을 할당해야 합니다. 지역 변수의 동적 조작은 일반적이지 않으므로, 추가로 필요한 클로저는 거의 항상 사용되지 않을 것으로 예상됩니다. 그럼에도 불구하고 이는 다소 특이한 경우입니다–둘러싼 범위의 지역 변수를 가리는 지역 변수를 가진 중첩 클래스 정의입니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.