老生常谈Python进阶之装饰器

发布时间 - 2026-01-11 01:04:18    点击率:

函数也是对象

要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,函数体中也可以再定义函数。

装饰器本质

可以通过编写一个纯函数的例子来还原装饰器所要做的事。

def decorator(func):
  
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap


def fun_test():
  print("func")


fun_test = decorator(fun_test)
fun_test()

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()

fun_test所指向的函数的引用传递给decorator()函数

decorator()函数中定义了wrap()子函数,这个子函数会调用通过func引用传递进来的fun_test()函数,并在调用函数的前后做了一些其他的事情

decorator()函数返回内部定义的wrap()函数引用

fun_test接收decorator()返回的函数引用,从而指向了一个新的函数对象

通过fun_test()调用新的函数执行wrap()函数的功能,从而完成了对fun_test()函数的前后装饰

Python中使用装饰器

在Python中可以通过@符号来方便的使用装饰器功能。

def decorator(func):
  
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap

@decorator
def fun_test():
  print("func")


fun_test()

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()

装饰的功能已经实现了,但是此时执行:

 

print(fun_test.__name__)

# Output:
# wrap

 fun_test.__name__已经变成了wrap,这是应为wrap()函数已经重写了我们函数的名字和注释文档。此时可以通过functools.wraps来解决这个问题。wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

更规范的写法:

from functools import wraps

def decorator(func):
  @wraps(func)
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap


@decorator
def fun_test():
  print("func")


fun_test()
print(fun_test.__name__)

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
# fun_test

带参数的装饰器

通过返回一个包裹函数的函数,可以模仿wraps装饰器,构造出一个带参数的装饰器。

from functools import wraps

def loginfo(info='info1'):
  def loginfo_decorator(func):
    @wraps(func)
    def wrap_func(*args, **kwargs):
      print(func.__name__ + ' was called')
      print('info: %s' % info)
      
      return func(*args, **kwargs)
    return wrap_func
  return loginfo_decorator
  
@loginfo()
def func1():
  pass
  
func1()

# Output:
# func1 was called
# info: info1

@loginfo(info='info2')
def func2():
  pass

func2()
# Output:
# func2 was called
# info: info2

装饰器类

通过编写类的方法也可以实现装饰器,并让装饰器具备继承等面向对象中更实用的特性

首先编写一个装饰器基类:

from functools import wraps

class loginfo:
  def __init__(self, info='info1'):
    self.info = info
    
  def __call__(self, func):
    @wrap
    def wrap_func(*args, **kwargs):
      print(func.__name__ + ' was called')
      print('info: %s' % self.info)
      
      self.after()  # 调用after方法,可以在子类中实现
      return func(*args, **kwargs)
    return wrap_func

  def after(self):
    pass


@loginfo(info='info2')
def func1():
  pass
  
# Output:
# func1 was called
# info: info1

再通过继承loginfo类,扩展装饰器的功能:

class loginfo_after(loginfo):
  def __init__(self, info2='info2', *args, **kwargs):
    self.info2 = info2
    super(loginfo_after, self).__init__(*args, **kwargs)

  def after(self):
    print('after: %s' % self.info2)


@loginfo_after()
def func2():
  pass

func2()
  
# Output:
# func2 was called
# info: info1
# after: info2

以上这篇老生常谈Python进阶之装饰器就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


# python  # 装饰器  # python装饰器使用实例详解  # python 一篇文章搞懂装饰器所有用法(建议收藏)  # python类装饰器用法实例  # Python装饰器模式定义与用法分析  # python中装饰器级连的使用方法示例  # python如何实现不用装饰器实现登陆器小程序  # python装饰器代替set get方法实例  # 可以通过  # 给大家  # 进阶  # 这是  # 文档  # 让我们  # 老生常谈  # 其他的  # 子函数  # 并在  # 希望能  # 要做  # 写了  # 作为一个  # 可以实现  # 中也  # 这篇  # 这可  # 小编  # 解决这个问题 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?  Laravel怎么连接多个数据库_Laravel多数据库连接配置  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  高性价比服务器租赁——企业级配置与24小时运维服务  如何快速查询域名建站关键信息?  网站制作价目表怎么做,珍爱网婚介费用多少?  如何在局域网内绑定自建网站域名?  JS中对数组元素进行增删改移的方法总结  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法  三星、SK海力士获美批准:可向中国出口芯片制造设备  Laravel如何使用Sanctum进行API认证?(SPA实战)  Python自然语言搜索引擎项目教程_倒排索引查询优化案例  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  微信h5制作网站有哪些,免费微信H5页面制作工具?  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  如何用y主机助手快速搭建网站?  如何在腾讯云免费申请建站?  js实现获取鼠标当前的位置  如何快速选择适合个人网站的云服务器配置?  如何快速查询网址的建站时间与历史轨迹?  海南网站制作公司有哪些,海口网是哪家的?  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  使用spring连接及操作mongodb3.0实例  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  如何确保FTP站点访问权限与数据传输安全?  Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】  如何在 React 中条件性地遍历数组并渲染元素  百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏  Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】  网站页面设计需要考虑到这些问题  如何快速生成ASP一键建站模板并优化安全性?  如何安全更换建站之星模板并保留数据?  详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南  Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层  Laravel怎么实现一对多关联查询_Laravel Eloquent模型关系定义与预加载【实战】  Laravel如何实现API资源集合?(Resource Collection教程)  移动端脚本框架Hammer.js  详解Android图表 MPAndroidChart折线图  北京网站制作的公司有哪些,北京白云观官方网站?  香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?  Win11怎么开启自动HDR画质_Windows11显示设置HDR选项  Python文本处理实践_日志清洗解析【指导】  如何基于PHP生成高效IDC网络公司建站源码?  Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置  b2c电商网站制作流程,b2c水平综合的电商平台?