Python3装饰器 - vip共享吧
  • 网站首页
  • IT技术笔记
    • Java教程
    • MySql数据库
    • PHP开发
    • Python教程
    • JavaScript
    • SEO优化
    • 常用工具
  • 好资源福利
    • 会员账号共享
  • 网站模板源码
    • 小程序源码
    • 网站源码
  • 共享网络资源
  • 更多功能
    • 留言吐槽
    • 文章归档
    • 我的邻居
    • 史上今日
    • 视频解析
    • 高清壁纸
    • 公告动态
    • 广告合作
    • 关于我们


导航菜单
  • 网站首页
  • IT技术笔记
    • Java教程
    • MySql数据库
    • PHP开发
    • Python教程
    • JavaScript
    • SEO优化
    • 常用工具
  • 好资源福利
    • 会员账号共享
  • 网站模板源码
    • 小程序源码
    • 网站源码
  • 共享网络资源
  • 更多功能
    • 留言吐槽
    • 文章归档
    • 我的邻居
    • 史上今日
    • 视频解析
    • 高清壁纸
    • 公告动态
    • 广告合作
    • 关于我们
Python

Python3装饰器

2024/9/6 韩俊  Python教程 464 0

前面我们学习了闭包,闭包从语法上来讲就是在函数体内定义一个函数,而本节课我们要讲的装饰器就是利用闭包语法来实现项目的简洁化和模式化。

Python装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

写个装饰器

由于项目越来越庞大,有时候客户反映程序用起来不像以前那么顺,这时候,技术负责人想让你编程对项目中每个函数进行性能测试, 此时,作为一线搬砖的你应该怎么办?

首先,假如我们项目中有一些函数,如下:


# 函数 1
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

现在我们想看看测试这个函数的性能,于是我们改写以上代码如下:

from time import time

'''
项目已有函数
'''
# 函数 1
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

'''
对项目已有函数的性能测试
'''
before = time()
func_one(25000)
after = time()
print("该函数耗时:", (after - before))

before = time()
func_two(25000)
after = time()
print("该函数耗时:", (after - before))

before = time()
func_three(25000)
after = time()
print("该函数耗时:", (after - before))

作为一个懒惰的程序员,我们立马就发现了,有一些重复的代码,像喋喋不休的排比句一样反复出现:执行一个函数,并计算这个函数的执行时间。于是我们就可以把这个模式抽象出来,改写代码如下:

from time import time

'''
项目已有函数
'''
# 函数 1
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

'''
对项目已有函数的性能测试
'''
def timer(func, data):
    before = time()
    func(data)
    after = time()
    return(after - before)

print("该函数耗时:", timer(func_one, 25000))
print("该函数耗时:", timer(func_two, 25000))
print("该函数耗时:", timer(func_three, 25000))

但这样还是很麻烦,因为我们得改到所有的测试用例,比如把 func_one(25000) 改成 timer(add, 25000) 等等。于是我们进一步改进,让 timer 返回函数:

from time import time
'''
项目已有函数
'''
# 函数 1
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

'''
对项目已有函数的性能测试
'''

def timer(func):
    def wraper(data):
        before = time()
        rst = func(data)
        after = time()
        print("该函数耗时:", after - before)
        return rst
    return wraper


func_one = timer(func_one)
func_two = timer(func_two)
func_three = timer(func_three)

func_one(25000)
func_two(25000)
func_three(25000)

这里的最后一个问题是,我们的 timer 包装的函数可能有不同的参数,于是我们可以进一步用 *args, **kwargs 来传递参数,只需要把上面的 timer 函数改成下面的形式即可:

def timer(func):
    def wraper(*args, **kwargs):
        before = time()
        rst = func(*args, **kwargs)
        after = time()
        print("该函数耗时:", after - before)
        return rst
    return wraper

这里的 timer 函数就是一个“装饰器”,它接受一个函数,并返回一个新的函数。在装饰器的内部,对原函数进行了“包装”。

大家注意,我们调用代码 func_one = timer(func_one) 之后,实际上相当于改写了 func_one 函数,原项目中的关于 func_one 函数的调用都多了一个对 func_one 函数的进行性能测试的功能,而对 func_one 函数本身的返回值并无影响。当然,我们可以把性能测试的输出写到日志里面,而不是输出到屏幕上。

这种不需要改写原函数本身,就可以增加原函数行为的方式非常符合软件工程中的“对增加开发,对修改关闭”的设计原则。

装饰器语法糖

刚刚我们利用闭包的语法解决了项目中关于对所有函数进行性能测试的问题,实际上,我们自己写了一个装饰器,但是我们都知道,Python 是有很高的装逼姿势,python 提供了一个装饰器的语法糖,看起来很有逼格。

当然,我们也不能为了装逼而装逼,凡事总得有个理由吧,那我们就找一个,我们发现我们自己写的装饰器代码,最后调用部分代码如下:

func_one = timer(func_one)
func_two = timer(func_two)
func_three = timer(func_three)

func_one(25000)
func_two(25000)
func_three(25000)

上面这个语句里,我们把要测试性能的每个函数的名字重复了 3 次,如果项目中函数改了名字,我们的性能测试代码就得改 3 处。装逼的 Python 就想了一个更“好”的方法,提供了一个语法来替换上面的内容:

from time import time

def timer(func):
    def wraper(*args, **kwargs):
        before = time()
        rst = func(*args, **kwargs)
        after = time()
        print("该函数耗时:", after - before)
        return rst
    return wraper


# 函数
@timer
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
@timer
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
@timer
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

func_one(25000)
func_two(25000)
func_three(25000)

我们发现,使用了装逼的语法糖后,我们对每个要测试性能的函数只需要调用一次即可。使用语法糖后的装饰性和我们自己写的装饰器完全等价,只是 @ 写法更简洁一些。

