Python进阶之装饰器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python进阶之装饰器相关的知识,希望对你有一定的参考价值。

    

函数也是对象

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

装饰器本质

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

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


fun_test = decorator(fun_test)
fun_test()# Output:# Doing someting before executing func()# func# Doing someting after executing func()
  1. fun_test所指向的函数的引用传递给decorator()函数

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

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

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

  5. 通过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 [email protected] 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 wrapsdef decorator(func):    @wraps(func)
    def wrap():
        print("Doing someting before executing func()")
        func()
        print("Doing someting after executing func()")    return [email protected] 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 wrapsdef 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: [email protected](info=‘info2‘)def func2():
    passfunc2()# Output:# func2 was called# info: info2

装饰器类

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

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

from functools import wrapsclass 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):
        [email protected](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():
    passfunc2()    
# Output:# func2 was called# info: info1# after: info2


以上是关于Python进阶之装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python之函数的进阶闭包装饰器

Python - 函数进阶之装饰器

python进阶之装饰器之2.定义一个可接受参数的装饰器如何定义一个属性可由用户修改的装饰器定义一个能接受可选参数的装饰器

Python进阶之[非局部变量,闭包,装饰器]

Python 之 进阶学习

python函数四(装饰器进阶)