TL ; DR: questo comportamento esiste già da Python 2.1 PEP 227: Nested Scopes ed era noto all'epoca. Se un nome è assegnato all'interno di un corpo di classe (come), allora si presume che sia una variabile locale/globale; se non è assegnato a (x
), può anche indicare una cella di chiusura. Le variabili lessicali non vengono visualizzate come nomi locali/globali nel corpo della classe.
In Python 3.4, dis.dis(func)
mostra la seguente:
>>> dis.dis(func)
4 0 LOAD_CONST 1 ('xlocal')
3 STORE_DEREF 0 (x)
5 6 LOAD_CONST 2 ('ylocal')
9 STORE_FAST 0 (y)
6 12 LOAD_BUILD_CLASS
13 LOAD_CLOSURE 0 (x)
16 BUILD_TUPLE 1
19 LOAD_CONST 3 (<code object C at 0x7f083c9bbf60, file "test.py", line 6>)
22 LOAD_CONST 4 ('C')
25 MAKE_CLOSURE 0
28 LOAD_CONST 4 ('C')
31 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
34 STORE_FAST 1 (C)
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
I LOAD_BUILD_CLASS
carichi il builtins.__build_class__
in pila; questo è chiamato con argomenti __build_class__(func, name)
; dove func
è il corpo della classe e name
è 'C'
. Il corpo della classe è la costante # 3 per la funzione func
:
>>> dis.dis(func.__code__.co_consts[3])
6 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
6 LOAD_CONST 0 ('func.<locals>.C')
9 STORE_NAME 2 (__qualname__)
7 12 LOAD_NAME 3 (print)
15 LOAD_CLASSDEREF 0 (x)
18 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
21 POP_TOP
8 22 LOAD_NAME 3 (print)
25 LOAD_NAME 4 (y)
28 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
31 POP_TOP
9 32 LOAD_CONST 1 (1)
35 STORE_NAME 4 (y)
38 LOAD_CONST 2 (None)
41 RETURN_VALUE
All'interno del corpo della classe, x
si accede con LOAD_CLASSDEREF
(15) mentre è y
carico con LOAD_NAME
(25). LOAD_CLASSDEREF
è un codice operativo Python 3.4+ per caricare i valori dalle celle di chiusura specificatamente all'interno dei corpi delle classi (nelle versioni precedenti veniva utilizzato il generico LOAD_DEREF
); il LOAD_NAME
è per il caricamento dei valori da locals e quindi globals. Tuttavia, le celle di chiusura non mostrano né la gente del posto né i globali.
Ora, poiché il nome è memorizzato all'interno del corpo della classe (35), viene sempre utilizzato come cella di chiusura ma come nome locale/globale. Le celle di chiusura non vengono visualizzate come variabili locali nel corpo della classe.
Questo comportamento è stato vero ever since implementing PEP 227 - nested scopes. E a quel tempo BDFL ha dichiarato che questo non dovrebbe essere risolto - e quindi è stato per questi 13+ anni.
L'unica variazione rispetto PEP 227 è l'aggiunta di nonlocal
in Python 3; se si usa all'interno del corpo della classe, il corpo classe può impostare i valori delle celle nell'ambito contenente:
x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
class C:
nonlocal y # y here now refers to the outer variable
print(x)
print(y)
y = 1
print(y)
print(C.y)
func()
L'uscita ora è
xlocal
ylocal
1
Traceback (most recent call last):
File "test.py", line 15, in <module>
func()
File "test.py", line 13, in func
print(C.y)
AttributeError: type object 'C' has no attribute 'y'
Cioè, print(y)
leggere il valore di la cella dell'ambito di contenimento e y = 1
imposta il valore in quella cella; in questo caso, non è stato creato alcun attributo per la classe C
.
Non darà alcun errore a meno che non si chiami la funzione interna –
Interessante; 'y' dovrebbe essere un'eccezione non associata nel caso di' y', ma non lo è nemmeno quando rendi 'C' un globale. Custodie ad angolo FTW! I corpi di classe sono speciali; non creano un nuovo ambito personale. Di conseguenza, y = 1' lo renderebbe locale, ma fino a quel momento è un enunciato globale * in classe ** solo ***. –
[9.2. Python Scopes and Namespaces] (https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces) e [4.1. Naming e binding] (https://docs.python.org/3/reference/executionmodel.html#naming-and-binding) sono un inizio, potrebbe essere necessario rileggerli spesso fino a quando non affonda. – wwii