Closure and Decorator Foundation

Keywords: Python

closure

Definition

  • Closed is a closed (function in a function) and a package is a inclusion (the internal function refers to variables in the scope of an external function rather than in the global scope).

  • What is closure

    • The Reference of Internal Functions to Variables in the Scope of External Functions
    • The attributes in a function have a life cycle (during the execution of the function)
    • Closure functions within closures privatize variables and complete data encapsulation, similar to object-oriented

Introduce

def foo():
    print("in foo()")

    def bar():
        print("in bar()")
#1. Running internal functions directly to report errors
#bar()
#2. Consider running external functions first and then internal functions, which will still report errors, and the resources will be released after the external functions are finished running.
#foo()
#bar()

Because of scoping problems, attributes within a function have a lifecycle, only during the execution of the function.

Considering this code again, only if foo() is called, print() and bar() inside can survive.

def foo():
    print("in foo()")
    def bar():
        print("in bar()")
    return bar  #Returns the body of the bar function as a return value
var = foo() #At this point var is equivalent to bar
var()		#Executing var() is equivalent to executing bar()
#Result
in foo()
in bar()
-------------------------------------------------------
# As mentioned earlier, the reference of internal functions to the scope variables of external functions
def foo():
    a = 66
    print("in foo()")

    def bar(num):
        print("in bar()")
        print(a + num)

    return bar

var = foo() #At this point, VaR is var.
var(22)		#Equivalent to direct execution of bar(20)
print(a)
#Result
in foo()
Traceback (most recent call last):
in bar()
88
    print(a)  #Departing from the body of the function, A is a local variable, reporting errors
NameError: name 'a' is not defined

When the bar() function can be operated continuously

li = [1, 2, 3, 4, 5]
def foo(obj):
    print("foo:", obj)
    def bar():
        obj[0] += 1
        print("bar:", obj)
    return bar
var = foo(li)
var()
var()
#Result
foo: [1, 2, 3, 4, 5] #Execute foo(li) directly and return bar to var
bar: [2, 2, 3, 4, 5] #For the first operation, obj[0]=1+1=2
bar: [3, 2, 3, 4, 5] #The second operation, obj[0]=2+1=3

Decorator

Introduce

Sometimes it is necessary to extend the function without affecting the original function.

Example

Need to calculate the running time of each program

import time
def counts():
    s=0
    for i in range(100001):
        s+=i
    print('sum:%d'%s)
start =time.time()
counts()
end=time.time()
print('execution time:',(end-start))

sum:5000050000
//execution time: 0.008973836898803711

Successful implementation time calculation, but if there are thousands of functions, how to write each function is very troublesome.
The amount of code will also increase a lot from scratch.

def use_time(func):
    start = time.time()
    func()
    end = time.time()
    print('execution time:',(end-start))

After modification, a function is defined to realize the function of time calculation, which simplifies the previous operation, but when used, it is still necessary to pass the function into the function of time calculation.

