Réponse longue : Pour bien comprendre ce qu'il se passe, voici le code désassemblé :
>>> def f():
... x = 0
... def g():
... return x
... return g
...
>>> import dis
>>> dis.dis(f)
0 MAKE_CELL 1 (x)
1 2 RESUME 0
2 4 LOAD_CONST 1 (0)
6 STORE_DEREF 1 (x)
3 8 LOAD_CLOSURE 1 (x)
10 BUILD_TUPLE 1
12 LOAD_CONST 2 (<code object g at 0x00000243D97258F0, file "<stdin>", line 3>)
14 MAKE_FUNCTION 8 (closure)
16 STORE_FAST 0 (g)
5 18 LOAD_FAST 0 (g)
20 RETURN_VALUE
Disassembly of <code object g at 0x00000243D97258F0, file "<stdin>", line 3>:
0 COPY_FREE_VARS 1
3 2 RESUME 0
4 4 LOAD_DEREF 0 (x)
6 RETURN_VALUE
Premièrement, dans la fonction f on créé la variable x avec l'opcode MAKE_CELL. Et on l'initialise à 0 avec LOAD_CONST et STORE_DEREF.
Vient ensuite le MAKE_FUNCTION qui va créer la fonction g() en utilisant un "code object" (le corps de la fonction g compilé), ainsi qu'un tuple qui va contenir l'espace mémoire pour des variables libres.
Ce tuple est créé avec l'opcode BUILD_TUPLE 1 (tuple de 1 élément), le tuple va être créé en prenant un élément sur la pile. Cet élément c'est une référence vers x qui est chargée avec LOAD_CLOSURE (python 3.11, depuis python 3.13 c'est LOAD_FAST).
Enfin, dans le code de la fonction g(), on a COPY_FREE_VARS qui va introduire dans la "stack frame" actuelle les variables référencées par le tuple avec lequel on a construit la "closure".
Démontrant bien qu'il n'existe que 2 espaces de noms : global et local. La fonction g porte avec elle une copie des variables qu'elle capture. Après tout, g n'est pas une fonction mais une "closure" (fonction + environnement).
En pseudo-code python, voici à peu près ce qu'il se passe :
deff():x=0g=make_g(x)returngclassmake_g:# rien ne nous oblige a appeler `self` "self" ;)def__init__(free_vars,x):free_vars.x=xdef__call__(free_vars):returnfree_vars.x
[^] # Re: Petite bizarrerie à noter
Posté par David Delassus (site web personnel) . En réponse au journal écrire du code dans le corps d'une classe python. Évalué à 10.
Tu as bien 2 espaces de nom "global" (accessible via la fonction builtin
globals()et "local" (accessible via la fonction builtinlocals().On vois ici que
xne fait partie ni de "global" ni de "local" :Mais alors, d'où il vient ?
Réponse courte : Du
locals()de la fonctionf().Réponse longue : Pour bien comprendre ce qu'il se passe, voici le code désassemblé :
Premièrement, dans la fonction
fon créé la variablexavec l'opcodeMAKE_CELL. Et on l'initialise à0avecLOAD_CONSTetSTORE_DEREF.Vient ensuite le
MAKE_FUNCTIONqui va créer la fonctiong()en utilisant un "code object" (le corps de la fonctiongcompilé), ainsi qu'un tuple qui va contenir l'espace mémoire pour des variables libres.Ce tuple est créé avec l'opcode
BUILD_TUPLE 1(tuple de 1 élément), le tuple va être créé en prenant un élément sur la pile. Cet élément c'est une référence versxqui est chargée avecLOAD_CLOSURE(python 3.11, depuis python 3.13 c'estLOAD_FAST).Enfin, dans le code de la fonction
g(), on aCOPY_FREE_VARSqui va introduire dans la "stack frame" actuelle les variables référencées par le tuple avec lequel on a construit la "closure".Démontrant bien qu'il n'existe que 2 espaces de noms : global et local. La fonction
gporte avec elle une copie des variables qu'elle capture. Après tout,gn'est pas une fonction mais une "closure" (fonction + environnement).En pseudo-code python, voici à peu près ce qu'il se passe :
https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg