Summary of Python Decorator

Keywords: Python Programming

Let's start with a few definitions:

1. Function

In python, functions are defined by def keywords, function names, and optional parameter lists. Returns the value through the return keyword. Let's give an example of how to define and invoke a simple function:

 

#coding:UTF8

def foo():
     return 1
print foo()

1

Method bodies (of course, multiple lines are the same) are required, and functions can be invoked by indentation, followed by double parentheses ().

2. Scope

In Python, functions create a new scope. Python developers may say that functions have their own namespace. This means that when they encounter a variable inside a function, the function will find it in its own namespace first. Here are some simple examples to illustrate local scope and global scope.

 

#coding:UTF8

a_string = "This is a global variable"
def foo():
     print locals()
print globals() # doctest: +ELLIPSIS

foo()  #2


{'foo': <function foo at 0x00000000026ECF98>, ...., 'a_string': 'This is a global variable',...}
{}

 

The built-in function globals returns a field (omitted part) containing the names of variables known to all Python interpreters. In # 2, the function foo is called to print out the contents of the local scope on the back of the function. You can see that the function foo has its own separate namespace, even if there is nothing in the temporary namespace.

3. Variable Analysis Rules

Of course, it's not that you can't access outside global variables in a function. In Python's scope rules, creating variables will create a variable in the current scope, but accessing or modifying variables will now look for variables in the current scope, and finding no matching variables will go up in turn in the closed scope. so, for example, modifying function foo is to achieve printing global. Scope variables are also possible

#coding:UTF8

a_string = "This is a global variable"
def foo():
     print a_string   #1

foo()

This is a global variable

At # 1, the Python interpreter will try to find the variable a_string, which is not found in the local scope of the function, so will then look in the upper scope.
On the other hand, if we assign global variables to the inner part of the function, the result will be different.

 

#coding:UTF8

a_string = "This is a global variable"
def foo():
    a_string='Test'  #1
    print locals()
    
foo()

{'a_string': 'Test'}

print a_string  #2

This is a global variable

 

Global variables can be accessed (if they are variable data types (such as list,dict) or even can be changed), but assignment is not possible. In #1 inside the function, a new local variable is actually created to hide the same name variable in the global scope. This conclusion can be drawn by printing out the contents of the global namespace. Also, a_string printed at #2 remains unchanged.

4. Variable Life Cycle

It is worth noting that variables not only exist in one namespace, but also have their own lifecycle, as follows:

 

#coding:UTF8

def foo():
     x = 1
foo()
print x # 1

NameError: name 'x' is not defined

 

# The error in one place is not only caused by scope rules, but also related to the mechanism of function call implementation in Python and many other programming languages. There is no valid syntax to get the value of variable x at the execution time point here, because there is no root, and the namespace of function foo begins and ends with the function call.

5. Functional parameters

Python allows you to think of functions passing parameters, which become local variables and functions inside.

#coding:UTF8
def foo(x):
     print locals()
foo(1)

{'x': 1}

There are many ways to define and pass parameters in Python. Here's a brief description: Function parameters are required location parameters or optional naming, default parameters

#coding:UTF8
def foo(x, y=0): # 1
     return x - y
 
print foo(3, 1) # 2
2
print foo(3) # 3
3
print foo() # 4
TypeError: foo() takes at least 1 argument (0 given)
print foo(y=1, x=3) # 5
2

  

Function foo is defined at # 1. A location parameter x and a named parameter y call functions in # 2 in a conventional way. Even if there is only one named parameter, the parameters can still be passed to the function through the location parameter. When calling a function, the named parameter y can be completely ignored as shown in # 3. If the named parameter does not receive any value, Python will do so. Activate the default value of the declaration. But you cannot omit the first location parameter x, otherwise you will make an error like # 4.

python supports named parameters for function calls. Look at the function call at # 5, passing two named arguments. At this time, because of the name identification, the order of parameter passing is not concerned.

Of course, the opposite is true: the second parameter of a function is y, but the value is passed to it by position. The function at # 2 calls foo(3,1). We pass 3 to the first parameter and 1 to the second parameter, although the second parameter is a named parameter.

