什么是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安装与使用redis的方法
Apr 19 Python
Python爬虫框架scrapy实现的文件下载功能示例
Aug 04 Python
解决Pycharm调用Turtle时 窗口一闪而过的问题
Feb 16 Python
浅析python 中大括号中括号小括号的区分
Jul 29 Python
详解pandas使用drop_duplicates去除DataFrame重复项参数
Aug 01 Python
django 控制页面跳转的例子
Aug 06 Python
Python 分发包中添加额外文件的方法
Aug 16 Python
Python中函数的返回值示例浅析
Aug 28 Python
python 实现Flask中返回图片流给前端展示
Jan 09 Python
Django 实现将图片转为Base64,然后使用json传输
Mar 27 Python
TensorFlow keras卷积神经网络 添加L2正则化方式
May 22 Python
Python实现手绘图效果实例分享
Jul 22 Python
Python经常使用的一些内置函数
python处理json数据文件
Python几种酷炫的进度条的方式
Python通过loop.run_in_executor执行同步代码 同步变为异步
Python Pandas解析读写 CSV 文件
宝塔更新Python及Flask项目的部署
python模板入门教程之flask Jinja
You might like
几道坑人的PHP面试题 试试看看你会不会也中招
2014/08/19 PHP
ThinkPHP中create()方法自动验证表单信息
2017/04/28 PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
2020/05/02 PHP
LBS blog sql注射漏洞[All version]-官方已有补丁
2007/08/26 Javascript
javascript 写类方式之八
2009/07/05 Javascript
浅谈Javascript事件处理程序的几种方式
2012/06/27 Javascript
Javascript操作URL函数修改版
2013/11/07 Javascript
jQuery 获取浏览器所在的IP地址的小例子
2013/11/08 Javascript
Javascript和Java获取各种form表单信息的简单实例
2014/02/14 Javascript
javascript获取flash版本号的方法
2014/11/20 Javascript
jQuery实现表格行上移下移和置顶的方法
2015/05/22 Javascript
js实现开启密码大写提示
2016/12/21 Javascript
Vue Ajax跨域请求实例详解
2017/06/20 Javascript
让nodeJS支持ES6的词法----babel的安装和使用方法
2017/07/31 NodeJs
利用Node.js了解与测量HTTP所花费的时间详解
2017/09/22 Javascript
JavaScript数据结构之双向链表和双向循环链表的实现
2017/11/28 Javascript
如何获取TypeScript的声明文件.d.ts
2018/05/01 Javascript
Vue表单输入绑定的示例代码
2018/11/01 Javascript
[02:52]2014DOTA2西雅图国际邀请赛 CIS战队巡礼
2014/07/07 DOTA
详解Python的Django框架中的中间件
2015/07/24 Python
python中的字典使用分享
2016/07/31 Python
Python简单生成8位随机密码的方法
2017/05/24 Python
Python用字典构建多级菜单功能
2019/07/11 Python
如何用OpenCV -python3实现视频物体追踪
2019/12/04 Python
python实现PCA降维的示例详解
2020/02/24 Python
Python Selenium 设置元素等待的三种方式
2020/03/18 Python
windows下的pycharm安装及其设置中文菜单
2020/04/23 Python
Python基于DB-API操作MySQL数据库过程解析
2020/04/23 Python
Django模板标签{% for %}循环,获取制定条数据实例
2020/05/14 Python
HTML5拖拽文件到浏览器并实现文件上传下载功能代码
2013/06/06 HTML / CSS
Why do we need Unit test
2013/01/03 面试题
抽奖活动主持词
2014/03/31 职场文书
租房协议书
2014/04/10 职场文书
高中学生自我评价范文
2014/09/23 职场文书
怎样写离婚协议书
2015/01/26 职场文书
python中__slots__节约内存的具体做法
2021/07/04 Python