Namespaces and LEGB Rules
Before vaguely mentioning a few questions about the particularity of a Python assignment statement, the root of the problem is that the namespace mechanism of variables in Python is not the same as the familiar C or Java.
Name space
The so-called namespace refers to the division of variable names according to the different areas of the code, and in a namespace there is often a corresponding relationship between variable names and variable contents. In a language of value semantics, a variable name is often a nickname for a variable's content in an in-memory address, but in Python, the variable name itself is a string object, and the namespace simply corresponds to the string object and object. Further, the namespace in Python is actually a dictionary that records the correspondence of all variable names and variable contents in that space. It will be mentioned later how to call this dictionary to see the namespace.
LEGB rules
LEGB refers to four of the namespaces in Python from low to high levels, namely local, enclosing, Global, built-in. Local refers to the internal space of a function or method, enclosing refers to the inner space of the closure, and global is the overall space of the entire script, while built-in refers to the space of some of the names of Python's topmost system.
Because Python can provide so many different levels of namespaces, when we write a variable name in the program, the interpreter finds the exact contents of the variable name in a certain order. This order is obviously local------enclosing---built-in. If the contents of the relevant variable name are not found in this order, it indicates that the variable name does not exist in any level namespace, and the program will raise a nameerror.
Because namespaces can be nested with each other, it is possible that the program does not run as we would think if you encounter many variables of the same name in the program.
Local and global namespaces
The most common namespace relationships are between local and global:
" Global Var " def Test (): " local var " Print vartest () Print var # Results # local var # Global Var
It is obvious that var in function def is a variable in a local namespace, so the interpreter has found the relevant variable name in the local namespace when the print Var is in the function. On the other hand, when you print var outside of the function, Var belongs to the program's global namespace and has no associated variable name definition in the small namespace below it, and the final interpreter can only find the variable name in the global namespace. If the var = "Local var" in the function is commented out then obviously the result becomes a two-line global VAR because the interpreter also finds the variable name in the global namespace until it is inside the function. On the other hand, if you do want to make certain changes to the global variable var in the function, you can declare a variable with the global keyword to find the global namespace instead of its underlying namespace. For example:
var = global var " def Test (): global = " local var " print Vartest () print var # result # # local var
Because the lower namespace can overwrite the contents of the upper namespace for the interpreter, and the new content in the namespace at the same level can overwrite the contents of the old variable, it is important to notice the change of namespace in the actual work. Take care, for example, in the From module import * , because importing all the variable names in a module into the global namespace might overwrite some of the same name variables that currently exist in the namespace.
enclosing namespaces
The closure space between local and global is used primarily for closures that occur during function-oriented programming. For example, the following code:
' Global Value ' def outer (): ' Enclosed value ' def inner (): ' Local Value ' Print (Var) inner () outer ()# result #Local value
If the value of var = ' local value ' is commented out then the result becomes enclosed, and further the var = ' enclosed value ' is commented out and the result becomes global value. It all seems to be obvious.
Similar to the global namespace, variable names located in the enclosed namespace package can also be specified by the keyword to not search for local, but directly to the enclosed level namespace. The key word is nonlocal. Nonlocal is currently only available in python3.x, python2.x is not yet available.
Built-in namespaces
Python comes with a lot of functions and variables, and the names of these objects belong to the built-in namespaces. If we want to customize a variable with the same name, we will overwrite the variables that are built into the space. For example, I can customize a Len function to overwrite the python's original Len function. Although it is possible to do this, it is not recommended that overwriting the contents of the built-in namespace may have unintended consequences, such as the possibility of invoking the Len function in a third-party library that we might not think of in subsequent function calls, which can cause confusion.
View of namespace content
Python comes with locals () and Globals () two functions that return the relationship of all variable names and values in the local namespace where the current position of this function is called, in the form of a dictionary, and the relationships of all variable names and values in the global namespace. For example:
var1 = 1def Test (): = 2 print locals () print globals () Test ()# result #{' var2 ': 2}#{' var1 ': 1, ' __builtins__ ': <module ' __builtin__ ' (built-in), ' __file__ ': ' d:/pycharmprojects/testproject/test.py ', ' __package__ ': None, ' Test ': <function test at 0x000000000383fb38>, ' __name__ ': ' __main__ ', ' __doc__ ': None}
Some additions
The above mentioned include the namespace, LEGB rules, with the help of common sense and the knowledge gained in other languages, in the actual application of the process of a little brain repair can always be solved. But sometimes there are some surprises in Python (at least for me now, I can't find a clear, self-explanatory statement to explain these surprises). The following is a description of these situations, which is also important for this article.
Do not easily change the values of global variables in the lower namespace
Although it is mentioned earlier, the Global keyword can also be modified in the function by advance declaration, but this is not very good. Can cause a lot of messy errors, one way to solve this is to pass the value of the global variable to the function in the form of a function parameter, and then return the value after the function is processed. After the function definition is returned to the global namespace, the function is called, and the return value is assigned to the globally variable:
var = 1def Test (): Global var = 2Test ()## # # #上面这样不好 , change to the following # # #var = 1def Test (para ):= 2 return= Test (VAR)
Default behavior for Assignment statements
The following code will error:
var = 1def Test (): + = 1test ()# error message #unboundlocalerror : local variable ' var ' referenced before assignment
In general cognition, var + = 1 is equivalent to var = var + 1, this understanding is no problem. In Python, however, the default behavior of an assignment statement is "to create a new local variable of that name as long as there are no declarations of the same name in the current local namespace and no keywords such as global,nonlocal," and then the operation to the right of the assignment statement equals Constrain the computed object to this local variable. In this example, a local variable named var is created, because the name Var already exists in the current local namespace, so the interpreter does not look for the variable in the upper namespace. However, this var does not have a specific object constraint at this time, so the error is caused by the operation on the right side of the equals sign.
If you replace Var + + + 1 in the above code with VAR + 1, without an assignment, the interpreter will not assume that Var is a newly created local variable, and then find the global variable VAR to take its value to do the operation, so it does not error.
Also, if you replace Var with another type that can be changed by non-assignable means (in other words, all mutable types):
var = [1]def Test (): var.append (2) test ()print var# result #[+]
In this case, you can change the global variable without error by avoiding the steps to create a new local variable.
Further, a relatively easy to confuse is to change the Append method in the above example to var[0] + = 1. Such a statement, at first glance or an assignment statement, still appears to be an error, but it is not. Because it is not a variable name on the left side of the equals sign, creating a new local variable by the name of the variable is impossible. Because there is no way to create a new local variable, there is no calculation error caused by a variable whose name exists on the right of the equal sign but does not have an actual value. So var[0] + = 1 This statement will eventually change the global variable var,var from [1] into [2].
The particularity of namespaces in a class
The namespace rules mentioned above are basically based on the premise of function-oriented programming, in object-oriented programming, sometimes there are some strange, similar to the problem of namespaces. In fact, these problems should belong to some laws in the framework of object-oriented mechanism. Some of this stuff I wrote a little bit in the Python class mechanism article, here's a little bit of a supplement.
For example, the problem of naming conflicts between class variables and instance variables:
class Test (): = 1 def__init__(self): = 2= Test ()print T.var
In this case, because the instance variable exists, the Var attribute is referred to as the Var of the instance variable. But if Self.var is not mentioned in the initialization method, then T.var is directly oriented to the Var of the class variable.
On the other hand, if there is no instance variable Var, referencing Self.var in other property methods (except for other methods of the __init__ method) will point to the Var of the class variable. In the case of Var with instance variables, the Self.var in the other attribute methods naturally point to the instance attribute var. If the instance variable var exists, you want to reference the class variable var can be referenced by the class name Test.var or Self.__class__.var.
To mention the special point of the namespace in a little class, if you change the Self.var = 2 in the example code to Self.var + = 1, it seems, in form, as if it had been previously mentioned that the Global keyword declaration does not directly assign a value to a globally variable. The result of that situation was an error unboundlocalerror. But here, there is no error. As a result, the Self.var on the left side of the equal sign is not itself a variable name, just like the previously mentioned var[0] + = 1, no new local variables are created; There seems to be a special mechanism in the class's attribute method that automatically recognizes the self.xxx of the left of the assignment statement as a property of the current instance, and if it does not have a property to automatically create a new property, based on this inference, in the assignment statement in the class, the left side of the equal sign is not a new local if it is self.xxx. A variable is a property of a new instance. Judging from the results, what happens here is that the right side of the equals sign Self.var the var of the class variable, and assigns a value to the Var of a new instance variable after the +1 operation, and since then the instance obtained by this initialization method has a Self.var instance variable whose value is the class variable var+1. Because of the possibility of this confusion, if you want to refer to a class variable in a method of a class, try to refer to it by the class name. For example, the above mentioned Self.var + = 1 is changed to Test.var + = 1 can be created after each instance of the class variable var +1.
The variable name of the for loop pollutes the external namespace
Inside the C,java, the first clause in the FOR loop can declare a variable that is limited to this loop, such as the for (int i,i=0;i<10;i++), and the space occupied by this I is freed after the loop is over.
This for expression in Python is more concise for I in range (10), but I in this case will affect the code after the loop. That is, the variable in the loop header of the Pythonfor Loop statement is not a local variable in the loop statement, but a variable that is leaked to the external namespace:
forIinchRange (10): ifi = = 9: Printlocals ()Printlocals ()#Results#{' __builtins__ ': <module ' __builtin__ ' (built-in);, ' __file__ ': ' d:/pycharmprojects/testproject/test.py ' , ' __package__ ': none, ' I ': 9, ' __name__ ': ' __main__ ', ' __doc__ ': none}#{' __builtins__ ': <module ' __builtin__ ' (built-in), ' __file__ ': ' d:/pycharmprojects/testproject/test.py ', ' __package__ ': none, ' I ': 9, ' __name__ ': ' __main__ ', ' __doc__ ': none}
In the namespace information returned by the second locals function, I is 9, indicating that I is leaking out and polluting the outer namespace. This needs to be noted.
"Python" namespace and LEGB rules