老生常谈Python进阶之装饰器

作者:jingxian 时间:2022-05-20 10:51:23 

函数也是对象

要理解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,装饰器
0
投稿

猜你喜欢

  • pytorch实现CNN卷积神经网络

    2023-07-04 20:23:06
  • Python paramiko 模块浅谈与SSH主要功能模拟解析

    2023-10-01 11:06:28
  • 如何过滤中国站长站(chianz)文章干扰码

    2008-01-04 20:14:00
  • 在JavaScript中,为什么要尽可能使用局部变量?

    2009-03-01 12:38:00
  • Python语言的12个基础知识点小结

    2023-08-23 10:12:07
  • 泛泛而谈界面视觉效果的一致性

    2010-01-05 17:05:00
  • Python常用字符串替换函数strip、replace及sub用法示例

    2022-04-07 18:21:17
  • 在python中如何建立一个自己的包

    2022-12-27 12:42:25
  • python+selenium实现12306模拟登录的步骤

    2021-06-18 15:32:56
  • 使用ASP脚本命令重新启动服务器

    2008-10-10 11:53:00
  • 微信小程序上传图片功能(附后端代码)

    2023-07-24 04:21:40
  • 从 msxml6.dll 中获取 DOMDocument 对象的方法与属性

    2009-02-22 18:46:00
  • Asp DatePart 函数的语法详解(用于计算日期并返回指定的时间间隔)

    2012-12-04 20:04:29
  • DreamweaverMX2004的一句话技巧

    2009-05-22 18:23:00
  • python 自动化偷懒的四个实用操作

    2023-11-19 08:49:48
  • Python translator使用实例

    2021-02-09 22:40:24
  • 详解Python中for循环的使用

    2023-06-13 13:55:40
  • 一文带你学会使用PHP接口

    2023-06-07 18:40:13
  • python实现从ftp服务器下载文件的方法

    2023-08-02 20:50:54
  • 设计高效的faq页面

    2011-06-10 13:13:00
  • asp之家 网络编程 m.aspxhome.com