装饰器

装饰器的介绍

  什么是装饰器

“装饰”指的是为被装饰对象添加新的功能,“器”指的是工具。

装饰器指的是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能

  为何要用装饰器

# 开放封闭原则
    开放:对扩展是开放的(可以添加新的功能)
    封闭:对修改是封闭的(不能修改源代码和调用方式)
装饰器就是在不修改被装饰对象源代码和调用函数的前提下为被装饰对象添加额外的功能

装饰器的实现

装饰器分为无参装饰器和有参装饰器,二者的实现原理都是 函数嵌套+闭包函数+函数对象 组合使用的产物

  无参装饰器

 无参装饰器的模板

def outter(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

 无参装饰器的推导

需求:在不修改index函数的源代码及调用方式的前提下为其添加统计运行时间的功能

# 源代码及其调用方式
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200
index(111,y=222)
复制代码
# 解决方案一
# 没有修改被装饰对象的源代码,没有修改改变方式,并且增加了新功能,但代码冗余
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200
start_time = time.time()
index(111,y=222)
stop_time = time.time()
print(stop_time-start_time)
# 当index需要调用n次,新添的功能代码也要编写n次,代码冗余
解决方案一
复制代码
复制代码
# 解决方案二
# 解决了方案一的冗余问题,但是函数的调用方式改变了
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200

def wrapper():
    start_time = time.time()
    index(111,y=222)
    stop_time = time.time()
    print(stop_time-start_time)
wrapper()
解决方案二
复制代码
复制代码
# 方案二优化一
# 将index的参数写活
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200

def wrapper(*args,**kwargs):  # args=(111,) kwargs={'y':222,}
    start_time = time.time()
    index(*args,**kwargs)  # index(111,y=222)
    stop_time = time.time()
    print(stop_time-start_time)
wrapper(111,y=222)
方案二优化一
复制代码
复制代码
# 方案二优化二
# 在优化一的基础上把被装饰对象写活,原来只能装饰index
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200
def home(name):
    time.sleep(1)
    print('my name is %s' % name)
    return 300
def outter(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs):
        stop_time = time.time()
        print(stop_time-start_time)
    return wrapper
index = outter(index)  # index=wrapper的内存地址
home = outter(home)  # home=wrapper的内存地址
index(111,y=222)
home('yuanxiaojiang')
方案二优化二
复制代码
复制代码
# 方案二的优化三
# 将wrapper做的跟被装饰对象一摸一样,以假乱真
import time
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200
def home(name):
    time.sleep(2)
    print('index %s' % name)
    return 300

def outter(func):
    # func = index的内存地址
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)  # index的内存地址
        stop_time = time.time()
        print(stop_time-start_time)
        return res
    return wrapper
index = outter(index)  # index=wrapper的内存地址
home = outter(home)  # home=wrapper的内存地址
res = home('yuanxiaojiang')
print(res)
方案二的优化三
复制代码
复制代码
# 糖心法(在被装饰对象正上方单独写一行@装饰器名字)
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)
        stop_time = time.time()
        print(stop_time-start_time)
        return res
    return wrapper

@timmer  # index=timmer(index)
def index(x,y):
    time.sleep(2)
    print('index %s %s' % (x,y))
    return 200

@timmer  # home=timmer(home)
def home(name):
    time.sleep(2)
    print('index %s' % name)
    return 300
复制代码

 装饰器的叠加

@deco3
@deco2
@deco1
def index():
    pass

# index = deco3(deco2(deco1(index)))

 wraps装饰器

复制代码
from functools import wraps

def outter(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    # 手动将原函数的属性赋值给wrapper函数
    # 1、函数wrapper.__name__ = 原函数.__name__
    # 2、函数wrapper.__doc__ = 原函数.__doc__
    # wrapper.__name__ = func.__name__
    # wrapper.__doc__ = func.__doc__
    return wrapper

@outter  # index=outter(index)
def index(x,y):
    """这是个主页功能"""
    print(x,y)

print(index.__name__)
print(index.__doc__)  # help(index)
复制代码

  有参装饰器

# 添加一个为被装饰对象添加认证功能的装饰器,实现的基本形式如下
def deco(func):
    def wrapper(*args,**kwargs):
        # 编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res
    return wrapper

 

复制代码
from functools import wraps
def auth(driver):
    def deco(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            if driver == 'file':
                # 编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res
                print('基于文件的认证')
                res = func(*args,**kwargs)
                return res
            elif driver == 'mysql':
                # 编写基于mysql的认证,认证通过则执行res=func(*args,**kwargs),并返回res
                print('基于mysql的认证')
                res = func(*args,**kwargs)
                return res
        return wrapper
    return deco

# 方式1
deco = auth(driver='file')  # deco=deco函数的内存地址
@deco  # index=deco(index),index=wrapper函数的内存地址
def index(x,y):
    print('index->>%s:%s' % (x,y))
    return 200
deco = auth(driver='mysql')  # deco=deco函数的内存地址
@deco  # home=deco(home),home=wrapper函数的内存地址
def home(name):
    print('index->>%s' % name)
    return 300

# 方式2
@auth(driver='file')  # @deco  # index = deco(index)
def index(x,y):
    print('index->>%s:%s' % (x,y))
    return 200
@auth(driver='mysql')  # @deco  # home = deco(home)
def home(name):
    print('index->>%s' % name)
    return 300
复制代码

 

posted @   猿小姜  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示