6. Nested functions

Python allows the creation of nested functions, which means that functions can be defined in functions and that existing scopes and variable lifetimes still apply.

#coding:UTF8
def outer():
     x = 1
     def inner():
         print x # 1
     inner() # 2
 
outer()


1

The Python interpreter needs to find a local variable called x, which will continue to look up the upper scope after failure. This upper scope is defined in another function. For function outer, the variable x is a local variable.
Function inner can access enclosed scope. At # 2, function inner can be called. Inner is only a variable name that follows Python variable parsing rules. Python interpreter will first find matching variables for variable name inner in the scope of outer.

7. Functions are first-class objects in the Python world

In Python, functions are objects like everything else.

#coding:UTF8
print issubclass(int, object) # all objects in Python inherit from a common baseclass
True
def foo():
     pass
print foo.__class__ # 1
<type 'function'>
print issubclass(foo.__class__, object)
True

  

Functions are objects in Python. Like others, in Python, functions are just ordinary values. That is to say, functions are passed as parameters to other functions or returned from functions, such as:

#coding:UTF8
def add(x, y):
     return x + y
def sub(x, y):
     return x - y
def apply(func, x, y): # 1
     return func(x, y) # 2
print apply(add, 2, 1) # 3
3
print apply(sub, 2, 1)
1

  

At # 1, you can see that the function is ready to receive a variable of a function, which is just a common variable. Like other variables, you can call the incoming function at # 2: "() Represents the operation of the calling function and the calling variable contains a quota value. At # 3, you can see that the transfer function has no special use." The name of the function is just the same identifier as other variables.

Python converts frequently used operations into functions to be used as parameters, passing a function to the key parameter of the built-in sorting function to customize the sorting rules.

#coding:UTF8
def outer():
     def inner():
         print "Inside inner"
     return inner # 1
 
foo = outer() #2
print foo 
<function inner at 0x000000000269C048>

foo()
Inside inner

At # 1, the variable inner, which happens to be the function identifier, is returned as the return value. "Return the function inner, otherwise it will not be called at all." Every time the function outer is called, the function inner will be redefined. If it is not treated as the variable return amount, it will no longer exist after each execution.

Capture the return value at # 2, function inner, and store it in a new variable foo. When evaluating foo, determine the containing function inner, and be able to call it.

8. Closure

#coding:UTF8

def outer():
     x = 1
     def inner():
         print x # 1
     return inner
foo = outer()
print foo.func_closure

(<cell at 0x00000000026861F8: int object at 0x0000000001E279A8>,)

 

X is a local variable in outer. When function inner prints x at # 1, the Python interpreter finds the corresponding variable inside inner, which is not found in fact, and then finds the matching in the closed scope.

From the point of view of the life cycle of variables, variable x is a local variable of function outer, which means that only when function outer is running can it exist. According to Python operation mode, it is impossible to call function inner after function outer returns. When function inner calls, variable x no longer exists, and a runtime error may occur.
But the returned function inner can continue to work. Python supports a feature called function closure. Nested functions defined in a non-global scope can remember the closed namespace where they were defined. This can be concluded by looking at the func_closure property of the function, which contains values in the closed scope (only captured values). For example, x, if there are other values defined in the outer, there will be no values in the enclosed scope.

Every time a function outer is called, the function inner is redefined. Now the value of variable x does not change, so the function inner returned is the same logic every time.
With a slight change:

#coding:UTF8

def outer(x):
     def inner():
         print x # 1
     return inner
print1 = outer(1)
print2 = outer(2)

print1()
1
print2()
2

You can see that closures -- enclosed scopes remembered by functions -- can be used to create custom functions, which are essentially hard-coded parameters. In fact, they do not transfer parameters 1 or 2 to function inner, but rather create custom versions that print various numbers.

Closures come out alone as a very powerful function. In some ways, outer is like a constructor for inner servers, and x is like a private variable.

9. Decorators

The decorator is actually a closure that takes a function as a parameter and returns an alternate version of the parameter.

