09 装饰器

目录链接:https://www.jianshu.com/p/e1e201bea601

函数核心

  • 一、在 Python 中, 函数是一等公民(first-class citizen) , 函数也是对象。 我们可以把函数赋予变量
def func(message):
    print('Got a message: {}'.format(message))
    
send_message = func  # 把函数赋予变量
send_message('hello world')
09 装饰器_第1张图片
把函数赋予变量.png
  • 二、可以把函数当作参数, 传入另一个函数中
def get_message(message):
    return 'Got a message: ' + message
def root_call(func, message):
    print(func(message))
root_call(get_message, 'hello world') # 把函数当作参数
09 装饰器_第2张图片
函数当作参数.png
  • 三、可以在函数中定义函数, 也就是函数的嵌套
def func(message):
    def get_message(message): # 在函数中定义函数
        print('Got a message: {}'.format(message))
    return get_message(message)
func('hello world')
09 装饰器_第3张图片
在函数中定义函数.png
  • 四、函数的返回值也可以是函数对象(闭包)
def func_closure():
    def get_message(message): # 可以在函数中定义函数
        print('Got a message: {}'.format(message))
    return get_message # 函数的返回值也可以是函数对象(闭包)
send_message = func_closure() 
send_message('hello world')
09 装饰器_第4张图片
函数的返回值也可以是函数对象(闭包).png

简单的装饰器

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper
def greet():
    print('hello world')
greet = my_decorator(greet)
greet()
09 装饰器_第5张图片
简单的装饰器.png

变量 greet 指向了内部函数 wrapper(),而内部函数 wrapper() 中又会调用原函数greet(), 因此, 最后调⽤ greet() 时, 就会先打印 'wrapper of decorator' , 然后输出 'helloworld' 。

函数 my_decorator() 就是⼀个装饰器, 它把真正需要执行的函数 greet() 包裹在其中, 并且改变了它的⾏为, 但是原函数 greet() 不变。

更简单、 更优雅的表示:

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper
@my_decorator
def greet():
    print('hello world')
greet()

@my_decorator 就相当于前面的 greet=my_decorator(greet) 语句, 只不过更加简洁

带有参数的装饰器

原函数 greet() 有参数需要传递给装饰器,一个简单的办法 是在对应的装饰器函数 wrapper() 上, 加上相应的参数。

def my_decorator(func):
    def wrapper(message): # 带有参数的装饰器
        print('wrapper of decorator')
        func(message)
    return wrapper
@my_decorator
def greet(message): # 带有参数的装饰器
    print(message)
greet('hello world')
09 装饰器_第6张图片
带有参数的装饰器.png

这样处理有个问题,如果我另外还有个函数,也需要使用 my_decorator() 装饰器, 但是这个新的函数有两个参数。

通常这种情况下会把 *args 和 **kwargs , 作为装饰器内部函数 wrapper() 的参数。 *args 和 **kwargs , 表示接受任意数量和类型的参数。

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper

带有自定义参数的装饰器

装饰器可以接受原函数任意类型和数量的参数, 除此之外, 它还可以接受自己定义的参数。

def repeat(num): # 带有自定义参数的装饰器
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator
# 带有自定义参数的装饰器
@repeat(4) 
def greet(message):
    print(message)
greet('hello world')
09 装饰器_第7张图片
带有自定义参数的装饰器.png

原函数还是原函数吗?

def repeat(num): 
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator
@repeat(4) 
def greet(message):
    print(message)
print('greet.__name__ = ',greet.__name__)
help(greet)
09 装饰器_第8张图片
原函数还是原函数吗01.png

greet() 函数被装饰以后, 它的元信息变了。 元信息告诉我们“它不再是以前的那个greet() 函数,而是被 wrapper() 函数取代了”。

为了解决这个问题, 通常使用内置的装饰器 @functools.wrap , 它会帮助保留原函数的元信息(也就是将原函数的元信息, 拷贝到对应的装饰器函数中) 。

import functools
def repeat(num): 
    def my_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator
@repeat(4) 
def greet(message):
    print(message)
print('greet.__name__ = ',greet.__name__)
help(greet)
09 装饰器_第9张图片
原函数还是原函数吗02.png

类装饰器

类也可以作为装饰器。 类装饰器主要依赖于函数 _call() , 每当你调用一个类的实例时, 函数 call() 就会被执行一次。

class Count:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs): # 类装饰器
        self.num_calls += 1
        print('num of calls is: {}'.format(self.num_calls))
        return self.func(*args, **kwargs)
@Count
def example():
    print("hello world")
example()
example()
09 装饰器_第10张图片
类装饰器.png

装饰器的嵌套

Python 也支持多个装饰器,比如写成下面这样的形式:

@decorator1
@decorator2
@decorator3
def func():
    ...

其执行顺序是从里到外,等价于

decorator1(decorator2(decorator3(func)))

举个例子:

import functools
def my_decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator1')
        func(*args, **kwargs)
    return wrapper
def my_decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator2')
        func(*args, **kwargs)
    return wrapper
@my_decorator1
@my_decorator2
def greet(message):
    print(message)
greet('hello world')
09 装饰器_第11张图片
装饰器的嵌套.png

参考资料:

极客时间 Python核心技术与实战学习

Python核心技术与实战(极客时间)链接:
http://gk.link/a/103Sv


GitHub链接:
https://github.com/lichangke/LeetCode

知乎个人首页:
https://www.zhihu.com/people/lichangke/

个人首页:
https://www.jianshu.com/u/3e95c7555dc7

个人Blog:
https://lichangke.github.io/

欢迎大家来一起交流学习

你可能感兴趣的:(09 装饰器)