代码改变世界

装饰器

2019-08-14 17:25  美丽的名字  阅读(86)  评论(0)    收藏  举报

装饰器

定义:本质就是函数,从来未其他函数增加附件功能。

原则:不修改被修饰函数的源代码,不修改被修饰函数的调用方式

 

装饰器 = 高阶函数 + 函数嵌套 + 闭包


 

高阶函数:

  - 函数接收的参数是一个函数名

  - 函数的返回值是一个函数名

  - 满足上述条件中的任一个都可称之为高阶函数


 

函数嵌套:

  - 在函数中定义另一个函数,并进行调用

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

注:print(locals())获取当前的环境变量,当前没有定义该环境变量,会逐层网上找,直到找到为止,否则报错。


 

闭包:

#内部函数包含对外部作用域而非全局作用域的引用

 

def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素

装饰器样例一:

import time


def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print("time period: %s" % (stop_time - start_time))
        return res
    return wrapper

@timer
def test():
    time.sleep(2)
    print("running in test....")
    return "return from test"

test()

有参装饰器样例二:

def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')

装饰器的副作用:

Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring

没有加wraps的情况:

#coding=utf-8
# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

执行结果:

('wrapper', 'decorator')

 

添加wraps后的情况:

#coding=utf-8
# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

执行结果:

('example', 'Docstring')


 

多个装饰器叠加:

# 叠加多个装饰器
# 1. 加载顺序(outter函数的调用顺序):自下而上
# 2. 执行顺序(wrapper函数的执行顺序):自上而下
def outter1(func1): #func1=wrapper2的内存地址
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def outter2(func2): #func2=wrapper3的内存地址
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3=最原始的那个index的内存地址
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3



@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
    print('from index')

print('======================================================')
index()

示范代码