014.Python之装饰器

一、什么是装饰器

装饰:指的是为被装饰对象添加额外的功能。

器:指的是工具/功能。

装饰器本质就是一个函数,可以为其他函数添加功能的函数。

二、为何要用装饰器

程序的开发应该遵循开放封闭的原则,开放,指的是对拓展功能开放,封闭,指的是对修改源代码封闭。

为了实现这个开放封闭的原则,我们就需要使用装饰器。因为装饰器它可以在不修改被装饰对象的源代码以及调用方式的前提下,为被装饰对象添加上新的功能。

三、如何使用装饰器

(一)无参装饰器的实现

从定义函数开始一步步推进,逐步实现一个可以记录函数运行时间功能的无参装饰器。

1.装饰器初探

# 定义函数
import time

def index(x, y):
    time.sleep(2)
    print("This is index===>",x,y)

index(1,2)  # This is index===> 1 2

def home(name):
    time.sleep(1)
    print("This is page of ==>",name)

home("zhangsan")  # This is page of ==> zhangsan
# 实现一个简单的装饰器
# 1) 首先要实现最基本的功能记录时间的功能
# 2) 然后,通过一个闭包函数,实现调用外层函数outter(),返回内层函数wrapper的内存地址
# 3) 把outter()赋值给index,这个index就相当于outter函数的返回值wrapper
# 4) 给index加括号之后,可以触发wrapper函数运行,当wrapper这个局部函数运行时,会寻找它自己的内层函数需要的变量x,
# 5) wrapper函数在定义的时候,确定了参数x是外层变量,所以去外层空间寻找,找到变量值index,由于在调用wrapper的时候,已经传了index需要的两个参数,所以index函数触发调用,
# 6) index层层向上寻找,在全局作用域找到index函数,运行对用的功能,结合wrapper增加的功能,得出了我们需要的结果。但是,这里有一个问题还没有解决,就是这个工具,是针对index定制的,不能用于home等其他函数,需要进行改进。

def outter():
    x = index

    def wrapper(a,b):
        start_time = time.time()  
        x(a,b)
        stop_time = time.time()
        print("run time is :%s" %(stop_time-start_time))

    return wrapper  # 不能加括号,否则会触发函数运行!

index = outter()

index(1,2)  # This is index===> 1 2
            # run time is :2.000650405883789

2.装饰器改进1

# 装饰器改进1
# 1) 因为初步版本的装饰器,受限于传参个数问题,以及无法将函数调用给其他函数使用,所以做了如下改进:
# 2) 通过使用*与**将wrapper函数的形式参数变为可变长度的参数,
# 3) 再把wrapper函数内部的函数x的实际参数变为可变长度参数,这样就实现了,wrapper接收的任何函数,都可以原样传给目标函数x,
# 4) 在wrapper函数的外层,将写死的x=函数名去掉,使用外层函数outter进行接收参数:目标函数名,
# 5) 至此,实现了使用index/home 赋值给outter(index/home),即index/home等同于outter函数的返回值wrapper,加上括号传参后触发运行,由于后面有括号,且可以传任意参数,wrapper函数开始运行,寻找变量x,找到外层函数outter传入的参数,继续运行,最终得出我们想要的结果。
# 6) 但是,仍然还是有问题,装饰后的函数,无法给出与原函数一样的返回值,目前的函数返回值为None,需要继续改进。
import time

def index(x, y):
    time.sleep(2)
    print("This is index===>",x,y)
	return "index的返回值"

def home(name):
    time.sleep(1)
    print("This is page of ==>",name)
    return "home的返回值"

def outter(x):
    # x = home
    def wrapper(*args,**kwargs):
        start_time = time.time()
        x(*args,**kwargs)
        stop_time = time.time()
        print("run time is :%s" %(stop_time-start_time))
    return wrapper

index = outter(index)
home = outter(home)

index(1,2)  # wrapper(1, 2)
            # This is index===> 1 2
            # run time is :2.000706195831299
home("zhangsan")    # wrapper("zhangsan")
                    # This is page of ==> zhangsan
                    # run time is :1.0000085830688477

3.装饰器改进2

# 装饰器改进2
# 1) 初步改进版的装饰器函数,无法给出与原函数一样的返回值,所以做了如下改进:
# 2) 先给我们wrapper内的函数赋值,
# 3) 再给wrapper函数一个返回值,就是我们赋值的的那个变量,
# 4) 至此,我们实现了使用outter()给我们想要的函数进行装饰,增加装饰功能的后,不影响原函数的功能,不修改原函数的代码,不改变原函数的调用方式,并且可以输出原函数的结果,以及得出与原函数相同的返回值
# 5) 虽然如次使用在功能上没有问题,但是,每次使用都要先使用与目标函数相同的函数名,去给装饰器函数去赋值并传参目标函数名,然后才可以像装饰目标函数一样调用,显得有些麻烦。

