Skip to content

Commit cc59b52

Browse files
committed
Add section about variable scopes.
1 parent b4e411f commit cc59b52

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ written correctly.
8585
- [The `continue` statement](src/control_flow/test_break.py)
8686
5. **Functions**
8787
- [Function Definition](src/functions/test_function_definition.py) (`def` and `return` statements)
88+
- [Scopes of Variables Inside Functions](src/functions/test_function_scopes.py) (`global` and `nonlocal` statements)
8889
- [Default Argument Values](src/functions/test_function_default_arguments.py)
8990
- [Keyword Arguments](src/functions/test_function_keyword_arguments.py)
9091
- [Arbitrary Argument Lists](src/functions/test_function_arbitrary_arguments.py)

src/functions/test_function_scopes.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""Scopes and Namespaces.
2+
3+
@see: https://docs.python.org/3/tutorial/classes.html#scopes-and-namespaces-example
4+
5+
A NAMESPACE is a mapping from names to objects. Most namespaces are currently implemented as Python
6+
dictionaries, but that’s normally not noticeable in any way (except for performance), and it may
7+
change in the future. Examples of namespaces are: the set of built-in names (containing functions
8+
such as abs(), and built-in exception names); the global names in a module; and the local names
9+
in a function invocation. In a sense the set of attributes of an object also form a namespace.
10+
The important thing to know about namespaces is that there is absolutely no relation between names
11+
in different namespaces; for instance, two different modules may both define a function maximize
12+
without confusion — users of the modules must prefix it with the module name.
13+
14+
By the way, we use the word attribute for any name following a dot — for example, in the expression
15+
z.real, real is an attribute of the object z. Strictly speaking, references to names in modules are
16+
attribute references: in the expression modname.func_name, modname is a module object and func_name
17+
is an attribute of it. In this case there happens to be a straightforward mapping between the
18+
module’s attributes and the global names defined in the module: they share the same namespace!
19+
20+
A SCOPE is a textual region of a Python program where a namespace is directly accessible.
21+
“Directly accessible” here means that an unqualified reference to a name attempts to find the name
22+
in the namespace.
23+
24+
Although scopes are determined statically, they are used dynamically. At any time during execution,
25+
there are at least three nested scopes whose namespaces are directly accessible:
26+
- the innermost scope, which is searched first, contains the local names.
27+
- the scopes of any enclosing functions, which are searched starting with the nearest enclosing
28+
scope, contains non-local, but also non-global names.
29+
- the next-to-last scope contains the current module’s global names.
30+
- the outermost scope (searched last) is the namespace containing built-in names.
31+
32+
BE CAREFUL!!!
33+
-------------
34+
Changing global or nonlocal variables from within an inner function might be a BAD
35+
practice and might lead to harder debugging and to more fragile code! Do this only if you know
36+
what you're doing.
37+
"""
38+
39+
# pylint: disable=invalid-name
40+
test_variable = 'initial global value'
41+
42+
43+
def test_function_scopes():
44+
"""Scopes and Namespaces Example"""
45+
46+
# This is an example demonstrating how to reference the different scopes and namespaces, and
47+
# how global and nonlocal affect variable binding:
48+
49+
# pylint: disable=redefined-outer-name
50+
test_variable = 'initial value inside test function'
51+
52+
def do_local():
53+
# Create variable that is only accessible inside current do_local() function.
54+
# pylint: disable=redefined-outer-name
55+
test_variable = 'local value'
56+
return test_variable
57+
58+
def do_nonlocal():
59+
# Address the variable from outer scope and try to change it.
60+
# pylint: disable=redefined-outer-name
61+
nonlocal test_variable
62+
test_variable = 'nonlocal value'
63+
return test_variable
64+
65+
def do_global():
66+
# Address the variable from very global scope and try to change it.
67+
# pylint: disable=redefined-outer-name,global-statement
68+
global test_variable
69+
test_variable = 'global value'
70+
return test_variable
71+
72+
# On this level currently we have access to local for test_function_scopes() function variable.
73+
assert test_variable == 'initial value inside test function'
74+
75+
# Do local assignment.
76+
# It doesn't change global variable and variable from test_function_scopes() scope.
77+
do_local()
78+
assert test_variable == 'initial value inside test function'
79+
80+
# Do non local assignment.
81+
# It doesn't change global variable but it does change variable
82+
# from test_function_scopes() function scope.
83+
do_nonlocal()
84+
assert test_variable == 'nonlocal value'
85+
86+
# Do global assignment.
87+
# This one changes global variable but doesn't change variable from
88+
# test_function_scopes() function scope.
89+
do_global()
90+
assert test_variable == 'nonlocal value'
91+
92+
93+
def test_global_variable_access():
94+
"""Testing global variable access from within a function"""
95+
96+
# Global value of test_variable has been already changed by do_global() function in previous
97+
# test so let's check that.
98+
# pylint: disable=global-statement
99+
global test_variable
100+
assert test_variable == 'global value'
101+
102+
# On this example you may see how accessing and changing global variables from within inner
103+
# functions might make debugging more difficult and code to be less predictable. Since you
104+
# might have expected that test_variable should still be equal to 'initial global value' but
105+
# it was changed by "someone" and you need to know about the CONTEXT of who had changed that.
106+
# So once again access global and non local scope only if you know what you're doing otherwise
107+
# it might be considered as bad practice.

0 commit comments

Comments
 (0)