Variable scope

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)

Function definition

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.

closure

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

Unbound variables

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

for statement

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.

List comprehension

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

application?

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.

set comprehension

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

Recommended Posts

Variable scope
About variable scope. .. ..
Python underscore variable
Undefined variable NameError
#Python basics (scope)
EP 15 Know How Closures Interact with Variable Scope