一、装饰器简易版本

def outer(func_name):
    # func_name = index
    def get_time():
        # 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        func_name()
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
    return get_time
index=outer(index) # get_time的内存地址
index()

二、装饰器解决参数问题

def outer(func_name):
    # func_name = index
    def get_time(*args, **kwargs):
        # 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        func_name(*args, **kwargs)
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
    return get_time
index = outer(index)  # get_time的内存地址
index()
# login=outer(login)
login('jack')

三、解决返回值问题

def outer(func_name):
    # func_name = index
    def get_time(*args, **kwargs):
        # 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        res=func_name(*args, **kwargs)  # index()
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
        return res  # 才是真正的函数的返回结果
    return get_time
# index = outer(index)  # get_time的内存地址
# res=index()
# print(res)  # None

home=outer(home)
print(home())

四、课堂练习题

# 写一个认证装饰器
# 定义一个index函数,让用户输入用户名和密码,如果输入正确,就执行index函数,否则不能执行函数

'''升华:
    如果有一个函数被认证成功,后续的函数都不在认证了
'''
# 定义一个变量来存储是否认证成功
is_login={'is_login':False}

def login_auth(func_name):
    # func_name = index
    def auth():
        if is_login.get('is_login'):
            res = func_name()
            return res
        # 1. 让用户输入用户名和密码
        username = input('username:').strip()
        password = input('password:').strip()

        # 2. 要认证,判断用户名和密码是否正确
        if username == 'kevin' and password == '123':
            # 才是正常执行的函数
            res=func_name()
            is_login['is_login'] = True
            return res
        else:
            print('认证失败,不能执行函数')
    return auth

五、装饰器的固定模板

# 务必掌握 
def outer(func):
    def inner(*args, **kwargs):
        # 执行被装饰对象之前可以做的额外操作
        res = func(*args, **kwargs)
        # 执行被装饰对象之后可以做的额外操作
        return res
    return inner

六、装饰器语法糖

当我们使用上面的装饰器模版的时候,改变调用方式的那个、偷天换日的操作容易造成误导。因此我们引进语法糖的概念:

def outer(func_name):
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res
    return inner
"""
语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@函数调用,语法糖就是上文中把装饰器调用名变成原函数的操作
"""
@outer  # func = outer(func)
def func():
    print('from func')
    return 'func'

@outer  # index = outer(index)
def index():
    print('from index')
    return 'index'

func()
index()

七、多层语法糖

多层语法糖实际应用中出现较少,但是我们也需要了解相关的运行原理:

1、先判断距离被语法糖作用的函数最近的语法糖,它的作用就是把被装饰函数放入装饰器的函数中当参数。这个时候装饰器外层的函数会返回内部函数名。

2、接下来我们判断第二个装饰器的语法糖。这个语法糖的作用也是把下方得到的函数传到第二个装饰器中去,由于最下方那个语法糖的作用我们得到了最下方那个语法糖对应的内层函数名,所以这里就是把这个内层的函数名传到第二个装饰器中,然后我们会得到第二个装饰器返回给我们它的内层函数名。

3、最上方的语法糖的原理跟第二个类似,把第二个装饰器返回的内层函数名当成参数传到最上方语法糖对应的装饰器中,这里我们会得到最上方语法糖对应的装饰器内层的函数名称,并将她和被装饰函数的名称绑定

import'''注意语法糖会将下面紧挨着的函数当作参数传递给@符号后面的函数名运行'''


def outter1(func1):     # 12.这个时候func1就接到了wrapper2的函数名了
    print('加载了outter1')     # 13.打印加载outter1
    def wrapper1(*args, **kwargs):      # 14.在创建新的wapper1 return
        print('执行了wrapper1')        # 16.打印wrapper1
        res1 = func1(*args, **kwargs)       # 17.遇到括号优先执行 这个时候的fun1是wrapper2所以直接跳到wrapper2
        return res1
    return wrapper1     # 15. 返回 wrapper1


def outter2(func2):     # 07.所以这个时候的func2就是wrapper3
    print('加载了outter2')     # 08.打印加载outter2 
    def wrapper2(*args, **kwargs):      # 09.然后定义了wrapper2 下一步return
        print('执行了wrapper2')        # 18.打印wrapper2
        res2 = func2(*args, **kwargs)       # 19.func2就是wrapper1
        return res2
    return wrapper2     # 10.这个时候返回wrapper2就是index返回了


def outter3(func3):     # 02.这个时候fun3是真的index 运行fun3函数
    print('加载了outter3')     # 03.打印加载了outter3 函数定义不用看代码函数体代码 所以下一步 return
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')        # 20.打印wrapper3
        res3 = func3(*args, **kwargs)       # 21.func3就是index跳到index
        return res3
    return wrapper3     # 04.所以这时候index的返回值就是wrapper3 这个时候wrapper就成了函数名 再次触发 outter2


@outter1        #11.这个时候index就到了outter1在运行outter1		15.这个时候就变成伪装的 index = routter1(wrapper2)
@outter2        # 06.所以这个时候wrapper3会传到outter2里面在运行
@outter3        # 01.这个时候就会把真正的index传给outter3 所以 outer3(index) 括号优先级执行	05.所以这个时候就是 wrapper3 = outter3(index)
def index():
    print('from index')     # 22.结束


