装饰器

装饰器

定义

装饰的工具

必须要遵循的原则:“开放封闭”

开放:对源函数功能的添加是开放的

封闭:对源函数功能修改是封闭的

装饰器的作用

在不修改被装饰对象源代码和调用方式的前提下,增加新功能

必须遵循的原则:

1、不修改被装饰对象的源代码

2、不修改被装饰对象的调用方式

为什么要使用装饰器

解决代码冗余问题,提高代码可扩展性

怎么使用装饰器

编写装饰器通过闭包函数实现

装饰器推导过程

# # 装饰器推导过程
def move():
    '''下载电影的源函数'''
    print('开始下载')
    time.sleep(2)
    print('下载完成')
def fun(index):
    def down():
        star_time = time.time()
        index() # 将被装饰对象move赋值给index执行
        end_time = time.time()
        print(f'总耗时{end_time - star_time}')
    return down
fun(move)()
# 有返回值时
def move():
    '''下载电影的源函数'''
    print('开始下载')
    time.sleep(2)
    print('下载完成')
    return '明日之巅.mp4'

def fun(index):
    def down():
        star_time = time.time()
        res = index()  # 将index执行结果赋值给res
        # print(res)
        end_time = time.time()
        print(f'总耗时{end_time - star_time}')
        return res  # 将index返回值返回出来
    # print(down())
    return down
move = fun(move)  # 将调用方式改成被装饰对象
move()
# 源函数有参数时
def move(url):
    '''下载电影的源函数'''
    print(f'{url}开始下载')
    time.sleep(2)
    print('下载完成')
    return '明日之巅.mp4'

def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print(f'总耗时:{end_time - start_time}')
        return res
    return inner
move = wrapper(move)
move(('https://www.cnblogs.com'))

装饰器最终模板

def wrapper(func):
    def inner(*args, **kwargs):
        # 调用前增加新功能
        res = func(*args, **kwargs)  # 调用被装饰对象,接收返回值
        # 调用后增加的新功能
        return res  # 接收被装饰对象的返回值
    return inner

装饰器语法糖

1、装饰器的语法糖是属于装饰器的

2、用@+装饰器名,在被装饰对象开头

3、在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上

# 增加统计代码运行时间的装饰器
def wrapper(func):
    def inner(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)
        end_time = time.time()
        print(end_time-start_time)
        return res
    return inner
# 使用语法糖
@wrapper
def movie():
    print("开始下载")
    time.sleep(2)
    print('下载结束')
# 不使用语法糖调用增加新功能后的函数
# movie = wrapper(movie)
# movie()
# 使用语法糖后可直接调用
movie()

叠加装饰器

在同一个被装饰对象中,添加多个装饰器,并执行

每一个新功能都写一个新的装饰器,否则会导致代码冗余,结构不清晰,可扩展性差

@装饰器1
@装饰器2
@装饰器3
def 被装饰对象():
	pass

注意:在调用被装饰对象时,才会执行添加的新功能

叠加装饰器装饰顺序,执行顺序:

装饰的顺序:由下往上装饰

执行的顺序:由上往下执行

注意:

1、装饰器内函数inner中出现任何判断,最后都要返回“调用后的被装饰对象”func(*args,**kwargs)

2、被装饰对象在调用时,如果还有其他装饰器,会先执行其他装饰器中的inner

def wrapper1(func1):
    def inner1(*args, **kwargs):
        print("1————start")
        res = func1(*args, **kwargs)
        print("1————end")
        return res
    return inner1

def wrapper2(func2):
    def inner2(*args, **kwargs):
        print("2————start")
        res = func2(*args, **kwargs)
        print("2————end")
        return res
    return inner2

def wrapper3(func3):
    def inner3(*args, **kwargs):
        print("3————start")
        res = func3(*args, **kwargs)
        print("3————end")
        return res
    return inner3

@wrapper1  
@wrapper2
@wrapper3
def index():
    print("index")
index()
>>>
# 执行顺序:由上而下
# 装饰顺序:由下而上
1————start
2————start
3————start
index
3————end
2————end
1————end

有参装饰器

无参装饰器:装饰在被装饰对象时,没有传参数的装饰器

# 以下是无参装饰器
@wrapper1  
@wrapper2
@wrapper3

有参装饰器:在某些时候我们需要给用户的权限进行分类

# 以下是有参装饰器
@wrapper1(参数1)  
@wrapper2(参数2)  
@wrapper3(参数3)

如果我们想提供多种不同的认证方式以供选择 , 函数inner需要一个driver参数,而函数wrapper与inner的参数都有其特定的功能,不能用来接受其他类别的参数,可以在wrapper的外部再包一层函数user_auth,用来专门接受额外的参数,这样便保证了在user_auth函数内无论多少层都可以引用到

# 给用户的权限进行分类
def user_auth(driver):
    def wrapper(func):
        def inner(*args, **kwargs):
            if driver == "svip":
                # 加入超级会员的功能
                res = func(*args, **kwargs)
                return res
            elif driver == "vip":
                # 加入会员的功能
                res = func(*args, **kwargs)
                return res
            else:
                # 加入普通用户的功能
                res = func(*args, **kwargs)
                return res
        return inner
    return wrapper

@user_auth(driver='svip')
def user_power():
    pass

user_power()

wraps

是一个修复工具,修复的是被装饰对象的空间

使用:

1、先从functools中调用wraps >>> from functools import wraps

2、装饰器在定义内层函数前加@wraps(func) 修改名称空间:将inner >>> func

3、inner前加@wraps(func)是将inner名称空间修改到func,如果不用@wraps(func)打印注释本质打印的是inner的注释,加上@wraps(func)修复被装饰对象的空间,打印注释就会打印被装饰对象的注释

# 函数对象.__doc__   是查看内部的注释
from functools import wraps
# 从functools中调用wraps模块
def wrapper(func):
    @wraps(func)  # 修改名称空间,inner >>>  func
    def inner(*args,**kwargs):
        '''
        装饰器的注释
        '''
        # 调用被装饰对象前加的功能
        res = func(*args,**kwargs)
        # 调用被装饰对象前加的功能
        return res
    return inner

@wrapper
def index():
    '''
    被装饰对象index的注释
    :return:
    '''
    pass
print(index.__doc__)
# 加上@wraps(func)
>>>
    被装饰对象index的注释
    :return:
# 不加@wraps(func)
>>>

        装饰器的注释
posted @ 2019-11-12 20:11  Mr沈  阅读(230)  评论(0编辑  收藏  举报