什么是Python装饰器?如何定义和使用?


Posted in Python onApril 11, 2022

1.装饰器的定义

装饰器:给已有函数增加额外的功能的函数,本质上是一个闭包函数

特点:

  •     1.不修改已有函数的源代码
  •     2.不修改已有函数的调用方式
  •     3.给已有函数增加额外的功能
  •     4.代码执行时先解析装饰器
import time
 
# 装饰器原理
# def show():
#     n=0
#     for i in range(10000000):
#         n+=i
#     print('show_',n)
#
# # 定义一个闭包
# def count_time(fun):
#     def inner():
#         start=time.time()
#         fun()
#         end=time.time()
#         print(f'用时{end-start}秒')
#     return inner
#
# # 装饰器在装饰函数时的原理
# show=count_time(show)
# show()
 
# 定义装饰器(语法糖)
def count_time(fun):    # 必须要有一个参数接收被装饰函数
    def inner():
        start=time.time()
        fun()
        end=time.time()
        print(f'用时{end-start}秒')
    return inner
# 装饰器写法:@闭包的外部函数,必须在闭包定以后使用
print('解析装饰器1')
@count_time # 解释成show=count_time(show),show指向count_time函数中的inner
def show():
    n=0
    for i in range(10000000):
        n+=i
    print('show_',n)
 
print('解析装饰器2')
@count_time # 解释成display=count_time(display)
def display():
    print('Display')
 
print('正式执行...')
show()
display()

2.装饰器的通用类型的定义

(当被装饰函数有参数或者有返回值时同样适用)

'''
装饰器的通用类型的定义(当被装饰函数有参数或者有返回值时同样适用)
'''
 
def outer(func):
    def inner(*args,**kwargs):  # *为元组和列表解包,**为字典解包
        print('*'*30)
        print(args,kwargs)
        ret=func(*args,**kwargs)    # 解包,否则形参是元组或字典
        print('*'*30)
        return ret
    return inner
 
@outer
def show(name,msg):
    return str(name)+' say: '+str(msg)
 
print(show('Tom',msg='Hello'))

3.多个装饰器同时装饰一个函数

# 第一个闭包
def wrapper_div(func):
    def inner(*args,**kwargs):
        return '<div>'+func(*args,**kwargs)+'</div>'
    return inner
 
# 第二个闭包
def wrapper_p(func):
    def inner(*args,**kwargs):
        return '<p>'+func(*args,**kwargs)+'</p>'
    return inner
 
# 从下往上装饰,从上往下执行
@wrapper_div
@wrapper_p
# 定义一个函数
def show():
    return 'Short life I use Python.'
 
print(show())   #<div><p>Short life I use Python.</p></div>

4.多个装饰器同时装饰一个函数(二)

def outer1(func):
    def inner():
        print('装饰器1-1')
        func()
        print('装饰器1-2')
    return inner
 
def outer2(func):
    def inner():
        print('装饰器2-1')
        func()
        print('装饰器2-2')
    return inner
'''
1.show指向outer1.inner
2.outer1.inner.func指向outer2.inner
3.outer2.inner.func指向show
'''
@outer1
@outer2
def show():
    print('Show...')
 
show()

5.类装饰器使用方法

import time
 
class Wrapper():
    def __init__(self,func):
        self.func=func
 
    # 当类中实现了此方法时,该类的实例对象就变成了可调用对象,即可以在实例对象后面加()
    def __call__(self, *args, **kwargs):
        print('装饰内容1...')
        start=time.time()
        ret=self.func(*args,**kwargs)
        end=time.time()
        print(f'执行了{end-start}秒')
        print('装饰内容2...')
        return ret

该装饰器执行完成后,被装饰函数指向该类的实例对象
如果让被装饰函数执行,那么在类中要添加__call__方法,相当于闭包格式中的内函数
一旦被装饰函数执行调用,那么就会去执行实例对象中的__call__函数

@Wrapper    #解释成show=Wrapper(show),show变成了类的一个对象
def show():
    print('Show...')
 
show()
6.装饰器带有参数(使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数)
# @Author  : Kant
# @Time    : 2022/1/23 22:43
 
def set_args(msg):
    def outer(func):
        def inner():
            print('装饰内容',msg)
            func()
        return inner
    return outer
 
'''
使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回的是装饰器
调用set_args()后会返回outer的地址引用,变成了@outer
'''
@set_args('Hello')
# 无论闭包函数写成什么样子,被装饰函数永远指向闭包函数的内函数
def show():
    print('Show...')
 
show()

6.使用装饰器实现自动维护路由表

