This article introduces the Python variable scope LEGB usage analysis, the text of the sample code through the introduction of the very detailed, for everyone's learning or work with certain reference learning value, the need for friends can refer to the following
A closure is, well, a function nested inside a function. Decorators are just a special case of closures, in that if the parameter of the outer function points to the address of the function to be decorated (not necessarily the address, feel free to do so), you get "@xxx", which is quite interesting. The role of the decorator is to fill in new functionality to the original function without changing the code of the original function. The way it's written, it's quite simple and elegant.
A common way to write a decorator
# Generic way of writing decorators def out(func): def inner(*args, **kwargs): print("we are checking...", args[0]) return func(*args, **kwargs) return inner @out def check_2019_nCov(name): return f"now, {name} is very healthy..." tmp = check_2019_nCov('youge') print(tmp) # output we are checking... youge now, youge is very healthy...
Passing parameters to the decorator
Although this way of writing "@" requires that the parameter of the outer function is a func address, in order to pass a parameter, it is enough to wrap another layer of function around the outer function (whose function is to accept the parameter), which is equivalent to expanding the scope of action and getting the parameter.
# The outermost function serves to pass parameters to the decorator # def get_param(*args, **kwargs): def out(func): def inner(*args, **kwargs): print("get params", args, kwargs) return func(*args, **kwargs) return inner return out @get_param("youge") def check_2019_nCov(name): return f"now, {name} is very healthy..." tmp = check_2019_nCov("youge") print(tmp) # output get params ('youge',) {} now, youge is very healthy...
This kind of decorator passing parameters application scenario, in the Web application, for example, Flask, is the concept of all the routing url ah, such as route("/login") such a way to write, the principle is to use a variety of decorators to realize the routing -> view mapping relationship.
On closer inspection, this whole process misses an important topic, namely namespaces, and variable scopes, or what namespaces look like.
LEGB Law
namespace (computing)
As explained in detail in the previous section, Python variables are essentially pointers, references to objects, and everything in Python is an object. The object is the memory address where the data is actually stored, and it's an instance of various classes (data types, data structures). (Variables are references to objects.) Something like that.
Most intuitive explanation.
" A namespace is a mapping from names to objects". (Mapping of variable names to objects)
"Most namespaces are currently implemented as Python dictionaries." (Most namespaces are implemented through dictionaries)
That is, namespaces are constraints used to avoid variable naming conflicts. Namespaces are independent of each other, there can be no renaming in one space, and there is no relationship between different spaces. It is the same logic as a computer system, storing files.
for i in range(10): print(i) # Both phrases use i but their respective spaces are different. # [i for i in range(100)]
- built-in names: Python built-in names, such as built-in functions, exception classes...
- Global names: constants, names defined in modules (classes, imported modules)...
- Enclosed: functions that may be nested within functions, etc...
- Local names: (local names): Names defined in functions (variables within functions) ...
Python finds variables in the following order: Local -> Enclosed -> Global -> Built-in.
Actually, in my personal experience, it's good to be able to distinguish between local and global relativity. It's good, basically. Intuitively, take a py file that writes code. At the outermost level, there are, variables, class definitions, function definitions, from ... import ... to ... import. import ... These are global variables, and the outermost class or function, inside, is its own namespace.
# var1 is global var1 = 666 def foo(): # var2 is localized var2 = 666 def foo2(): # Embedded local var3 = 666 # print(var2) print(var3) # G->L can't be found # # Finding var2 in foo2 is L->E is ok # exist foo center quest var2 be E->L be不行的
It's actually quite easy to understand. In the case of the above code, according to the L-E-G-B rule, it's enough to understand a relative.
Global vs Local
total = 0 # Global def sum(a, b): """Rewrite the built-in sum""" total = a + b print("Localized total:", total) sum(1, 1) print("global total:", total) # output localtotal: 2 security situationtotal: 0
As you can see, the local does not change the global, but in the local can get the global variable. Otherwise, how can the inner function get the parameters that the outer function receives from the closure? It's the outer function that "extends" the scope of the inner function, which is searched by the L->E->G->B rule.
global and nonlocal
name = "youge" def change_name(): name = "youyou" # Wish to change "youge" to "youyou" # change_name() print(name) # output youge
It's natural to find out that it doesn't work. This is natural, because, when calling a function, the name inside is a Local variable, which does not affect the global name, and if you want to change the global variable inside the function, you can just declare the variable with the global keyword.
name = "youge" def change_name(): global name name = "youyou" # Wish to change "youge" to "youyou" # change_name() print(name) # output youyou
It's easy to declare a variable inside a function as a global variable using global. Similarly, for ** function nesting, i.e., to closures, decorators, etc., the keyword nonlocal implements the declaration of a variable inside a function as an Enclose level outside the function**.
name = "jack" def outer(): name = "youge" # There is a local function space within the function def inner(): name = "youyou" print("local:", name) inner() # Trying to change the name of the nested layer print("encolse:", name) print("global:", name) outer() # output global: jack local: youyou encolse: youge
Now I want to change the E-level name in the, inner function (L-level), i.e., declare the name as nonlocal in inner.
name = "jack" def outer(): name = "youge" # There is a local function space within the function def inner(): nonlocal name name = "youyou" print("local:", name) inner() # Trying to change the name of the nested layer print("encolse:", name) print("global:", name) outer() # output global: jack local: youyou encolse: youyou
In the nested function scenario, the enclosed layer's name is changed by declaring name nonlocal at the local level. But declaring global at the local level doesn't have the same effect, why, uh... I don't know yet, it's an experiment, for now.
Oh, and suddenly I want to post one of the little mistakes I used to make when I was a rookie.
name = 'youge' def change_name(): name = name + "youyou" change_name() print(name) # output UnboundLocalError: local variable 'name' referenced before assignment
The reason for this is that the name is not defined in the function's internal space. In Python, the storage of procedures is accomplished by means of a recursive stack. Using the FILO, (first in, first out) feature of the stack, when a function is encountered, the stack is used to put the members of the function, in order, onto the stack, and if there is a return, it is placed as a stack element.
Variables have to be defined first, then used, and in Python, defined means that the variable refers to an instance, not a type declaration as in other languages. Oh, this is where it gets most confusing.
Modify name to be a global variable, passed as a function parameter.
# Option 1: Define a separate function to handle the name = 'youge' def change_name(s): name = s + "youyou" print(name) # Global variables are passed to the function space, i.e. "define first, execute later") change_name(name) # output yougeyouyou
# Option 2: Declare as global, not recommended. name = 'youge' def change_name(): global name name = name + "youyou" change_name() print(name) # output yougeyouyou
wrap-up
- Closures, Decorators are essentially nested functions, Parameters and functions can be passed because, Pyhton variables are essentially pointers.
- Namespaces are used in Python to resolve variable name conflicts, using the same logic as computer systems (such as Linux) that store files.
- The rules for finding variable names are Local -> Enclosed -> Global -> Built-in
- Personally, I think it's enough to understand the "relativity" between global and local, and to change the scope of variables between global and nonlocal (E-level).
- Variable scopes, always used, but often ignore it, here is a summary, often turn over, scopes, here it is.
This is the whole content of this article.