I was confused because I was misunderstanding about the scope of variables in Python, so I studied again. (Checked with CPython 2.7.10 and 3.4.3)
Since the function definition creates a scope, the variables declared in the function definition are invisible to the outside.
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
It's basic. This property prevents the propagation of unintended effects inside and outside the function.
When referencing a variable that is not defined in the function definition, if there is a variable with the same name in the same scope as the function definition, it will be bound to it, so even if the same variable exists in the environment at the time of calling, it will not be affected.
closure.py
def getfunc():
def func():
return s
s = "value@def" #I dare to write it behind
return func
f = getfunc()
s = "value@call"
print(f())
% python2.7 closure.py
value@def
% python3.4 closure.py
value@def
Same with lambda
closure_lambda.py
def getfunc():
func = lambda: s
s = "value@def" #I dare to write it behind
return func
f = getfunc()
s = "value@call"
print(f())
% python2.7 closure_lambda.py
value@def
% python3.4 closure_lambda.py
value@def
If it is not bound when the function is defined, the environment at the time of calling is referred to. Be aware that unintended effects may be propagated.
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
Since the for statement does not create scope, both loop variables and variables declared internally are shared outside the for statement.
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
Note that it is wrong to assume the behavior of C ++ for (i can be referenced only inside the loop).
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.
Variables used in list comprehensions are leaked out in python2.
reference:
list_comprehension.py
[i for i in range(3)]
print(i)
% python2.7 list_comprehension.py
2
Not knowing this, I was wondering for a while why I made a programming mistake and didn't get an error. In addition, it does not leak in 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
Taking advantage of the leaked variable scope, it is written in Python's lambda is broken! You can see why the problem is occurring
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)
In other words, I'm generating three lambda functions, but I'm binding a single i.
In the case of set comprehension, variables are not leaked out (as in list comprehension).
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