|
| 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