Python中装饰器

装饰器

装饰器就是对被装饰的对象(函数、类)进行重构的,其可以在不改变原来对象的情况下调用对象时执行重构后的行为

  • 解决问题: 在函数执行之前和执行之后添加功能, 调用函数的方式改变了.
  • 不改变原有函数的调用方法: 函数里面嵌套函数, 并且返回嵌套的函数
def desc(fun):   # fun = login  需要传递一个函数, 要装饰的函数;装饰器函数里面嵌套函数
    def add_info():
        print('中秋快乐')
        fun()
        print('欢迎下次光临')
    return add_info
def login():
    print('loging...')
login=desc(login)
login()

在这里插入图片描述

装饰器语法糖

在Python中,可以使用”@”语法糖来精简装饰器的代码,把 decorator 置于函数的定义处,免去给函数重新赋值**(** 即function = decorator(funtion))
如下解释:

import functools  #functools标准库
import time       #time 时间函数
def add_log(fun):
    @functools.wraps(fun)  #不加这个的话add模块中__name__,__doc__可能会丢失
    def wrapper(*args,**kwargs):
        """*args为元组,**kwargs为字典"""
        start_time=time.time()
        res=fun(*args,**kwargs)
        end_time=time.time()
        print('[%s]函数名:%s,运行时间:%.5f,运行返回值:%d' %(time.ctime(),fun.__name__,end_time-start_time,res))
        return res   #返回值为所要装饰的函数
    return wrapper   #返回值为所要修饰函数添加的模块
@add_log    #调用语法糖
def add(x,y):
    time.sleep(0.2)
    return x+y
print(add(3,4))

在这里插入图片描述
装饰器需求: 获取每个函数的执行时间

  • 1).函数执行之前计算时间
  • 2).函数执行之后计算时间

eg1:
看字符串拼接的效率
1). “hello” + “world”
2). " ".join([‘hello’, “world”])

import random
import string
import time
li=[random.choice(string.ascii_letters) for i in range(30)]
def timeit(fun):
   def wrapper(*args,**kwargs):
       #函数执行之前
       start_time=time.time()
       #执行函数
       fun()
       #执行函数之后
       end_time=time.time()
       print('运行时间:%.6f' %(end_time-start_time))
   return wrapper
@timeit
def con_add():
   s=''
   for i in li:
       s+=(i+',')
   print(s)
@timeit
def join_add():
   print(','.join(li))

con_add()
join_add()

在这里插入图片描述
eg2:
检测列表生成式和map的效率高低, n为函数传入的参数===两者实力相当
1). [n2 for i in range(n)]
2). map(lambda x:x
2, range(n))

import time
def timeit(fun):
   def wrapper(*args,**kwargs):
   """这是一个wrapper函数"""
       start_time=time.time()
       fun(*args,**kwargs)
       end_time=time.time()
       print('运行时间为:%.6f' %(end_time-start_time))
   return wrapper
@timeit
def fun_list(n):
   print([2*i for i in range(n)])
@timeit
def fun_map(n):
   print(list(map(lambda x:x*2,range(n))))
fun_list(50000)
fun_map(50000)
print(fun_list.__name__)
print(fun_list.__doc__)

在这里插入图片描述

代码中最后两行,输出的是fun_list信息,但是打印出来却是关于装饰器的信息,那么如何来保留被装饰函数的信息呢?

保留被装饰的函数的函数名和帮助文档信息

解决办法:导入functools标准库,使用functools.wraps函数,保留被装饰的函数的函数名和帮助文档信息。

import functools
import time
def timeit(fun):
    """这是一个装饰器timeit"""
    @functools.wraps(fun) #可以保留被装饰函数的函数名和帮助文档信息
    def wrapper(*args,**kwargs):
        """这是一个wrapper函数"""
        start_time=time.time()
        res=fun(*args,**kwargs)
        end_time=time.time()
        print('运行时间为:%.6f' %(end_time-start_time))
        return res
    return wrapper
@timeit
def fun_list(n):
    """这是一个fun_list函数,被timeit装饰"""
    return [2*i for i in range(n)]
@timeit
def fun_map(n):
    """这是一个fun_map函数"""
    return map(lambda x:x*2,range(n))

