클래스 정의의 목록 이해에서 클래스 변수 액세스
클래스 정의 내의 목록 이해에서 다른 클래스 변수에 액세스하려면 어떻게 해야 합니까?다음은 Python 2에서 작동하지만 Python 3에서는 실패합니다.
class Foo:
x = 5
y = [x for i in range(1)]
Python 3.2는 다음 오류를 나타냅니다.
NameError: global name 'x' is not defined
중Foo.x그것도 안 돼요.Python 3 python python python 에 python python python python python python python python python python?
조금 더 복잡한 동기 부여 예:
from collections import namedtuple
class StateDatabase:
State = namedtuple('State', ['name', 'capital'])
db = [State(*args) for args in [
['Alabama', 'Montgomery'],
['Alaska', 'Juneau'],
# ...
]]
예에서는 " " 입니다.apply()적절한 회피책이 될 수 있었지만 Python 3에서는 안타깝게도 삭제되어 있습니다.
클래스 범위와 목록, 집합 또는 사전 포함 및 생성기 식이 혼합되지 않습니다.
그 이유 또는 이에 대한 공식 용어
Python 3에서는 로컬 변수가 주변 스코프로 블리딩되는 것을 방지하기 위해 목록 압축이 적절한 스코프(로컬 네임스페이스)로 제공되었습니다(이해 범위 이후에도 목록 이해 재바인드 이름 참조). 이게 맞습니까?)모듈이나 함수에서 이러한 목록 이해 기능을 사용하는 것은 좋지만, 수업에서 스코핑은 조금 이상합니다.
클래스 범위의 이름에 액세스할 수 없습니다.이름은 가장 안쪽을 둘러싸는 함수 범위에서 해결됩니다.클래스 정의가 중첩된 범위의 체인에서 발생하는 경우 해결 프로세스는 클래스 정의를 건너뜁니다.
복합 스테이트먼트 문서에는 다음과 같은 내용이 기재되어 있습니다.
그런 다음 새로 만든 로컬 네임스페이스와 원래 글로벌 네임스페이스를 사용하여 클래스의 스위트가 새 실행 프레임에서 실행됩니다(일반적으로 스위트에는 함수 정의만 포함됩니다).클래스 스위트의 실행이 완료되면 실행 프레임은 삭제되지만 로컬 네임스페이스는 저장됩니다.[4] 그런 다음 기본 클래스의 상속 목록과 속성 사전의 저장된 로컬 이름 공간을 사용하여 클래스 개체가 생성됩니다.
강조합니다. 실행범위는 일시적인 범위입니다.
객체의 수되지 않은 동작이 ?클래스 메서드가 참조되면 어떻게 됩니까?x로서 를 합니다.Foo.x예를 들면요?더 중요한 것은, 그것이 다음 서브클래스에 무엇을 의미합니까?FooPython은 함수 범위와 매우 다르기 때문에 클래스 범위를 다르게 취급해야 합니다.
마지막으로 이그제큐션 모델 문서의 링크된 명명 및 바인딩 섹션에서 클래스 범위를 명시적으로 언급하고 있습니다.
클래스 블록에 정의되어 있는 이름의 범위는 클래스 블록으로 한정됩니다.메서드의 코드 블록에는 확장되지 않습니다.함수 범위를 사용하여 구현되어 있기 때문에, 이러한 코드 블록에는 통합 및 생성 식도 포함됩니다.이것은, 다음의 에러가 발생하는 것을 의미합니다.
class A: a = 42 b = list(a + i for i in range(10))
요약하면, 클래스 범위에 액세스 할 수 없습니다.이러한 범위에는 함수, 리스트의 통합 또는 생성자 식에 둘러싸인 클래스 범위는 존재하지 않는 것처럼 동작합니다.Python 2에서는 목록 압축이 숏컷을 사용하여 구현되었지만 Python 3에서는 자체 함수 범위(그동안 가지고 있었어야 했던)를 얻었기 때문에 당신의 예는 깨졌습니다.다른 이해 유형은 Python 버전에 관계없이 고유한 범위를 가지므로 Python 2에서 set 또는 dict 이해와 유사한 예가 깨질 수 있습니다.
# Same error, in Python 2 or 3
y = {x: x for i in range(1)}
(작은) 예외 또는 한 부품이 여전히 작동하는 이유
Python 버전에 관계없이 주변 스코프에서 실행되는 이해식 또는 생성기 식 중 하나가 있습니다.그것은 가장 외설적인 표현일 것이다.예에서는요, 이에서는요.range(1):
y = [x for i in range(1)]
# ^^^^^^^^
이렇게 '마지막으로x에러를 발생시키지 않습니다.
# Runs fine
y = [i for i in range(x)]
은 가장 에 있는 반복 가능한 됩니다. 에 여러 개의 이해도가 입니다. 이해에 여러 개의 항목이 있는 경우for, inner의 , " " "for이치
# NameError
y = [i for i in range(1) for j in range(x)]
# ^^^^^^^^^^^^^^^^^ -----------------
# outer loop inner, nested loop
이 설계결정은 제너레이터 식에서 가장 바깥쪽의 반복가능성이 오류를 발생시키는 경우 또는 가장 바깥쪽의 반복가능성이 오류를 발생시키지 않는 것으로 판명된 경우 반복시간이 아닌 genexp 생성시 오류를 발생시키기 위해 이루어졌다.통합은 일관성을 위해 이 동작을 공유합니다.
후드 밑을 들여다보거나, 당신이 원했던 것보다 훨씬 더 상세하게
모듈을 사용하면 이 모든 것이 동작하고 있음을 알 수 있습니다.다음 예에서는 Python 3.3을 사용하고 있습니다. 왜냐하면 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클래스 바디를 함수로 만들고 호출합니다.그런 다음 이 호출의 결과를 사용하여 클래스의 네임스페이스를 만듭니다.__dict__직직아
여기서 주의할 점은 바이트코드에 중첩된 코드 오브젝트가 포함되어 있다는 것입니다.Python에서는 클래스 정의, 함수, 통합 및 생성자는 모두 바이트코드뿐만 아니라 로컬 변수, 상수, 글로벌에서 가져온 변수 및 중첩된 범위에서 가져온 변수를 나타내는 코드 오브젝트로 표현됩니다.컴파일된 바이트 코드는 이러한 구조를 참조하며, 파이썬 인터프리터는 제시된 바이트 코드에 액세스하는 방법을 알고 있습니다.
해야 할 한 점은 시 입니다. 즉, Python은 컴파일 시에 이러한 구조를 .class 오브젝트suet)입니다.<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의「 」를 보존한 해 주세요.5x된 함수는 , , , , , , , , , , , , , , , , , , , , 。그것은 리스트 이해입니다.클래스 본문과 마찬가지로 함수 오브젝트로 랩되어 있습니다.이치노range(1)반복자에게 전달되는 루프 코드에 사용할 수 있습니다.코드로 , 「 」는 「 」입니다.range(1)이치노
여기서 함수 또는 제너레이터의 코드 객체와 이해의 코드 객체의 유일한 차이점은 상위 코드 객체가 실행되었을 때 후자가 즉시 실행된다는 것입니다. 바이트 코드는 단순히 함수를 즉시 생성하고 몇 가지 작은 단계로 실행하기만 하면 됩니다.
Python 2.x는 대신 인라인 바이트 코드를 사용합니다. 다음은 Python 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)2.x로 Python 2.x 에서도 Python 2.x가 사용됩니다.FOR_ITER루프를 반복하여 출력을 생성합니다.
★★★★★★★를 정의했을 경우x foox 변수 스코프 다음과 같습니다.
>>> 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
요약하면 다음과 같습니다.
- 목록 압축은 Python 3에서 자체 코드 객체를 가져오며 함수, 생성자 또는 컴프리먼트에 대한 코드 개체 간에 차이가 없습니다. 이해 코드 개체는 임시 함수 개체로 감싸지고 즉시 호출됩니다.
- 코드 오브젝트는 컴파일 시 작성되며 로컬 이외의 변수는 코드의 중첩된 범위에 따라 글로벌 변수 또는 자유 변수로 표시됩니다.클래스 바디는 이러한 변수를 검색하는 범위로 간주되지 않습니다.
- 코드를 실행할 때 Python은 글로벌 또는 현재 실행 중인 개체의 폐쇄만 조사하면 됩니다.컴파일러는 클래스 본문을 스코프로 포함하지 않았기 때문에 임시 함수 네임스페이스는 고려되지 않습니다.
회피책 또는 대처법
「」의 명시적인 는, 「 」xvariable은 함수에서와 같이 목록 이해에 클래스 클래스 맵 변수를 사용할 수 있습니다.
>>> 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'),
# ...
]]
Python 3의 결점이라고 생각합니다.그들이 바꾸길 바란다.
Way (, Old Way (2.7 old old old old, old old old old)NameError: name 'x' is not defined( (3+)
class A:
x = 4
y = [x+i for i in range(1)]
": " "로 를 지정합니다.A.x이 안 되다
새로운 방법(3+로 동작):
class A:
x = 4
y = (lambda x=x: [x+i for i in range(1)])()
구문이 너무 못생겨서 일반적으로 생성자의 모든 클래스 변수를 초기화합니다.
인정된 답변은 훌륭한 정보를 제공하지만, 여기에는 목록 이해와 생성자 표현식의 차이인 몇 가지 다른 주름이 있는 것 같습니다.내가 가지고 놀았던 데모:
class Foo:
# A class-level variable.
X = 10
# I can use that variable to define another class-level variable.
Y = sum((X, X))
# Works in Python 2, but not 3.
# In Python 3, list comprehensions were given their own scope.
try:
Z1 = sum([X for _ in range(3)])
except NameError:
Z1 = None
# Fails in both.
# Apparently, generator expressions (that's what the entire argument
# to sum() is) did have their own scope even in Python 2.
try:
Z2 = sum(X for _ in range(3))
except NameError:
Z2 = None
# Workaround: put the computation in lambda or def.
compute_z3 = lambda val: sum(val for _ in range(3))
# Then use that function.
Z3 = compute_z3(X)
# Also worth noting: here I can refer to XS in the for-part of the
# generator expression (Z4 works), but I cannot refer to XS in the
# inner-part of the generator expression (Z5 fails).
XS = [15, 15, 15, 15]
Z4 = sum(val for val in XS)
try:
Z5 = sum(XS[i] for i in range(len(XS)))
except NameError:
Z5 = None
print(Foo.Z1, Foo.Z2, Foo.Z3, Foo.Z4, Foo.Z5)
가장 바깥쪽 반복기는 주변 범위에서 평가되므로 와 함께 사용하여 의존성을 이해 범위로 전달할 수 있습니다.
import itertools as it
class Foo:
x = 5
y = [j for i, j in zip(range(3), it.repeat(x))]
네스트된 '네스트드'도할 수 .for이해에 루프하고 가장 바깥쪽의 반복 가능한 종속성을 포함합니다.
class Foo:
x = 5
y = [j for j in (x,) for i in range(3)]
OP의 구체적인 예:
from collections import namedtuple
import itertools as it
class StateDatabase:
State = namedtuple('State', ['name', 'capital'])
db = [State(*args) for State, args in zip(it.repeat(State), [
['Alabama', 'Montgomery'],
['Alaska', 'Juneau'],
# ...
])]
이것은 Python의 버그입니다.압축은 루프에 상당하는 것으로 어드버타이즈 됩니다만, 클래스에서는 그렇지 않습니다.적어도 Python 3.6.6까지는 클래스에서 사용되는 이해에서 외부에서 하나의 변수만 이해 내에서 액세스할 수 있으므로 가장 바깥쪽의 반복자로 사용해야 합니다.함수에서는 이 범위 제한이 적용되지 않습니다.
이것이 버그인 이유를 설명하기 위해 원래의 예시로 돌아가 보겠습니다.실패:
class Foo:
x = 5
y = [x for i in range(1)]
하지만 이 방법은 효과적입니다.
def Foo():
x = 5
y = [x for i in range(1)]
이 제한은 참조 가이드의 이 섹션 끝에 설명되어 있습니다.
이게 의도적인 디자인일 수도 있지만 IMHO, 안 좋은 디자인이에요.제가 여기 전문가가 아니라는 걸 알아요. 그리고 이 배경의 이유를 읽어보려고 노력했지만, 평범한 Python 프로그래머라면 이해할 수 없을 것 같아요.
나에게 이해는 정규 수학 표현과 크게 다르지 않아 보인다.예를 들어 'foo'가 로컬 함수 변수인 경우 다음과 같은 작업을 쉽게 수행할 수 있습니다.
(foo + 5) + 7
하지만 난 할 수 없어:
[foo + x for x in [1,2,3]]
현재 범위 내에 하나의 표현이 존재하고 다른 표현은 자신의 범위를 만든다는 사실은 매우 놀랍고, 말장난 의도 없이 '이해할 수 없다'는 것이다.
왜 이것이 버그가 아닌 기능인지 이해하기 위해 꽤 오랜 시간을 소비했습니다.
간단한 코드를 고려합니다.
a = 5
def myfunc():
print(a)
myfunc()에 정의되어 있는 "a"가 없기 때문에 스코프가 확장되어 코드가 실행됩니다.
이제 클래스의 동일한 코드를 고려합니다.그러면 클래스 인스턴스의 데이터에 액세스하는 작업이 완전히 중단되기 때문에 작동하지 않습니다.기본 클래스 또는 인스턴스의 변수에 액세스하고 있는지 알 수 없습니다.
리스트 이해는 같은 효과의 하위 케이스일 뿐입니다.
언급URL : https://stackoverflow.com/questions/13905741/accessing-class-variables-from-a-list-comprehension-in-the-class-definition
'programing' 카테고리의 다른 글
| Python에서 현재 OS를 찾으려면 어떻게 해야 하나요? (0) | 2022.10.08 |
|---|---|
| python에서 목록 항목 집합을 구성하는 방법은 무엇입니까? (0) | 2022.10.08 |
| React에서 중첩된 상태 특성을 업데이트하는 방법 (0) | 2022.10.08 |
| mysql의 float(2,2)와 float()의 차이 (0) | 2022.10.08 |
| Python에서는 여러 컨스트럭터를 정의할 수 없습니까? (0) | 2022.10.08 |