Python(十二)装饰器

一、装饰器的概念

通俗讲:本质是函数,为其它函数添加附加功能,算是高阶函数!

     原则:1、不能修改被装饰的函数的结构

                 2、不能修改被装饰的函数的调用方式!

    简言之:python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数

    好处:就是在不用更改原函数的代码前提下给函数增加新的功能!

二、使用方式

需求1

# (1)定义一个函数,在不修改源代码的前提下,在不修改函数代码的前提下,对函数的功能进行拓展
def fun1():
    print('登陆成功')

# (2)#2.定义一个高级函数(闭包)实现对f1()函数进行权限验证!
def fun2(fun1):
    def fc():
        print("首先对fun1函数权限进行验证")
        fun1() #实际功能函数模块!
        print("fun1函数已经处理完毕了")
    return fc

# (3)函数的调用-->实现对fun1()函数调用的权限验证!

# 说明:这个过程一定要好好体会!
# 这里传递的是函数名,并没有传递参数!
t = fun2(fun1)
t()

"""
首先对fun1函数权限进行验证
登陆成功
fun1函数已经处理完毕了
"""

进一步简化

def wrapper(fun1):
    def fc():
        print("首先对fun1函数权限进行验证")
        fun1()
        print("fun1函数已经处理完毕了")
    return fc

# 等价方式--->fun1=wrapper(fun1)-->返回同一个接口!
# 作用--->添加功能并保持原函数名不变!
# Python中支持了@语法糖-->帽子
@wrapper
def fun1():
    print('登陆成功')

# (3)函数的调用
fun1()

"""
首先对fun1函数权限进行验证
登陆成功
fun1函数已经处理完毕了
"""

理解:函数和函数调用的区别

需求2:装饰器实现一个函数计时器

# 说明:统计
import time
import string
import random
import functools

li = [random.choice(string.ascii_letters) for i in range(1000)]

def timewc(fun):
    def wapper(*args, **kwargs):
        # 在函数的执行之前
        # 当前时间-->秒
        start_time = time.time()
        # 执行函数-->统计它的执行时间!
        res = fun(*args, **kwargs)
        # 在函数执行之后
        end_time = time.time()
        # 注意:默认是保留6位小数!
        print('运行的时间为:%.6f' % (end_time - start_time))
        return res
    return wapper

@timewc
def con_add(): 
    # print('+'.join(li)) -->等价方式!
    s = ''
    for i in li:
        s += (i + '+')
    print(s)

con_add()

需求3:性能比较(完成相同的时间耗时)

@timeit
def fun_list(n):
    """这是fun_list函数,被timeit装饰"""
    return [2 * i for i in range(n)]
@timeit
def fun_map(n):
    """这是fun_map函数,被timeit装饰"""
    return list(map(lambda x:x*2,range(n)))

# 说明:部分代码省略(见上)
fun_list(5000)
fun_map(5000)

需求4:打印日志

"""
# 创建装饰器, 要求如下:
# 1. 创建add_log装饰器, 被装饰的函数打印日志信息;
# 2. 日志格式为: [字符串时间] 函数名: xxx,
 运行时间:xxx, 运行返回值结果:xxx
"""
import time
import functools
#print(time.ctime())

def add_log(func):
    # 说明:这里对帮助文档和函数名称又进行了包装,确保获取的是源函数的信息!
    # 细节-->接受参数的问题(一般参数到元组中,关键字参数到字典中)
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        """warpper函数的帮助文档"""
        start_time = time.time()
        res = func(*args,**kwargs)
        end_time = time.time()
	# "时间字符串"-->time.ctime()
	#  注意是长"__",并且需要倒包,否则就是包装函数的信息!
	# "函数的名字"-->func.__name__
        print('[%s] 函数名:%s,运行时间:%.6f,运行返回值的'
              '结果:%d' %(time.ctime(),func.__name__,
                        end_time-start_time,res))
        return res
    return wrapper

@add_log
def add(x,y):
    """add源函数的帮助文档"""
    # 只是为了效果说明
    time.sleep(1)
    return x+y

result=add(1,10)
# 理解-->实际调用的是wrapper(1,10)
# 注意观察:倒包前后所显示的帮助文档信息和函数名称!
print(add.__name__)

 解决1:被装饰的函数有返回值的时候怎么办!

 解决2:被装饰的函数如何保留自己的函数名和帮助信息文档!

思考:多个装饰函数装饰同一个函数?

# 多个装饰器-->执行先后顺序
def deorator_a(fun):
    print('A')
    def inner(*args, **kwargs):
        print(args)
        print('B')
        return fun(*args, **kwargs)

    print('C')
    return inner


