python 装饰器

函数也是对象

要理解Python装饰器,首先要明白在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()

  

  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中能够经过@符号来方便的使用装饰器功能。code

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接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这能够让咱们在装饰器里面访问在装饰以前的函数的属性。blog

更规范的写法:继承

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

  

装饰器类

经过编写类的方法也能够实现装饰器,并让装饰器具有继承等面向对象中更实用的特性it

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

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类,扩展装饰器的功能:test

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
相关文章
相关标签/搜索