index()
    
"""
多层语法糖 加载顺序由下往上
每次执行之后如果上面还有语法糖 则直接将返回值函数名传给上面的语法糖
如果上面没有语法糖了 则变形 index = outter1(wrapper2)
"""
PYTHON 折叠 复制 全屏

八、装饰器修复技术

装饰器修复技术不算重点知识,但是在面试的时候可以装逼。

这里需要先介绍一下help方法的作用:用于查看函数或模块用途的详细说明。

如果一些小白使用help方法查看我们被装饰函数的用法,会发现查看到的是装饰器函数的内容,这时候我们使用一些小手段就可以让help方法指向的内容回到原函数:

1、在装饰器函数上面加上from functools import wraps

2、并且在内层函数前加上@wraps(func_name),这里括号内写装饰器外层的参数名称就可以了。

def index():
	"""index函数 非常的牛"""
    pass
help(index)
help(len)
# 这是使用修复技术前的代码,返回的结果会是外层装饰器的内容

from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器的效果更加逼真 平时可以不写
    def inner(*args, **kwargs):
        """我是inner 我擅长让人蒙蔽"""
        res = func_name(*args, **kwargs)
        return res
    return inner

@outer
def func():
    """我是真正的func 我很强大 我很牛 我很聪明"""
    pass


# help(func)
# print(func)
func()
# 这个时候我们发现代码运行后返回的是被装饰函数原本的内容

九、课堂练习题

# 判断七句print执行顺序
'''注意语法糖会将下面紧挨着的函数当作参数传递给@符号后面的函数名运行'''


def outter1(func1):     # 12.这个时候func1就接到了wrapper2的函数名了
    print('加载了outter1')     # 13.打印加载outter1
    def wrapper1(*args, **kwargs):      # 14.在创建新的wapper1 return
        print('执行了wrapper1')        # 16.打印wrapper1
        res1 = func1(*args, **kwargs)       # 17.遇到括号优先执行 这个时候的fun1是wrapper2所以直接跳到wrapper2
        return res1
    return wrapper1     # 15. 返回 wrapper1


def outter2(func2):     # 07.所以这个时候的func2就是wrapper3
    print('加载了outter2')     # 08.打印加载outter2 
    def wrapper2(*args, **kwargs):      # 09.然后定义了wrapper2 下一步return
        print('执行了wrapper2')        # 18.打印wrapper2
        res2 = func2(*args, **kwargs)       # 19.func2就是wrapper1
        return res2
    return wrapper2     # 10.这个时候返回wrapper2就是index返回了


def outter3(func3):     # 02.这个时候fun3是真的index 运行fun3函数
    print('加载了outter3')     # 03.打印加载了outter3 函数定义不用看代码函数体代码 所以下一步 return
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')        # 20.打印wrapper3
        res3 = func3(*args, **kwargs)       # 21.func3就是index跳到index
        return res3
    return wrapper3     # 04.所以这时候index的返回值就是wrapper3 这个时候wrapper就成了函数名 再次触发 outter2


@outter1        #11.这个时候index就到了outter1在运行outter1		15.这个时候就变成伪装的 index = routter1(wrapper2)
@outter2        # 06.所以这个时候wrapper3会传到outter2里面在运行
@outter3        # 01.这个时候就会把真正的index传给outter3 所以 outer3(index) 括号优先级执行	05.所以这个时候就是 wrapper3 = outter3(index)
def index():
    print('from index')     # 22.结束


index()

十、有参装饰器

当我们使用装饰器的时候,如果装饰器内部的代码需要参数的时候我们发现不能用传参的方式来添加参数了,所以我们又使用闭包函数在外层包了一个函数,打到传参的目的。

注:当我们看到语法糖后面跟着括号和参数的时候,需要先看函数名和括号内的内容,然后再应用语法糖功能,在我们运行之后会发现有参装饰器又变回了之前的普通装饰器只是多了一个局部名称空间读取变量

# 校验用户是否登录装饰器
def outer(mode):
    def login_auth(func_name):		
        # 这里我们可以看出来是用来传函数名的,不适合加参数了
        def inner(*args, **kwargs):		
            # 这里我们又可以看出来所有的参数都是加到内部被装饰函数中的,也不适合传别的参数所以就使用了闭包函数传参
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            if mode == '1':
                print('数据直接写死')
            elif mode == '2':
                print('数据来源于文本文件')
            elif mode == '3':
                print('数据来源于字典')
            elif mode == '4':
                print('数据来源于MySQL')
        return inner
    return login_auth
'''当装饰器中需要额外的参数时>>>:有参装饰器'''

"""
函数名加括号执行优先级最高 有参装饰器的情况 
    先看函数名加括号的执行
    然后再是语法糖的操作
"""
@outer('1')
def index():
    print('from index')
index()

@outer('2')
def func():
    print('from func')
func()
# 如果我们不想使用装饰器功能了,就把语法糖部分的代码注释掉就可以正常运行了。

十一、有参装饰器模板

def outter(add_n):
    def middle(func):
        def inner(*args, **kwargs):
            # 这里写一些代码需要参数的那种,把最外层的add_n用进来就可以了
            res = func(*args, **kwargs)
            return res

        return inner

    return middle


@outter('参数')
def index():
    pass