装饰器

装饰器

1.什么是装饰器
	是一个用来为被装饰对象添加功能的工具
    
    被装饰者:函数
    装饰器:可以用函数实现装饰器这种工具
2.为什么要用装饰器
    为了在不修改被装饰对象源代码以及调用方式的前提下
    为被装饰对象添加上新功能
3.如何用装饰器
    无参装饰器
   	有参装饰器
    开放封闭原则:
        开放:拓展功能是开放的,不修改源代码情况下加新功能
        封闭:对修改的源代码是封闭的

# 被装饰对象
import time

def foo(x):
    print(f'{x})
    time.sleep(3)

foo(11)
# 需求,得出上述对象打印时间
# 方案一:修改了源代码,不符合修饰器
import time

def foo(x):
    start = time.time()
    print(f'{x})
    time.sleep(3)
    print(time.time() - start)  

foo(11)
foo(22) 
foo(33)    
# 方案二:没有修改被装饰对象的源代码与调用方式,但是有代码冗余的问题
import time

def foo(x):
    print(f'{x})
    time.sleep(3)

start = time.time()        
foo(11)
print(time.time() - start)     

start = time.time()        
foo(22)
print(time.time() - start)   

start = time.time()         
foo(33)
print(time.time() - start)    
# 方案三:定义函数能解决冗余的问题,但wrapper函数需要外部传参,而且修改了调用方式,函数内部功能不灵活。不符合装饰器
import time

def foo(x):
    print(f'{x})
    time.sleep(3)

def wrapper():
    start = time.time
    foo(11)
    print(time.time = start)
wrapper() 
# 方案四:形参方式,wrapper可以传参,但还是修改了调用方式,内部还是写死了。
import time

def foo(x):
    print(f'{x})
    time.sleep(3)

def wrapper(func):
    start = time.time()
    func(11)
    print(time.time() - start)
wrapper(foo)  # 将foo内存地址传给func
# 方案五:闭包函数传参,调用方式形式上没有改变,但内部还是死的,不能灵活传参传值
import time

def foo(x):
    print(f'{x})
    time.sleep(3) 
          
def outter()
    func = foo
    def wrapper(func):
        start = time.time()
        func(11)
        print(time.time() - start)
    return wrapper  # 不能加括号,将函数wrapper返回,进行下一步赋值操作
foo = outter(foo)  # 此处的foo是一个变量,将wrapper的内存地址绑定给foo,即foo = wrapper的内存地址。形式上是foo原函数
foo()  
# 方案六:闭包函数传参,源代码未改变,调用方式形式上未改变,并利用*和**写活,可以传任何值,符合装饰器
import time

def foo(x):
    print(f'{x})
    time.sleep(3) 

def outter(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs):
        print(time.time() - start)
    return wrapper  # 不能加括号
          
foo = outter(foo)  # foo = wrapper的内存地址 
print(foo)  # <function outter.<locals>.wrapper at 0x00000258669C7550>
foo(11)  # 等价于 wrapper(11)
          
# 若被修饰对象有返回值
import time

def foo(x):
    print(f'{x})
    time.sleep(3) 
    return 666
          
def outter(func):   # func = 原foo = 地址1
    def wrapper(*args,**kwargs)  # wrapper = 地址2
        start = time.time()
        res = func(*args,**kwargs)  # 去除被修饰对象的返回值赋值给res
        print(time.time() - start)
        return res
    return wrapper #  不能加括号

foo = outter(foo) # 原foo解除地址1的绑定,新foo = outter(地址1) 原foo -> 指向地址2
res = foo(11)  # 相当于 res = wrapper(11)  
print res  # 666
# 把wrapper伪装的跟被装饰对象一样,更进一步
import time

def foo(x):
    """
    foo的提示信息
    """
    print(f'{x})
    time.sleep(3)
    return 996
     
def outter(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        print(time.time() - start)
        return res
    wrapper.__doc__ = foo.__doc__    # 将wrapper的提示信息变为foo的提示信息
    wrapper.__name__ = foo.__name__  # 将wrapper的名字变为foo的名字
    return wrapper
    
foo = outter(foo)
res = foo(11)
print(res)  # 996

装饰器语法糖:@

import time
from functools import wraps  # 内置函数工具说明的模块

def outter(func):
    @wraps(func)  # 相当于帮你运行了wrapper.__name__ = func.__name__等一系列伪装
    def wrapper(*args,**kwags)
        start = time.time()
        res = func(*args,**kwargs)
        print(time.time() - start)
        return res
    return wrapper

@outter  # "outter",名称由定义函数名决定,不是固定名称,作用就是foo = outter(foo),帮你传递了内存地址。形式 @+函数名称,写在被修饰对象的上面
def foo(x)
    """
    foo 的提示信息
    """
    print(f'{x}')
    time.sleep(3)
    return 666

res = foo(22)
print(res)  

help(foo)
print(foo)

输出结果:
# 22
# 3.007246971130371
# 66

# Help on function foo in module __main__:

# foo(x)
#     foo 的提示信息

# <function foo at 0x00000266A2A470D0>

无参装饰器模板

def outter(func):
    def wrapper(*args,**kwargs)
        res = func(*args,**kwargs)
        return res
    return wrapper

@outter
def foo():  # 无参
    pass

foo()
    

叠加多个装饰器

# 例1
def deco1(func1):  # func1 = wrapper2的内存地址
    def wrapper1(*args, **kwargs):
        print('--->运行wrapeer1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def deco1(func2):  # func2 = wrapper3的内存地址
    def wrapper2(*args, **kwargs):
        print('--->运行wrapeer2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def deco3(func1):  # func3 = index的内存地址
    def wrapper3(*args, **kwargs):
        print('--->运行wrapeer3')
        res1 = func1(*args, **kwargs)
        return res3
    return wrapper3

"""装饰器加载自下而上,运行自上而下"""
        # index = wrapper1的内存地址                         
@deco1  # deco1(wrapper2的内存地址)  -->  返回wrapper1的内存地址
@deco2  # deco2(wrapper3的内存地址)  -->  返回wrapper2的内存地址
@deco3  # deco3(index的内存地址)     -->  返回wrapper3的内存地址
def index():
    print('from index')
    return 666

res = index() # wrapper1()
print(res)

# 例2
import time

def deco1(dunc1):
    def wrapper(*args,**kwargs):
        # print('Begin')
        start = time.time()
        res1 = func(*args,**kwargs)
        print(time.timr() - start)
        return res1
    return wrapper1

def deco2(func2):
    def wrapper(*args,**kwargs):
        name = input('username>>>:').strip()
        pwd = input('password>>>:').strip()
        if name == 'richer' and pwd == '996'
           print('Login successfully')
           res2 = func2(*args,**kwargs)
           return res2
        else:
           print('Logon failed')
    return wrapper2
        # func1 = wrapper1的内存地址
@deco1  # deco(wrapper2的内存地址)  --> 返回wrapper1的内存地址
@deco2  # deco(index的内存地址)     --> 返回wrapper2的内存地址
def index():
    print('Index function')
    time.sleep(3)
    
index()

# 输出结果:
    # Begin  
    username>>>: richer
    password>>>: 996
    Login successfully
    index function
    16.39341425895691 
# 位置交换
@deco2
@deco1
def index():
    print('Index function')
    time.sleep(3)
    
index()
# 输出结果:
	username>>>: richer
	password>>>: 996
	Login successfully
	# Begin
	index function
	1.0014164447784424
 

有参装饰器

# 其实就是叠加多层函数,最好三层。内部函数需要传参,但参数被占据,只能在外层加,*与**不能动,在不影响语法糖使用的情况下,利用有参装饰器
def outter(engine):
    def deco(func):
        def wrapper(*args,**kwargs)
            if engine == 'file'
                name = input('username>>>:').strip
                pwd = input('password>>>:').strip
                if name == 'richer' and pwd == '996':
                    print('Login successfully')
                    res = func(*args,**kwargs)
                    return res
                else:
                    print('Login failed')
            elif engine == 'mysql'
                print('MySQL-based authentication')
            elif engine == 'ldap'
                print('ldap-based authentication')
            
        return wrapper
    return deco

@outter(engine = 'mysql')  # @ 后面只能跟一个内存地址,且只能帮你传一个参数
def index():
    print('index function')
    
# index = deco(index,engine='mysql')  # 在不使用语法糖的情况下使用该种方法,可以两层函数即可,及在定义deco处加形参engine:def deco(func,engine)
index()  # 相当于 wrapper()


结论:有参装饰器模板

def outter(x, y):
    def deco(func):
        def wrapper(*args,**args):
            print(x, y)
            res = func(*args,kwargs)
        return res
    return deco

@outter(11,22)
def index():
    pass
index()
            

posted @ 2021-07-01 20:21  Richer-J  阅读(85)  评论(0)    收藏  举报