fun_list(5000)
fun_map(5000)
print(fun_list.__name__)
print(fun_list.__doc__)

在这里插入图片描述

装饰器中不同条件下执行不同的函数

要求: 用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数

import functools
login_users=['admin','root']
def is_login(fun):#fun: writeBlog
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        #判断博客的用户是否登陆成功
        if kwargs.get('name') in login_users:
            res=fun(*args,**kwargs)
            return res
        else:
            res=login()
        return res
    return wrapper
#必须登陆成功
@is_login
def writeBlog(name):
    return '写博客'

def login():
    return '登陆...'
#是否登陆成功都可以执行的代码
def news():
    print('开始播放新闻...')
print(writeBlog(name='admin'))
必须登陆成功的执行结果

在这里插入图片描述

用户不在登陆名单执行结果

在这里插入图片描述

装饰器日志

1.创建add_log装饰器, 被装饰的函数打印日志信息;
2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx

import functools
import time
def add_log(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=fun(*args,**kwargs)
        end_time=time.time()
        print('[%s] 函数名:%s,运行时间:%.5f,运行返回值:%d' %(time.ctime(),fun.__name__,end_time-start_time,res))
        return res
    return wrapper
@add_log
def add(x,y):
    time.sleep(0.2)
    return x+y
print(add(4,5))

在这里插入图片描述

带有多个装饰器的函数

def makebold(fun):
    print('bold1')
    def wrapper1(*args,**kwargs):
        print('bold2')
        return fun(*args,**kwargs)
    return wrapper1
def makei(fun):
    print('i1')
    def wrapper(*args,**kwargs):
        print('i2')
        return fun(*args,**kwargs)
    return wrapper
#当有多个装饰器时,从下到上调用装饰器,
#真实wrapper内容执行是从上到下执行.
@makebold   #login=makebold(loogin)  login为wrapper1
@makei      #login=nakei(login)      login为wrapper
def login():
    return 'login...'
print(login())

在这里插入图片描述
练习:判断登陆用户是否为管理员is_admin(此处管理员只有一个为:admin用户)
1).如果用户为管理员, 则执行被装饰的函数;
2).如果用户不是管理员, 则报错;

import functools
login_users=['admin','root']
def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        if kwargs.get('name')=='admin':
            res=fun(*args,**kwargs)
            return res
        else:
            return 'Error 没有访问权限'
    return wrapper
def is_login(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        if kwargs.get('name') in login_users:
            res=fun(*args,**kwargs)
            return res
        else:
            res=login()
        return res
    return wrapper
#必须登陆成功
@is_login
@is_admin
def writeBlog(name):
    return '编写博客'
def login():
    return '登陆...'
#无论登陆成功都执行的代码
def news():
    print('this is a news')
print(writeBlog(name='root'))

管理员为root执行的结果
在这里插入图片描述
管理员为admin执行的结果
在这里插入图片描述

带有参数的装饰器

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

import functools
import time
def log(kind):
    def add_log(fun):
        @functools.wraps(fun)
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=fun(*args,**kwargs)
            end_time=time.time()
            print('<%s> [%s] 函数名:%s,运行时间:%.6f,返回值:%d' %(kind,time.ctime(),fun.__name__,end_time-start_time,res))
            return res
        return wrapper
    return add_log
@log('debug') #log('debug')===>返回值是add_log #add=add_log(add)
def add(x,y):
    time.sleep(0.3)
    return x+y
print(add(3,5))

在这里插入图片描述

装饰器判断变量类型

编写装饰器required_ints, 条件如下:
1). 确保函数接收到的每一个参数都是整数;
2). 如果参数不是整形数, 打印 TypeError:参数必须为整形

import functools
def required_ints(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        for i in args:
            if not isinstance(i,int):
                print('函数所有参数并非为整型')
                break
        else:
            res=fun(*args,**kwargs)
            return res
    return wrapper
@required_ints
def add(a,b):
    return a+b
@required_ints
def myMax(a,b,c,d):
    return max(a,b,c,d)
print(add(2,4.4))
print(myMax(5,8,3,9.2))

在这里插入图片描述
整型结果;
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值