← 返回首页
Python3基础教程(二十八)
发表时间:2022-03-31 16:37:38
装饰器

1.一切皆对象

实例

# -*- coding: utf-8 -*-
# @Time : 2022/4/4 9:52
# @File : decoration.py
# @Software : PyCharm

def foo():
    print('i am foo')

# 打印函数
print(foo)
# 我们甚至可以将一个函数赋值给一个变量,比如
f = foo;
f()

# 函数对象有一个__name__属性,可以拿到函数的名字
print(f.__name__)

运行结果:

<function foo at 0x0000018BB35C3E20>
i am foo
foo

2.装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。

先来看一个简单例子:

def foo():
    print('i am foo')

现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码:


import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


def foo():
    print('i am foo')
    logging.info("foo is running")

foo()

运行结果:

i am foo
2022-04-04 10:03:12,378 - root - INFO - foo is running

bar()、bar2()也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志 ,日志处理完之后再执行真正的业务代码。


import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


def use_logging(func):
    logging.warning("%s is running" % func.__name__)
    func()


def foo():
    print('i am foo')


def bar():
    print('I am bar')


use_logging(foo)
use_logging(bar)

运行结果:

i am foo
I am bar
2022-04-04 10:05:41,269 - root - WARNING - foo is running
2022-04-04 10:05:41,269 - root - WARNING - bar is running

但是现在又有了新问题,调用时不得不改成use_logging(bar),use_logging(foo)。那么有没有更好的方式的呢?当然有,答案就是装饰器。

函数use_logging就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被use_logging装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。学过Java 的spring框架的同学对这个概念应该一点也不陌生。

# -*- coding: utf-8 -*-
# @Time : 2022/4/4 9:52
# @File : decoration.py
# @Software : PyCharm

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.warning("%s is running" % func.__name__)
        return func(*args, **kwargs)

    return wrapper

# @符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作
@use_logging
def foo():
    print('i am foo')


@use_logging
def bar():
    print('I am bar')


foo()
bar()

装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。