什么是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 相关文章推荐
Python3 入门教程 简单但比较不错
Nov 29 Python
Python中的XML库4Suite Server的介绍
Apr 14 Python
python使用arcpy.mapping模块批量出图
Mar 06 Python
Django 实现下载文件功能的示例
Mar 06 Python
Python Logging 日志记录入门学习
Jun 02 Python
Python接口测试数据库封装实现原理
May 09 Python
python属于解释型语言么
Jun 15 Python
使用python编写一个语音朗读闹钟功能的示例代码
Jul 14 Python
如何利用Python动态模拟太阳系运转
Sep 04 Python
python如何实现DES加密
Sep 21 Python
Python基础之pandas数据合并
Apr 27 Python
聊聊Python中关于a=[[]]*3的反思
Jun 02 Python
Python经常使用的一些内置函数
python处理json数据文件
Python几种酷炫的进度条的方式
Python通过loop.run_in_executor执行同步代码 同步变为异步
Python Pandas解析读写 CSV 文件
宝塔更新Python及Flask项目的部署
python模板入门教程之flask Jinja
You might like
全国FM电台频率大全 - 17 湖北省
2020/03/11 无线电
PHP资源管理框架Assetic简介
2014/06/12 PHP
新浪微博OAuth认证和储存的主要过程详解
2015/03/27 PHP
js arguments,jcallee caller用法总结
2013/11/30 Javascript
javascript判断两个IP地址是否在同一个网段的实现思路
2013/12/13 Javascript
javascript获取鼠标点击元素对象(示例代码)
2013/12/20 Javascript
Jquery 获取指定标签的对象及属性的设置与移除
2014/05/29 Javascript
ie8模式下click无反应点击option无反应的解决方法
2014/10/11 Javascript
在JavaScript中处理数组之reverse()方法的使用
2015/06/09 Javascript
jquery分析文本里url或邮件地址为真实链接的方法
2015/06/20 Javascript
jQuery Validate表单验证插件的基本使用方法及功能拓展
2017/01/04 Javascript
jQuery+ajax的资源回收处理机制分析
2017/01/07 Javascript
jQuery实现图片下载代码
2019/07/18 jQuery
vue-cli+iview项目打包上线之后图标不显示问题及解决方法
2019/10/16 Javascript
js验证账户名是否重复
2020/05/26 Javascript
[02:40]2014DOTA2 国际邀请赛中国区预选赛 四大豪门抵达华西村
2014/05/23 DOTA
[50:50]完美世界DOTA2联赛PWL S3 Galaxy Racer vs Phoenix 第一场 12.10
2020/12/13 DOTA
Python的Urllib库的基本使用教程
2015/04/30 Python
Python 3.x读写csv文件中数字的方法示例
2017/08/29 Python
Python 函数基础知识汇总
2018/03/09 Python
python实现简单图片物体标注工具
2019/03/18 Python
基于python实现高速视频传输程序
2019/05/05 Python
Python爬取知乎图片代码实现解析
2019/09/17 Python
如何解决pycharm调试报错的问题
2020/08/06 Python
谈谈python垃圾回收机制
2020/09/27 Python
html5默认气泡修改的代码详解
2020/03/13 HTML / CSS
漫威玩具服装及周边商品官方购物网站:Marvel Shop
2019/05/11 全球购物
如何判断计算机可能已经中马
2013/03/22 面试题
毕业自我评价范文
2013/11/17 职场文书
上班看电影检讨书
2014/02/12 职场文书
国贸专业毕业求职信
2014/06/11 职场文书
中韩经贸翻译专业大学生职业生涯规划范文
2014/09/18 职场文书
小学优秀教师材料
2014/12/15 职场文书
旷工辞退通知书
2015/04/17 职场文书
详解Mysql和Oracle之间的误区
2021/05/18 MySQL
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python