J'étais confus quant à la portée des variables Python, alors je l'ai réétudiée. (Vérifié avec CPython 2.7.10 et 3.4.3)
Puisque la définition de fonction crée une étendue, les variables déclarées dans la définition de fonction ne sont pas visibles de l'extérieur.
func_def.py
def func():
i = 0
print(i)
% python2.7 func_def.py
Traceback (most recent call last):
File "func_def.py", line 3, in <module>
print(i)
NameError: name 'i' is not defined
% python3.4 func_def.py
Traceback (most recent call last):
File "func_def.py", line 3, in <module>
print(i)
NameError: name 'i' is not defined
C'est basique. Cette propriété empêche la propagation d'effets involontaires à l'intérieur et à l'extérieur de la fonction.
Lors du référencement d'une variable qui n'est pas définie dans la définition de fonction, s'il existe une variable avec le même nom dans la même portée que la définition de fonction, elle y est liée, donc même si la même variable existe dans l'environnement au moment de l'appel, elle n'est pas affectée.
closure.py
def getfunc():
def func():
return s
s = "value@def" #J'ose l'écrire derrière
return func
f = getfunc()
s = "value@call"
print(f())
% python2.7 closure.py
value@def
% python3.4 closure.py
value@def
Idem avec lambda
closure_lambda.py
def getfunc():
func = lambda: s
s = "value@def" #J'ose l'écrire derrière
return func
f = getfunc()
s = "value@call"
print(f())
% python2.7 closure_lambda.py
value@def
% python3.4 closure_lambda.py
value@def
S'il n'est pas lié lors de la définition de la fonction, il est fait référence à l'environnement au moment de l'appel. Veuillez noter qu'une transmission involontaire d'effets peut se produire.
func_call.py
def getfunc():
def func():
return s
return func
f = getfunc()
s = "value@call"
print(f())
% python2.7 func_call.py
value@call
% python3.4 func_call.py
value@call
Étant donné que l'instruction for ne crée pas de portée, les variables de boucle et les variables déclarées en interne sont partagées en dehors de l'instruction for.
for.py
for i in range(3):
s = "value inside loop"
print(i)
print(s)
% python2.7 for.py
2
value inside loop
% python3.4 for.py
2
value inside loop
Notez qu'il est erroné d'assumer le comportement de C ++ pour (i ne peut être référencé que dans la boucle).
for.cpp
#include <stdio.h>
int main()
{
const char *s = NULL;
for (int i = 0; i < 3; i++) {
s = "value inside loop";
}
printf("%d\n", i);
printf("%s\n", s);
return 0;
}
% clang for.cpp
for.cpp:8:18: error: use of undeclared identifier 'i'
printf("%d\n", i);
^
1 error generated.
Les variables utilisées dans la notation d'inclusion de liste sont divulguées vers l'extérieur dans python2.
référence:
list_comprehension.py
[i for i in range(3)]
print(i)
% python2.7 list_comprehension.py
2
Ne sachant pas cela, je me demandais pourquoi j'ai fait une erreur de programmation et je n'ai pas eu d'erreur pendant un moment. De plus, il ne fuit pas dans python3.
% python3.4 list_comprehension.py
Traceback (most recent call last):
File "list_comprehension.py", line 2, in <module>
print(i)
NameError: name 'i' is not defined
Profitant de la portée des variables qui ont fui, il est écrit en Le lambda de Python est cassé! Vous pouvez voir pourquoi le problème se produit
lambda_in_list_comprehension.py
fs = [(lambda: i) for i in range(3)]
print(fs)
print(fs[0](),fs[1](),fs[2]())
i += 1
print(fs[0](),fs[1](),fs[2]())
% python2.7 lambda_in_list_comprehension.py
[<function <lambda> at 0x10fd67f50>, <function <lambda> at 0x10fd70050>, <function <lambda> at 0x10fd700c8>]
(2, 2, 2)
(3, 3, 3)
En d'autres termes, nous générons trois fonctions lambda, mais nous lions un seul i.
Dans le cas de la notation d'inclusion d'ensemble, la variable ne s'échappe pas (comme dans la notation d'inclusion de liste).
set_comprehension.py
{i for i in range(3)}
print(i)
% python2.7 set_comprehension.py
Traceback (most recent call last):
File "set_comprehension.py", line 2, in <module>
print(i)
NameError: name 'i' is not defined
% python3.4 set_comprehension.py
Traceback (most recent call last):
File "set_comprehension.py", line 2, in <module>
print(i)
NameError: name 'i' is not defined