#coding:UTF8

def outer(func):
     def inner():
         print "before func"
         ret = func() # 1
         return ret + 1
     return inner
def foo():
     return 1
decorated = outer(foo) # 2
print decorated()

before func
2

  

Define a function outer, which has only one func parameter. In its definition of nested function inner,inner prints a string of strings, then calls func to get the return value in # 1. The func value may be different each time the outer calls, but it will be called no matter how it is used. Finally, inner returns the value of func()+1, which can be seen printed by calling the function stored in decorated at #2. String and return value 2, not expected return value 1 by calling function foo.


You can think of decorated variables as a decorative version of function foo, an enhanced version. In fact, if you want to write a useful decorator, you may want to replace the original function foo with the decorated version completely, so you will always get our "enhanced version" foo. To achieve this effect, you don't need to learn new grammar at all. Simply assign the variable foo:

foo = outer(foo)

Now, any call will not involve the original function foo, will get a new decorative version of foo, now to write a useful decoration

#coding:UTF8

import time
def bar():
    time.sleep(2)
    print('in the bar')
def test2(func):
    print(func)
    return func

# print(test2(bar))
bar=test2(bar)
bar()  #run bar

<function bar at 0x00000000026BCF98>
in the bar

  

10. Use @ identifiers to apply decorators to functions and use * args and **kwargs

Python 2.4 supports the use of identifier @ to apply decorators to functions, just by adding @ and the name of the decorator before the definition of the function. In the example in the previous section, we replaced the original method with the decorative method:

bar=test2(bar)

This method can pack any method at any time. But if you customize a method, you can decorate it with @

 1 #coding:UTF8
 2 
 3 import time
 4 
 5 def test2(func):
 6     print(func)
 7     return func
 8 @test2
 9 def bar():
10     time.sleep(2)
11     print('in the bar')
12 
13 bar()  #run bar

 

 1 #coding:UTF8
 2 
 3 import time
 4 def timer(func): #timer(test1)  func=test1
 5     def deco(*args,**kwargs):
 6         start_time=time.time()
 7         func(*args,**kwargs)   #run test1()
 8         stop_time = time.time()
 9         print("the func run time  is %s" %(stop_time-start_time))
10     return deco
11 @timer  #test1=timer(test1)
12 def test1():
13     time.sleep(1)
14     print('in the test1')
15 
16 @timer # test2 = timer(test2)  = deco  test2(name) =deco(name)
17 def test2(name,age):
18     print("test2:",name,age)
19 
20 test1()
21 test2("Tom",22)
22 
23 
24 in the test1
25 the func run time  is 1.05200004578
26 ('test2:', 'Tom', 22)
27 the func run time  is 0.0

 

Here's an advanced version of the decorator:

 1 #coding:utf8
 2 import time
 3 user,passwd = 'hbert','abc'
 4 def auth(auth_type):
 5     print("auth func:",auth_type)
 6     def outer_wrapper(func):
 7         def wrapper(*args, **kwargs):
 8             #print("wrapper func args:", *args, **kwargs)
 9             if auth_type == "local":
10                 username = raw_input("Username:").strip()
11                 password = raw_input("Password:").strip()
12                 if user == username and passwd == password:
13                     print("\033[32;1mUser has passed authentication\033[0m")
14                     res = func(*args, **kwargs)  # from home
15                     print("---after authenticaion ")
16                     return res
17                 else:
18                     exit("\033[31;1mInvalid username or password\033[0m")
19             elif auth_type == "ldap":
20                 print("Making wool ldap,Can't....")
21 
22         return wrapper
23     return outer_wrapper
24 
25 def index():
26     print("welcome to index page")
27 @auth(auth_type="local") # home = wrapper()
28 def home():
29     print("welcome to home  page")
30     return "from home"
31 
32 @auth(auth_type="ldap")
33 def bbs():
34     print("welcome to bbs  page")
35 
36 index()
37 print(home()) #wrapper()
38 bbs()

Posted by ozPATT on Fri, 14 Jun 2019 21:00:19 -0700