Python中的装饰器

本文分为以下几点

  • 装饰器介绍
  • 装饰器语法糖
  • 装饰的函数有返回值
  • 保留被装饰的函数的函数名和帮助文档信息
  • 装饰器中不同条件下执行不同的函数
  • 多个装饰器
  • 带有参数的装饰器

一.装饰器介绍

装饰器就是对被装饰的对象(函数、类)进行重构的,其可以在不改变原来对象的情况下调用对象时执行重构后的行为
1.解决问题:在函数执行之前和执行之后添加功能,调用函数的方式改变了
2.不改变原有函数的调用方法:函数里面嵌套函数,并且返回嵌套的函数
例:

##引题:当登陆某系统时常常会有欢迎语,但修改时将在登陆函数中修改,可能会出现问题,所以避免直接侵入原函数修改。
def login():
    print("中秋快乐")
    print("login....")
    print("欢迎您下次光临....")
login()
##为了避免在大的函数块中操作,将欢迎语独立出来,另建函数。
def desc(fun):
    def add_info():
        print("中秋快乐")
        fun()
        print("欢迎您下次光临")
    return add_info
def login():
    print("login....")
login = desc(login)
login()

运行结果:
这里写图片描述

二.装饰器语法糖

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

import functools           #functools 标准库
import time                #time 时间函数

def add_log(fun):
    @functools.wraps(fun)           #不用此模块add(x,y)函数的__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.1)
    return x+y
print(add(1,2))

这里写图片描述

小练习:

查看字符串拼接的效率
1."hello" + "world"
2." ".join(['hello' + 'world'])
import random
import string
import time
import functools
li = [random.choice(string.ascii_letters) for i in range(10000)]

def timeit(fun):
    def wrapper():
        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():         #用.join方法连接字符串
    print(" ".join(li))
con_add()
join_add()

这里写图片描述

三.装饰的函数有返回值

解决办法:给给返回值赋值

def timeit(fun):
    def wrapper(*args,**kwargs):    #接收可变参数和关键字参数
        #args:元组 kwargs:字典  args=(5,)
        #原函数执行之前
        start_time = time.time()
        #执行函数
        res = fun(*args,**kwargs)   #args解包
        #执行函数
        end_time = time.time()
        print("运行时间为:%.6f" % (end_time - start_time))
    return res
    return wrapper

@timeit
def list_create(n):
    return(i*2 for i in range(n)])

@timeit
def map_create(n):
    return(list(map(lambda x:x*2,range(n))))

list_create(5)
map_create(5)
print(list_create(100))     ##wrapper(100)

这里写图片描述

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

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

import  functools
def timeit(fun):
    """这是一个装饰器timeit"""    
    @functools.wraps(fun)       ##内置装饰器
    可以保留被装饰的函数的函数名和帮助文档信息   
    def wrapper(*args,**kwargs):    #接收可变参数和关键字参数
        """这是一个wrapper函数"""
        #args:元组 kwargs:字典  args=(5,)
        #原函数执行之前
        start_time = time.time()
        #执行函数
        res = fun(*args,**kwargs)   #args解包
        #执行函数
        end_time = time.time()
        print("运行时间为:%.6f" % (end_time - start_time))
    return res
    return wrapper
@timeit
def fun():
    print("hello")
print(fun_list.__name__)
print(fun_list.__doc__)

这里写图片描述
这里写图片描述

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

用实例说明:

 需求: 用户来CSDN登陆验证的装饰器is_login
   1). 如果用户登陆成功, 则执行被装饰的函数;
   2). 如果用户登陆不成功, 则执行登陆函数
import functools
login_users = ['admin', 'root']

def is_login(fun):  # fun: writeBlog
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):   # name="admin"  # kwargs={"name":"admin"}
        # 判断写博客的这个用户是否登陆成功;
        if kwargs.get("name") in login_users:
            res = fun(*args, **kwargs)
            return res
        else:
            res=login()
        return res

    return wrapper

# 必须登陆成功
@is_login    # writeBlog = is_login(writeBlog)
def writeBlog(name):
    return "编写博客"

def login():
    return "登陆。。。。"

# 是否登陆成功都可以执行代码
def news():
    print("新闻......")

print(writeBlog(name="admin"))

这里写图片描述
这里写图片描述

六.多个装饰器

若有两个装饰器,从上到下调用装饰器,wrapper内容也是为由上向下执行
以下代码说明其执行的顺序

def makebold(fun):
    print("bold1")
    def wrapper1(*args, **kwargs):
        print("bold2")
        return  fun(*args, **kwargs)  # wrapper
    return  wrapper1
def makei(fun):   # fun=login
    print("i1")
    def wrapper(*args, **kwargs):
        print("i2")
        return  fun(*args, **kwargs)
    return  wrapper
# 当有多个装饰器时,从上到下调用装饰器
# 真实wrapper内容执行是从上到下执行
@makebold   #login = makebold(login)    #login为wrapper1
@makei      #login = makei(login)       #login为wrapper
def login():
    return "登陆"
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   #先判断是否为admin用户
def writeBlog(name):
    return "编写博客"

def login():
    return "登陆..."

def news():
    print("新闻")

print(writeBlog(name = 'admin'))

这里写图片描述
这里写图片描述

七.带有参数的装饰器

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


import functools
import time

# format
def log(kind):  # kind="debug"
    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, 运行时间:%.5f, 运行返回值结果:%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.1)
    return x+y
print(add(14214124124,1241231231313))
# wrapper(14214124124,1241231231313)

这里写图片描述
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值