import time

def index(x, y):
    time.sleep(2)
    print("This is index===>",x,y)
    return "index的返回值"

def home(name):
    time.sleep(1)
    print("This is page of ==>",name)
    return "home的返回值"

def outter(x):
    # x = home
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = x(*args,**kwargs)
        stop_time = time.time()
        print("run time is :%s" %(stop_time-start_time))
        return res
    return wrapper

index = outter(index)  # index=wrapper
home = outter(home)  # home=wrapper

res1 = index(1,2)   # wrapper(1, 2)
                    # This is index===> 1 2
                    # run time is :2.000706195831299
print("====>",res1) # ====> index的返回值

res2 = home("zhangsan")     # wrapper("zhangsan")
                            # This is page of ==> zhangsan
                            # run time is :1.0000085830688477
print("====>",res2)         # ====> home的返回值

4.语法糖的使用

# 装饰器改进3  使用语法糖
# 1) 使用语法糖,可以达成如下效果:
# 2) 省略了使用与被装饰函数相同的变量名,给外层函数赋值的过程,这个过程是实现装饰器的关键,即index = outter(index)——>index=wrapper
# 3) 
import time

def outter(x):
    # x = home
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = x(*args,**kwargs)
        stop_time = time.time()
        print("run time is :%s" %(stop_time-start_time))
        return res
    return wrapper  # 千万不能加括号,否则会触发运行

@outter  # 替代了  # index = outter(index)  # index=wrapper
def index(x, y):
    time.sleep(2)
    print("This is index===>",x,y)
    return "index的返回值"

@outter  # 替代了  # home = outter(home)  # home=wrapper
def home(name):
    time.sleep(1)
    print("This is page of ==>",name)
    return "home的返回值"

res1 = index(1,2)   # wrapper(1, 2)
                    # This is index===> 1 2
                    # run time is :2.000706195831299
print("====>",res1) # ====> index的返回值

res2 = home("zhangsan")     # wrapper("zhangsan")
                            # This is page of ==> zhangsan
                            # run time is :1.0000085830688477
print("====>",res2)         # ====> home的返回值

5.装饰器的一般模板

# 目前阶段的装饰器,可以形成一个模板,给函数提供装饰功能
def outter(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

# 装饰器:计算函数运行时间
def timmer(func):
    def wrapper(*args, **kwargs):
        start=time.time()
        res = func(*args, **kwargs)
        stop=time.time()
        print(stop -start)
        return res
    return wrapper

# 装饰器:函数登陆认证后可以使用功能
def auth(func):
    def wrapper(*args,**kwargs):
        name = input("请输入用户名>>>: ").strip()
        pwd = input("请输入您的密码>>>:").strip()
        if name == "egon" and pwd == "123":
            print("用户登录成功")
            res = func(*args,**kwargs)
            return res
        else:
            print("账户名或密码错误")
    return wrapper

@auth  # 替代了  # index = outter(index)  # index=wrapper
def index(x, y):
    time.sleep(2)
    print("This is index===>",x,y)
    return "index的返回值"

@auth  # 替代了  # home = outter(home)  # home=wrapper
def home(name):
    time.sleep(1)
    print("This is page of ==>",name)
    return "home的返回值"

6.wraps装饰器的使用

# 为了让装饰器更加完美,跟原函数相比更加相似,我们可以对装饰器进一步优化,使用wraps装饰器来装饰我们的wrapper函数,在装饰后的函数值相同的情况下,进一步,让被装饰后的函数的文档注释等等细节属性,跟原函数保持一致,让被装饰器装饰的函数,与原函数无限接近。

from functools import wraps

def timmer(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res
    return wrapper
@timmer # index = timmer(index) # index = wrapper
def index(x, y):
    """
    这是index的文档注释
    """
    time.sleep(1)
    print("index===>>>",x,y)
    return "index的返回值"

index(1,2)   # index===>>> 1 2
             # 1.0009651184082031
print(index.__name__)  # index
help(index)   # 查看函数的文档注释
			  # Help on function index in module __main__:

              # index(x, y)
              #     这是index的文档注释

7.完整的无参装饰器模板

# 完整的无参装饰器模板
from functools import wraps

def outter(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper
原文地址:https://www.cnblogs.com/huluhuluwa/p/13160141.html