def deorator_b(fun):
    print('D')
    def outer(*args, **kwargs):
        print(args)
        print('E')
        return fun(*args, **kwargs)

    print('F')
    return outer


## 注意:代码的执行是从上到下执行!
@deorator_a
@deorator_b
def f(x):
    print('G')
    return x * 2


# 函数的执行
print(f(1))

结果

D
F
A
C
(1,)
B
(1,)
E
G
2

分析

"""
分析1:代码是自上而下执行的,由于全局只有一个线程,所以只设计代码的跳转问题,并不设计代码的阻塞问题!
分析2:相当于 f=decorator_a(decorator_b(f)) --> f(1)

执行过程
 
f=decorator_a(decorator_b(f)) --->只是对decorator_b和decorator_a函数的调用,返回inner

f(1)                          --->对decorator_a函数的内部函数inner进行调用,尽而调用decorator_b函数的内部函数outer,最后调用真实的f(1)函数!

     temp=decorator_b(f) -->outer -->fun=f
     f=decorator_b(temp) -->inner -->fun=outer
     f(1)

明确:函数只有在调用的时候才执行!

实际:先执行相关代码,然后才调用函数!

注意:要明确调用的函数(内层还是外层),只有传参的那一刻才是调用内层函数!

"""

说明:可以通过Debug的调试模式,来看代码的跳转来加以理解!

多个装饰器的应用

"""
已知:['root','admin','redhat']

模拟多个装饰器的应用场景:会采用多个装饰器先验证是否登陆成功,再验证登陆权限是否足够(是不是root)!

"""
import inspect
import functools
# 权限校验模块!
def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        # inspect.getcallargs会返回一个字典,
        # key值:形参 value:对应的实参数
        inspect_res = inspect.getcallargs(fun,*args,**kwargs)
        print('inspect的返回值是:%s' %(inspect_res))
        # (1)登陆用户是不是root-->权限足够!
        # 注意:为什么调用key=name -->底层形参名!
        if inspect_res.get('name') == 'root':
            temp = fun(*args,**kwargs)
            return temp
        else:
        # (2)权限够不够(是不是root)
            print('not root user,no permisson add user')
    return wrapper

# 用户名的数据库的模拟
login_session = ['root','admin','redhat']

# 用户登陆
def is_login(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        # 核心:判断登陆的用户存不存在数据库中(模拟)
        if args[0] in login_session:
            # 说明:一旦进入就表明登陆成功,此时进行权限校验的判断!
            temp = fun(*args,**kwargs)
            # 注意:这里返回的是函数名-->没有意义(因为底层并没有返回数值!)!
            return temp
        else:
            print('Error:%s 没有登陆成功' %(args[0]))
    return wrapper
@is_login
@is_admin
def add_user(name):
    print('add user~')

# 调用函数
add_user('root')

思考:如果装饰器想携带参数怎么办?

import functools
import time

def log(kind): #装饰器
    def add_log(func):# #装饰器加参数需要多加一层嵌套
        @functools.wraps(func)
        def wrapper(*args,**kwargs):# 为了兼容各类函数参数,添加 *args,**kwargs 不固定参数
            start_time = time.time()
            res = func(*args,**kwargs)
            end_time = time.time()
            print('<%s>[%s] 函数名:%s,运行时间:%.6f,运行返回值的'
              '结果:%d' %(kind,time.ctime(),func.__name__,
                        end_time-start_time,res))
            return res
        return wrapper
    return add_log

@log('debug')
def add(x,y):
    time.sleep(1)
    return x+y
print(add(1,2))

小结

关于多个装饰器修饰一个函数总结要点:

1.当一个函数被多个装饰器装饰时,装饰器的加载顺序是从内到外的(从下往上的)。

 理解:装饰器是给函数装饰的,所以要从靠近函数的装饰器开始从内往外加载!

2.外层的装饰器,是给里层装饰器装饰后的结果进行装饰。

相当于:外层的装饰器装饰的函数是里层装饰器的装饰原函数后的结果函数(装饰后的返回值函数)

三、装饰器的应用场景

可以用到装饰器的地方有很多,简单的举例如以下场景

  • 引入日志
  • 函数执行时间统计
  • 执行函数前预备处理
  • 执行函数后清理功能
  • 权限校验等场景
  • 缓存
1. 装饰器本质上是一个高级Python函数,通过给别的函数添加@标识的形式实现对函数的装饰

2.装饰器的功能:

它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。

装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

四、装饰器的相关参考

装饰器的工作原理和执行顺序

python的设计模式

python设计模式

Java设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值