I investigated how the scope looks

When I was looking at the closure implementation, I didn't know why it worked, so I looked at how the scope looked. I think it's very basic, but I couldn't find an article on the net that I could understand about how the scope looks. As a result of actually moving it, I thought it was difficult to explain.

Scope seen from the top level

Let's check the state of global variables and local variables with the following code.

Confirmation (1)

from pprint import pprint
print "-----globals----->>>"
pprint(globals())
print "-----locals----->>>"
pprint(locals())

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'pprint': <function pprint at 0x02222770>}
-----locals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'pprint': <function pprint at 0x02222770>}

In this state, the contents displayed for both global variables and local variables are the same. What happens if you define one variable?

Confirmation (2)

from pprint import pprint
a = 123
pprint(globals())
print "-----globals----->>>"
print "-----locals----->>>"
pprint(locals())

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>}
-----locals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>}

In the top-level script execution environment, the output contents of locals () and globals () are the same. The defined variables are present in both outputs. (*** It is judged that globals () and locals () are outputting the names of global scope and local scope, respectively, and the following is described **)

Also, pprint is in the output of globals (). (also in locals ()) Modules and functions imported by import will be included in the global scope. (Those that can be directly referenced at the top level are in the global scope)

What happens if you define a function and look at the scope from within the function?

Scope as seen from the function

##Verification(3)
a = 123
def tes001():
	b = 234
	print "-----globals----->>>"
	pprint(globals())
	print "-----locals----->>>"
	pprint(locals())
tes001()

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02422770>,
 'tes001': <function tes001 at 0x02422BF0>}
-----locals----->>>
{'b': 234}

The variable b defined in the function block exists in the local scope but not in the global scope.

Now let's define the variables after displaying the contents of locals () and globals () in the function block.

Confirmation (4)

a = 123
def tes001():
	print "-----globals----->>>"
	pprint(globals())
	print "-----locals----->>>"
	pprint(locals())
	b = 234
tes001()

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BF0>}
-----locals----->>>
{}

In this case, the variable b is not in the local scope either. What happens if you put the output of locals () before and after the variable definition?

Confirmation (5)

a = 123
def tes001():
	print "-----globals----->>>"
	pprint(globals())
	print "-----locals(1)----->>>"
	pprint(locals())
	b = 234
	print "-----locals(2)----->>>"
	pprint(locals())
tes001()

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BF0>}
-----locals(1)----->>>
{}
-----locals(2)----->>>
{'b': 234}

This is as expected. It is not in the output before the variable is defined, but it is in the output after it is defined.

Nest functions to find out scope

Next, let's nest the definition of the function and check the scope from the inner function.

Confirmation (6)

a = 123
def tes001():
	b = 234
	def inner():
		c = 345
		print "-----globals----->>>"
		pprint(globals())
		print "-----locals----->>>"
		pprint(locals())
	inner()
tes001()

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x022226F0>,
 'tes001': <function tes001 at 0x02222BF0>}
-----locals----->>>
{'c': 345}

There is no b in the local scope of inner (). What happens if you refer to b in inner ()? Display the output of locals () before and after referencing.

Confirmation (7)

a = 123
def tes001():
	b = 234
	def inner():
		c = 345
		print "-----globals----->>>"
		pprint(globals())
		print "-----locals(1)----->>>"
		pprint(locals())
		print b
		print "-----locals(2)----->>>"
		pprint(locals())
	inner()
tes001()

Execution result


-----globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x022226F0>,
 'tes001': <function tes001 at 0x02222BF0>}
-----locals(1)----->>>
{'b': 234, 'c': 345}
234
-----locals(2)----->>>
{'b': 234, 'c': 345}

This time, the variable b also exists in the local scope before referencing the variable b ( print b). This looks a little different from "confirmation (5)". The behavior is different between reference and assignment. Sensorially,

-----locals(1)----->>>
{'c': 345}
234
-----locals(2)----->>>
{'b': 234, 'c': 345}

I thought it would be like this. From the above result, it is assumed that the scope including the variable b is created in the local scope of the inner () function before executing the code. In the first place, the variable b is included in the local scope even though it is undefined in the inner () function. From the viewpoint of movement, when a function is nested, when a variable that is undefined in the inner function and is defined in the outer is referenced by the inner function, the definition contents of the outer function are copied and the inner function is copied. Set to the local scope of. This scope setting seems to be done before code execution. The feeling is that something like the initial value of the local scope is created before the function is executed.

Let's examine the scope when the variable defined on the outside is changed by the function on the inside.

Confirmation (8)

a = 123
def tes001():
	b = 234
	def inner():
		c = 345
		print "-----inner: globals----->>>"
		pprint(globals())
		print "-----inner: locals(1)----->>>"
		pprint(locals())
		print b
		b = 777
		print "-----inner: locals(2)----->>>"
		pprint(locals())
	inner()
	print "-----outer: locals(3)----->>>"
	pprint(locals())
tes001()

Execution result


-----inner: globals----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes002.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x022226F0>,
 'tes001': <function tes001 at 0x02222BF0>}