def  count_time(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        print('execution time:', (end - start))
    return wrapper
a=count_time(counts)
a()

With closures, when used, the counts function is redirected to the function reference returned by the count_time function. In this way, the counts function is used in the same way as before.

import time
def  count_time(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        print('execution time:', (end - start))
    return wrapper

@count_time
def counts():
    s=0
    for i in range(1000001):
        s+=i
    print ('sum:%d'%s)

counts()

sum:500000500000
//execution time: 0.08475852012634277

Using the decorator, when counts is called again, count_time is called and returned to it

When used, the counts function is redirected to the function call returned by the count_time function. Only counts are used in the same way as before.

Realization is to adopt decorative devices

Several Forms of Decorator

1. No parameter, no return value
def setFunc(func):
    def wrapper():
        print('start')
        func()
        print('end')
    return wrapper

@setFunc
def show ():
    print('in show')
show()
##Result:
start
in show
end
2. No parameter with return value
def setFunc(func):
    def wrapper():
        print('start')
        func() #At this point func() is not received by any other and is released directly. If return ed here, the end below will not print.   
        print('end')
        return func()#Return func(), the above func() is useless and can be omitted directly.
    return wrapper

@setFunc
def show ():
    return  'in show'
print(show())
start
end
in show  #None if there is no return func
3. There are parameters and return values
def setFunc(func):
    def wrapper(s):
        print('start')
        func(s)
        print('end')
        return func('jc') #Execute func(jc) and return None
    return wrapper

@setFunc
def show (s):
    print('hello %s'%s)
show('Bob')
#Result
start
hello Bob
end
hello jc
4. There are parameters and return values
def setFunc(func):
    def wrapper(a,b):
        print('start')
        print('end')
        return func(a,b)
    return wrapper
@setFunc
def add(x,y):
    return x+y
print(add(5,6))
#Result
start
end
11
5.Universal Decorator

According to the different definitions of the decorated function, four forms are distinguished.

Define Universal Decorator to satisfy any form of function definition

def setFunc(func):
    def wrapper(*args,**kwargs): #Variable parameters, receiving different parameter types
        print('wrapper context.')
        return func(*args,**kwargs)
    return wrapper
@setFunc
def func(name,age,job='student'):
    print(name,age,job)
func('bob',17)
@setFunc
def stp(a,b,*c,**d):
    print(a,b)
    print(c)
    print(d)
stp('cy','clg',1991,11,11,tk='ul')
##Result:
wrapper context.
bob 17 student
wrapper context.
cy clg
(1991, 11, 11)
{'tk': 'ul'}

Functions are decorated with multiple decorators

When a function is used, it may not meet expectations by extending it through a decorator.

A function is decorated with multiple decorators.

def setFunc1(func):
    def wrapper1(*args,**kwargs):
        print('Wrapper Context 1 start.'.center(42,'-'))
        func(*args,**kwargs)
        print('Wrapper Context 1 end.'.center(42,'-'))
    return wrapper1
def setFunc2 (func):
    def wrapper2(*args, **kwargs):
        print('Wrapper Context 2 start.'.center(42, '-'))
        func(*args, **kwargs)
        print('Wrapper Context 2 end.'.center(42, '-'))
    return wrapper2

@setFunc2   #F
@setFunc1   #g

def show(*args,**kwargs):  #f
    print('show R.'.center(42))
show()  #F(g(f))
#Result:
---------Wrapper Context 2 start.---------
---------Wrapper Context 1 start.---------
                 show R.                  
----------Wrapper Context 1 end.----------
----------Wrapper Context 2 end.----------
  • Procedure statement run order: bottom-up modification. First decorate with F1, then take the result as a whole, then decorate with F2.

  • Function calls: from the inside out. Similar to composite function

summary

The advantage of this implementation is that the closure function is defined. Just add @func to the function you want to decorate through the @func decorator grammar.

Users don't need to know that they are decorated when they use it. You just need to know what the original function is.

This form of function expansion, which does not change the original function, is called decorator.

When @func is executed, the original function is actually passed into the closure, and the reference to the original function points to the reference to the decorated inner function returned by the closure.

Review and summary

  • Functions can be passed as parameters or return values of functions, just like ordinary variables.
  • The interior of a function can define another function. Aim To realize the function of hidden function.
  • Closure is also a defining form of function
  • The rules of closure definition, then the external function defines an internal function. The internal function uses the variables of the external function and returns the reference of the internal function.
  • In python, decorators are implemented through closures
  • The function of decorator is to add function to function without changing original function.
  • The use of decorators to decorate existing functions in the form of @decorator function names and add functions
  • Decorators can be divided into four categories according to their parameters and return values. Universal Decorators are implemented by variable parameters (* args,**kwargs).
  • A decorator can provide decorative functions for multiple functions, and a function can also use multiple decorators.
  • Decorators can be implemented by classes: rewriting _init_ and _call_ functions
  • Class Decorator After Decorative Function, the original reference is no longer a function, but the object of Decorator.

Posted by angelkay73 on Sat, 03 Aug 2019 00:45:05 -0700