路由功能:通过请求的路径,可以找到资源的地址

# 定义一个路由表字典
router_table={}
def router(url):
    def wrapper(func):
        def inner():
            print('1')
            print('inner-',func)    # 查看当前的被装饰函数是谁
            func()
        # 在这里维护路由表字典
        router_table[url]=inner # 如果写func,inner函数中的内容都不会执行
        print('路由表字典:',router_table)
        return inner
    return wrapper
 
@router('index.html')
def index():
    print('首页内容')
 
@router('center.html')
def center():
    print('个人中心')
 
@router('mail.html')
def mail():
    print('邮箱页面')
 
@router('login.html')
def login():
    print('登录页面')
 
def error():
    print('访问页面不存在')
 
def request_url(url):
    func=error
    if url in router_table:
        func=router_table[url]
    func()
 
print('开始执行函数')
request_url('index.html')
request_url('center.html')
request_url('mail.html')
request_url('test.html')
request_url('login.html')
Python 相关文章推荐
跟老齐学Python之模块的加载
Oct 24 Python
用Python登录Gmail并发送Gmail邮件的教程
Apr 17 Python
编写Python爬虫抓取豆瓣电影TOP100及用户头像的方法
Jan 20 Python
Python正则捕获操作示例
Aug 19 Python
详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击
Oct 09 Python
在pycharm中使用git版本管理以及同步github的方法
Jan 16 Python
python适合人工智能的理由和优势
Jun 28 Python
Python上下文管理器用法及实例解析
Nov 11 Python
python中rc1什么意思
Jun 19 Python
Django视图、传参和forms验证操作
Jul 15 Python
Python Opencv实现单目标检测的示例代码
Sep 08 Python
Python实现将多张图片合成MP4视频并加入背景音乐
Apr 28 Python
Python经常使用的一些内置函数
python处理json数据文件
Python几种酷炫的进度条的方式
Python通过loop.run_in_executor执行同步代码 同步变为异步
Python Pandas解析读写 CSV 文件
宝塔更新Python及Flask项目的部署
python模板入门教程之flask Jinja
You might like
php常用的安全过滤函数集锦
2014/10/09 PHP
php通过array_merge()函数合并两个数组的方法
2015/03/18 PHP
php通过修改header强制图片下载的方法
2015/03/24 PHP
浅谈php数组array_change_key_case() 函数和array_chunk()函数
2016/10/22 PHP
新浪微博字数统计 textarea字数统计实现代码
2011/08/28 Javascript
jquery 根据name名获取元素的value值
2015/02/27 Javascript
JavaScript生成随机数的4种自定义函数分享
2015/02/28 Javascript
js获取表格的行数和列数的方法
2015/10/23 Javascript
JS组件Bootstrap Table布局详解
2016/05/27 Javascript
ES6学习教程之Map的常用方法总结
2017/08/03 Javascript
vue-router项目实战总结篇
2018/02/11 Javascript
Vue中使用webpack别名的方法实例详解
2018/06/19 Javascript
详解浏览器缓存和webpack缓存配置
2018/07/06 Javascript
Angular7.2.7路由使用初体验
2019/03/01 Javascript
JavaScript实现字符串与HTML格式相互转换
2020/03/17 Javascript
[01:03:13]VG vs Pain 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Django1.3添加app提示模块不存在的解决方法
2014/08/26 Python
python 实现得到当前时间偏移day天后的日期方法
2018/12/31 Python
详解Python传入参数的几种方法
2019/05/16 Python
Python笔记之工厂模式
2019/11/20 Python
浅谈Tensorflow 动态双向RNN的输出问题
2020/01/20 Python
Python几种常见算法汇总
2020/06/02 Python
解决python3中os.popen()出错的问题
2020/11/19 Python
美国第一大药店连锁机构:Walgreens(沃尔格林)
2019/10/10 全球购物
营业员实习自我鉴定
2013/12/07 职场文书
仓库班组长岗位职责
2013/12/12 职场文书
革命先烈的英雄事迹材料
2014/02/15 职场文书
小学生评语大全
2014/04/18 职场文书
小学三年级学生评语
2014/04/22 职场文书
公民代理授权委托书
2014/09/24 职场文书
八项规定个人对照检查材料思想汇报
2014/09/25 职场文书
会计工作检讨书
2015/02/19 职场文书
《抽屉原理》教学反思
2016/02/20 职场文书
提升Nginx性能的一些建议
2021/03/31 Servers
在JavaScript中如何使用宏详解
2021/05/06 Javascript
Fluentd搭建日志收集服务
2022/09/23 Servers