-----inner: locals(1)----->>>
{'c': 345}
Traceback (most recent call last):
  File "tes002.py", line 19, in <module>
    tes001()
  File "tes002.py", line 16, in tes001
    inner()
  File "tes002.py", line 12, in inner
    print b
UnboundLocalError: local variable 'b' referenced before assignment

After executing the above, I got an error (Unbound Local Error). Interestingly here, the content of locals (), which is first output by the inner () function, does not include the variable b. If only the reference ( print b) was done, b was included. The only difference is whether the inner function sets the variables defined on the outer. After all, it seems that the scope is prepared before executing the code. The code works based on this prepared scope. However, in the above sample, since there is a setting code ( b = 777) in the internal function, the variable b is not set in the local scope prepared at runtime. In this state (variable b is not in scope), an error occurs when trying to refer to variable b.

Let's organize the results confirmed so far

If you organize the contents confirmed so far, including some reasoning (speculation),

--In the top-level execution environment, all defined variables and functions are set to the global scope. Variables are defined by assignment. --The same global scope can be seen from both the top level and the function. (Probably) --Variables and functions (internal functions) defined in a function are set in the local scope of that function. -If you refer to an undefined variable in a function, before executing the code, look for the variable defined in the outer function from near the function to the outside, and local scope the found variable. Set to. The function works with this local scope. --However, when assigning to a variable in a function, the variable is defined in the function, and the variable is not set in the local scope at runtime. In this state, if you try to refer to a variable before assignment, an error (UnboundLocalError) will occur.

Examine how the scope looks when a global variable is referenced and set in a function

Next, let's examine how the scope looks when a global variable is referenced and set in a function.

Confirmation (8)

a = 123
def tes001():
	print a
	print "-----tes001: globals(1)----->>>"
	pprint(globals())
	print "-----tes001: locals(1)----->>>"
	pprint(locals())
	a = 345
	print "-----tes001: globals(2)----->>>"
	pprint(globals())
	print "-----tes001: locals(2)----->>>"
	pprint(locals())
tes001()
print "-----top: globals(3)----->>>"
pprint(globals())
print "-----top: locals(3)----->>>"
pprint(locals())

Execution result


Traceback (most recent call last):
  File "tes003.py", line 15, in <module>
    tes001()
  File "tes003.py", line 5, in tes001
    print a
UnboundLocalError: local variable 'a' referenced before assignment

In this case, the same as confirmation (7). UnboundLocalError. Same behavior when global scope and local scope are mixed. In the function tes001 (), assigning the variable a and then referencing it should work.

Confirmation (9)

a = 123
def tes001():
	print "-----tes001: globals(1)----->>>"
	pprint(globals())
	print "-----tes001: locals(1)----->>>"
	pprint(locals())
	a = 345
	print a
	print "-----tes001: globals(2)----->>>"
	pprint(globals())
	print "-----tes001: locals(2)----->>>"
	pprint(locals())
tes001()
print "-----top: globals(3)----->>>"
pprint(globals())
print "-----top: locals(3)----->>>"
pprint(locals())

Execution result


-----tes001: globals(1)----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes003.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BB0>}
-----tes001: locals(1)----->>>
{}
345
-----tes001: globals(2)----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes003.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BB0>}
-----tes001: locals(2)----->>>
{'a': 345}
-----top: globals(3)----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes003.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BB0>}
-----top: locals(3)----->>>
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'tes003.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 123,
 'pprint': <function pprint at 0x02222770>,
 'tes001': <function tes001 at 0x02222BB0>}

This worked fine. In the output of tes001: globals (2), a is 123 and in the output of tes001: locals (2) 345. In tes001 (), the local variable a is rewritten, so the value of a when returning to the top level remains the value (123) before the function call. I understand the theory somehow, but it feels a little strange. Remove the print statement and try to arrange the code when it works and when it doesn't.

Organize code for functions that access variables defined as global variables

If it works (1)

a = 123
def tes001():
	print a
tes001()

If it works (2)

a = 123
def tes001():
	a = 345
	print a
tes001()

If it doesn't work

a = 123
def tes001():
	print a			#UnboundLocalError
	a = 345
tes001()

I've made various movements and gave reasons for the operation results, but looking at the above, it's still difficult to understand. I'd like to investigate a little more, but it's getting longer, so I'll stop here. I think I can explain the implementation of closures.

Recommended Posts

I investigated how the scope looks
I investigated the mechanism of flask-login!
I investigated the device tree Overlay
I counted the grains
I investigated the reinforcement learning algorithm of algorithmic trading
I tried to simulate how the infection spreads with Python
I didn't know how to use the [python] for statement
I investigated the pretreatment that can be done with PyCaret
I tried to summarize how to use the EPEL repository again
I examined the device tree
How to use the generator
I tweeted from the terminal!
I touched the Qiita API
I tried the changefinder library!
I investigated Fluentd's http output
How to use the decorator
How to increase the axis
I downloaded the python source
How to start the program
I read the SHAP paper
I learned how the infrared remote control works with Raspberry Pi
I investigated the X-means method that automatically estimates the number of clusters
I summarized how to change the boot parameters of GRUB and GRUB2
I investigated the behavior of the difference between hard links and symbolic links
I investigated the relationship between Keras stateful LSTM and hidden state