클래스 범위 및 목록, 세트 또는 사전 이해 및 생성기 표현식이 혼합되지 않습니다.
이유; 또는 이것에 대한 공식적인 단어
파이썬 3에서,리스트 변수에는 로컬 변수가 주변 범위로 흘러 들어가는 것을 막기 위해 적절한 범위 (로컬 네임 스페이스)가 주어졌습니다 ( 파이어리스트 이해는 범위를 이해 한 후에도 리 바인드 이름을 참조하십시오 . 맞습니까? ). 모듈이나 함수에서 이러한 목록 이해를 사용할 때 좋습니다. 그러나 클래스에서는 범위가 약간, 음, 이상 합니다.
이것은 pep 227에 설명되어 있습니다 :
클래스 범위의 이름은 액세스 할 수 없습니다. 가장 안쪽에있는 함수 범위에서 이름이 확인됩니다. 중첩 된 범위의 체인에서 클래스 정의가 발생하면 해결 프로세스는 클래스 정의를 건너 뜁니다.
그리고 class
복합 명세서 문서에서 :
그런 다음 클래스 스위트는 새로 작성된 로컬 네임 스페이스 및 원래 글로벌 네임 스페이스를 사용하여 새 실행 프레임 ( 네이밍 및 바인딩 섹션 참조)에서 실행됩니다 . (일반적으로 스위트에는 함수 정의 만 포함됩니다.) 클래스 스위트가 실행을 마치면 해당 실행 프레임은 삭제되지만 로컬 네임 스페이스는 저장 됩니다. [4] 그런 다음 기본 클래스의 상속 목록과 속성 사전의 저장된 로컬 네임 스페이스를 사용하여 클래스 객체를 만듭니다.
강조 광산; 실행 프레임은 임시 범위입니다.
범위는 클래스 객체의 특성으로 용도가 변경되므로이를 비 로컬 범위로 사용하면 정의되지 않은 동작이 발생합니다. 예를 들어 x
중첩 범위 변수 라고하는 클래스 메서드가 조작 Foo.x
되면 어떻게됩니까? 더 중요한 것은 하위 클래스의 의미는 Foo
무엇입니까? 파이썬 은 클래스 범위가 함수 범위와 매우 다르기 때문에 다르게 취급해야합니다.
마지막으로, 실행 모델 문서 의 링크 된 이름 지정 및 바인딩 섹션에서 클래스 범위를 명시 적으로 언급합니다.
클래스 블록에 정의 된 이름의 범위는 클래스 블록으로 제한됩니다. 코드는 메소드의 코드 블록으로 확장되지 않습니다. 여기에는 함수 범위를 사용하여 구현되므로 이해 및 생성기 표현식이 포함됩니다. 이는 다음이 실패 함을 의미합니다.
class A:
a = 42
b = list(a + i for i in range(10))
따라서 요약하면 함수에서 클래스 범위에 액세스 할 수없고 해당 범위에 포함 된 이해 또는 생성기 표현식을 나열 할 수 없습니다. 해당 범위가 존재하지 않는 것처럼 작동합니다. Python 2에서는 목록 이해가 바로 가기를 사용하여 구현되었지만 Python 3에서는 자체 기능 범위 (모두 있어야 했으므로)가 있으므로 예제가 중단됩니다. 다른 이해 유형은 Python 버전에 관계없이 자체 범위를 가지므로 set 또는 dict 이해를 가진 유사한 예제는 Python 2에서 중단됩니다.
# Same error, in Python 2 or 3
y = {x: x for i in range(1)}
(작은) 예외; 또는 한 부분 이 여전히 작동하는 이유
파이썬 버전에 관계없이 주변 범위에서 실행되는 이해 또는 생성기 표현의 한 부분이 있습니다. 그것은 가장 바깥 쪽의 반복 가능한 표현입니다. 귀하의 예에서 다음과 range(1)
같습니다.
y = [x for i in range(1)]
# ^^^^^^^^
따라서 x
해당 표현식에서 사용 하면 오류가 발생하지 않습니다.
# Runs fine
y = [i for i in range(x)]
이것은 가장 바깥 쪽 반복 가능에만 적용됩니다. 이해에 여러 for
절이 있는 경우 내부 for
절의 반복 가능 항목은 이해 범위에서 평가됩니다.
# NameError
y = [i for i in range(1) for j in range(x)]
이 디자인 결정은 생성기 표현식의 가장 바깥 쪽 반복 가능을 만들 때 오류가 발생하거나 가장 바깥 쪽 반복 가능이 반복 가능하지 않을 때 반복 시간 대신 genexp 생성 시간에 오류를 발생시키기 위해 이루어졌습니다. 이해도는 일관성을 위해이 동작을 공유합니다.
후드를보고; 또는, 당신이 원했던 것보다 더 자세하게
dis
모듈을 사용하여이 모든 것을 실제로 볼 수 있습니다 . 다음 예제에서는 Python 3.3을 사용합니다 . 검사 할 코드 객체를 깔끔하게 식별하는 정규화 된 이름 을 추가하기 때문 입니다. 생성 된 바이트 코드는 기능적으로 Python 3.2와 동일합니다.
클래스 를 만들기 위해 Python은 본질적으로 클래스 본문을 구성하는 전체 제품군 ( class <name>:
라인 보다 한 수준 깊게 들여 쓰기 된 모든 항목)을 가져와 마치 함수 인 것처럼 실행합니다.
>>> import dis
>>> def foo():
... class Foo:
... x = 5
... y = [x for i in range(1)]
... return Foo
...
>>> dis.dis(foo)
2 0 LOAD_BUILD_CLASS
1 LOAD_CONST 1 (<code object Foo at 0x10a436030, file "<stdin>", line 2>)
4 LOAD_CONST 2 ('Foo')
7 MAKE_FUNCTION 0
10 LOAD_CONST 2 ('Foo')
13 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
16 STORE_FAST 0 (Foo)
5 19 LOAD_FAST 0 (Foo)
22 RETURN_VALUE
첫 번째 LOAD_CONST
는 Foo
클래스 본문에 대한 코드 객체를로드 한 다음이를 함수로 만들어 호출합니다. 그런 다음 해당 호출 의 결과 는 클래스의 네임 스페이스 인 its를 작성하는 데 사용됩니다 __dict__
. 여태까지는 그런대로 잘됐다.
여기서 주목할 것은 바이트 코드는 중첩 된 코드 객체를 포함한다는 것입니다. 파이썬에서 클래스 정의, 함수, 이해 및 생성기는 모두 바이트 코드뿐만 아니라 지역 변수, 상수, 전역 변수, 중첩 범위 변수를 나타내는 구조를 포함하는 코드 객체로 표현됩니다. 컴파일 된 바이트 코드는 이러한 구조를 참조하며 파이썬 인터프리터는 제공된 바이트 코드가 주어진 구조에 액세스하는 방법을 알고 있습니다.
여기서 기억해야 할 중요한 것은 파이썬은 컴파일 타임에 이러한 구조를 생성한다는 것입니다. 이 class
스위트는 <code object Foo at 0x10a436030, file "<stdin>", line 2>
이미 컴파일 된 코드 객체 ( )입니다.
클래스 바디 자체를 생성하는 코드 객체를 검사하자. 코드 객체의 co_consts
구조는 다음과 같습니다.
>>> foo.__code__.co_consts
(None, <code object Foo at 0x10a436030, file "<stdin>", line 2>, 'Foo')
>>> dis.dis(foo.__code__.co_consts[1])
2 0 LOAD_FAST 0 (__locals__)
3 STORE_LOCALS
4 LOAD_NAME 0 (__name__)
7 STORE_NAME 1 (__module__)
10 LOAD_CONST 0 ('foo.<locals>.Foo')
13 STORE_NAME 2 (__qualname__)
3 16 LOAD_CONST 1 (5)
19 STORE_NAME 3 (x)
4 22 LOAD_CONST 2 (<code object <listcomp> at 0x10a385420, file "<stdin>", line 4>)
25 LOAD_CONST 3 ('foo.<locals>.Foo.<listcomp>')
28 MAKE_FUNCTION 0
31 LOAD_NAME 4 (range)
34 LOAD_CONST 4 (1)
37 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
40 GET_ITER
41 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
44 STORE_NAME 5 (y)
47 LOAD_CONST 5 (None)
50 RETURN_VALUE
위의 바이트 코드는 클래스 본문을 만듭니다. 함수가 실행되고 결과 locals()
네임 스페이스가 포함 x
되어 y
클래스를 작성하는 데 사용됩니다 ( x
글로벌로 정의 되지 않아 작동하지 않는 경우 제외 ). 저장 한 후주의 5
에 x
, 또 다른 코드 객체를로드; 이것이 목록 이해력입니다. 클래스 본문과 마찬가지로 함수 객체로 래핑됩니다. 생성 된 함수는 range(1)
반복 인수로 캐스트 되는 위치 코드, 반복 코드에 사용할 iterable을 사용합니다. 바이트 코드에 표시된 것처럼 range(1)
클래스 범위에서 평가됩니다.
이것으로부터 함수 또는 생성기의 코드 객체와 이해를위한 코드 객체의 유일한 차이점 은 상위 코드 객체가 실행될 때 후자가 즉시 실행된다는 것 입니다. 바이트 코드는 단순히 함수를 즉시 생성하고 몇 가지 작은 단계로 실행합니다.
파이썬 2.x는 대신 인라인 바이트 코드를 사용합니다. 파이썬 2.7의 출력 결과는 다음과 같습니다.
2 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
3 6 LOAD_CONST 0 (5)
9 STORE_NAME 2 (x)
4 12 BUILD_LIST 0
15 LOAD_NAME 3 (range)
18 LOAD_CONST 1 (1)
21 CALL_FUNCTION 1
24 GET_ITER
>> 25 FOR_ITER 12 (to 40)
28 STORE_NAME 4 (i)
31 LOAD_NAME 2 (x)
34 LIST_APPEND 2
37 JUMP_ABSOLUTE 25
>> 40 STORE_NAME 5 (y)
43 LOAD_LOCALS
44 RETURN_VALUE
코드 객체가로드되지 않고 FOR_ITER
루프가 인라인으로 실행됩니다. 따라서 Python 3.x에서는 목록 생성기에 자체 코드 객체가 제공되었으므로 자체 범위가 있습니다.
그러나 모듈이나 스크립트가 인터프리터에 의해 처음로드 될 때 이해는 나머지 파이썬 소스 코드와 함께 컴파일되었으며 컴파일러는 클래스 스위트를 유효한 범위로 간주 하지 않습니다 . 목록 이해에서 참조 된 변수 는 클래스 정의를 둘러싼 범위 를 재귀 적으로 찾아야합니다 . 컴파일러가 변수를 찾지 못하면 전역 변수로 표시합니다. 목록 이해 코드 객체의 디스 어셈블리 x
는 실제로 전역으로로드 되었음을 보여줍니다 .
>>> foo.__code__.co_consts[1].co_consts
('foo.<locals>.Foo', 5, <code object <listcomp> at 0x10a385420, file "<stdin>", line 4>, 'foo.<locals>.Foo.<listcomp>', 1, None)
>>> dis.dis(foo.__code__.co_consts[1].co_consts[2])
4 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (i)
12 LOAD_GLOBAL 0 (x)
15 LIST_APPEND 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
이 바이트 코드 덩어리는 전달 된 첫 번째 인수 ( range(1)
반복자)를 로드하며 Python 2.x 버전 FOR_ITER
이 루프 오버 및 출력을 만드는 데 사용 하는 것과 같습니다 .
우리가 정의했던 x
의 foo
기능을 대신, x
셀 변수 (세포가 중첩 된 범위 참조) 될 것이다 :
>>> def foo():
... x = 2
... class Foo:
... x = 5
... y = [x for i in range(1)]
... return Foo
...
>>> dis.dis(foo.__code__.co_consts[2].co_consts[2])
5 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (i)
12 LOAD_DEREF 0 (x)
15 LIST_APPEND 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
는 LOAD_DEREF
간접적으로로드 x
코드 객체 세포 개체에서 :
>>> foo.__code__.co_cellvars # foo function `x`
('x',)
>>> foo.__code__.co_consts[2].co_cellvars # Foo class, no cell variables
()
>>> foo.__code__.co_consts[2].co_consts[2].co_freevars # Refers to `x` in foo
('x',)
>>> foo().y
[2]
실제 참조는 함수 객체의 .__closure__
속성 에서 초기화 된 현재 프레임 데이터 구조에서 값을 찾습니다 . 이해 코드 객체를 위해 생성 된 함수는 다시 폐기되므로 해당 함수의 닫힘을 검사 할 수 없습니다. 클로저가 실제로 작동하는지 확인하려면 대신 중첩 함수를 검사해야합니다.
>>> def spam(x):
... def eggs():
... return x
... return eggs
...
>>> spam(1).__code__.co_freevars
('x',)
>>> spam(1)()
1
>>> spam(1).__closure__
>>> spam(1).__closure__[0].cell_contents
1
>>> spam(5).__closure__[0].cell_contents
5
요약하면 다음과 같습니다.
- 리스트 이해는 파이썬 3에서 자체 코드 객체를 얻습니다. 함수, 생성기 또는 이해를위한 코드 객체에는 차이가 없습니다. 이해 코드 객체는 임시 함수 객체로 래핑되어 즉시 호출됩니다.
- 코드 객체는 컴파일 타임에 생성되며 로컬이 아닌 변수는 중첩 된 코드 범위에 따라 전역 변수 또는 자유 변수로 표시됩니다. 클래스 본문은 이러한 변수를 찾는 범위로 간주 되지 않습니다 .
- 코드를 실행할 때 파이썬은 전역 또는 현재 실행중인 객체의 클로저 만 조사하면됩니다. 컴파일러는 클래스 본문을 범위로 포함하지 않았으므로 임시 함수 네임 스페이스는 고려되지 않습니다.
해결 방법; 또는, 어떻게해야합니까
x
함수에서와 같이 변수에 대한 명시 적 범위를 작성하려는 경우 목록 이해에 클래스 범위 변수를 사용할 수 있습니다.
>>> class Foo:
... x = 5
... def y(x):
... return [x for i in range(1)]
... y = y(x)
...
>>> Foo.y
[5]
'임시' y
기능은 직접 호출 할 수 있습니다. 반환 값으로 할 때 대체합니다. 범위 는 다음 을 해결할 때 고려됩니다 x
.
>>> foo.__code__.co_consts[1].co_consts[2]
<code object y at 0x10a5df5d0, file "<stdin>", line 4>
>>> foo.__code__.co_consts[1].co_consts[2].co_cellvars
('x',)
물론, 당신의 코드를 읽는 사람들은 이것에 대해 머리를 긁을 것입니다. 당신은 왜 당신이 이것을하고 있는지 설명하는 큰 뚱뚱한 의견을 넣을 수 있습니다.
가장 좋은 해결 방법은 __init__
대신 인스턴스 변수를 만드는 데 사용하는 것입니다.
def __init__(self):
self.y = [self.x for i in range(1)]
머리를 긁지 말고 자신을 설명하는 질문을 피하십시오. 당신 자신의 구체적인 예를 들어, 나는 namedtuple
교실에 저장조차하지 않을 것입니다 . 출력을 직접 사용하거나 (생성 된 클래스를 전혀 저장하지 않음) 전역을 사용하십시오.
from collections import namedtuple
State = namedtuple('State', ['name', 'capital'])
class StateDatabase:
db = [State(*args) for args in [
('Alabama', 'Montgomery'),
('Alaska', 'Juneau'),
# ...
]]
NameError: global name 'x' is not defined
파이썬 3.2에 내가 기대했던 것입니다 3.3.