如果我们想给装饰器传参数应该怎么办?比如我们想把每个要测试性能函数的函数名传给装饰器,其实很简单,我们给测试函数性能的装饰器再装饰一下就可以了。

from time import time

def timer(funcname):
    def decorate(func):
        def wraper(*args, **kwargs):
            before = time()
            rst = func(*args, **kwargs)
            after = time()
            print(funcname + "耗时:", after - before)
            return rst
        return wraper
    return decorate


# 函数
@timer("func_one函数")
def func_one(data):
    for i in range(1, data):
        data += i
    return data

# 函数 2
@timer("func_two函数")
def func_two(data):
    for i in range(1, data):
        data -= i
    return data

# 函数 3
@timer("func_three函数")
def func_three(data):
    for i in range(1, data):
        data *= i
    return data

func_one(25000)
func_two(25000)
func_three(25000)

一个函数还可以同时定义多个装饰器,装饰器的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,比如以下函数有三个装饰器,该装饰器函数等同于:f = a(b(c(f)))。

@a
@b
@c
def f():
    pass

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的 __call__ 方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法,大家学完面向对象之后,可以再看如下案例。

from time import time

class Timer(object):
    def __init__(self, func):
        self._func = func

    def __call__(self, arg):
        before = time()
        rst = self._func(arg)
        after = time()
        print("该函数耗时:", after - before)
        return rst

@Timer
def func_one(data):
    for i in range(1, data):
        data += i
    return data

func_one(25000)

本节重要知识点

会使用基本语法写装饰器。

会使用装饰器语法写装饰器。

会给装饰器传参数。

点赞:0 分享

上一篇
Python3切片
下一篇
python3闭包函数
作者头像 作者名称 作者性别
韩俊

热门推荐

1 Python3的基本数据类型
2 Python3生成器
3 Python3字符串
4 Python中hashlib模块详解
5 Python3切片
6 Python文件处理和输入输出(IO)

评论列表

取消回复

    •  
      Login

      韩俊

      趁你现在还有时间,尽你自己最大的努力,努力做成你最想做的那件事,成为你最想成为的那种人,过着你最想过的那种生活。这个世界永远比你想的要更精彩,不要败给生活。

      退出登陆
      • 10072文章
      • 455评论
      • 80微语
  • 广告赞助

  • 二零二五年05月
    一二三四五六日
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
  • 分类

    • 网站模板源码
    • IT技术笔记
    • 好资源分享
    • 共享网络资源
  • 最新文章

      • 迷雾通:免费使用的VPN软件
        • 任推邦:打造你的赚钱新途径!
          • 任推邦是正规平台吗?任推邦邀请码多少?
            • 京东外卖商户入驻指南及详细入驻攻略
              • git教程及git常用命令指南
                • 女神过生日简短祝福语
                  • 三八祝福语简短优雅点
                    • 事业女性三八祝福语简短
  • 热门文章

    • 酷狗音乐VIP账号 酷狗音乐会员账号共享2017.01.29更新
    • 芒果tv vip会员账号 芒果tv会员账号共享2017.01.29更新
    • 爱奇艺vip账号 爱奇艺/PPS会员账号共享2016.12.12更新
    • 优酷/土豆vip会员账号 优酷会员账号共享2017.01.29更新
    • 活动:免费获得爱奇艺VIP/PPS会员账号50天以上的使用权!
    • 酷我音乐VIP账号 酷我音乐会员账号共享2016.12.31更新
    • 暴风影音会员账号 暴风影音会员账号共享2016.12.31更新
    • m1905会员账号 m1905会员账号共享2016.10.21更新
  • 最新评论

    • https://www.vipshare8.com/content/templates/meta/Static/images/tx/10.jpg

      你丫的 文章写得太好了 支持下!![F3...

    • https://www.vipshare8.com/content/templates/meta/Static/images/avatar.jpg

      很棒!刚在某网站看到这个,很欣赏,可惜下...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=3861064027

      呃呃呃 oooo

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      回复了111:根据激活的时间有一年的有效期!

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=792480561

      到2025就到期了吗?

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      [blockquote]打卡时间:16:...

    • https://www.vipshare8.com/content/templates/meta/Static/images/tx/7.jpg

      俊哥,想借用迅雷一用!但需要手机验证!看...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=3293901900

      感谢大大的分享

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      回复了好奥v:如果没有解析成功,可以切换接口...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=131241242441

      现在视频解析网站怎么用不了啦?

  • 网站统计 I 当前在线:507人

    • 本站管理:1位
    • 用户总数:591位
    • 置顶文章:2篇
    • 日志总数:10072篇
    • 微语总数:80条
    • 评论总数:455条
    • 标签总数:83条
    • 页面总数:8页
    • 分类总数:14个
    • 链接总数:16条
    • 运行天数:3678天
    • 最后更新:4月27日
    • 登录
    • 注册
    • 找回
    Copyright © 2025vip共享吧网站地图 网站备案豫ICP备19004194号-1

    免责声明:本站资源仅供用于学习和交流,本站部分素材内容来源于网络,如有侵权/投稿等,请及时联系站长.

    • 首页
    • 秒懂百科 秒懂百科
    • 搜索
    • 史上今日

    大家都在搜

    • javascript教程
    • redis
    • 网赚
    • VIP电影
    • 芒果视频
    • seo
    • 乐视会员账号共享
    • 好省
    • 酷狗会员账号共享
    • 乐视视频
    • 腾讯视频会员账号
    • 哔哩哔哩vip会员账号
    • 千图网vip账号共享
    • 设计素材
    • 2345网址导航
    • pptv
    • 
    • 