装饰器是Python的一大特点,虽然其他语言也可以通过各种设计模式实现装饰器的功能,但是Python无疑是最简洁的.

一切皆对象

Python将一切东西都看做是对象,也包括函数,这说明函数可以像变量那样随意传递.

In [1]:
def hi():
    return "hi Python!"

def doSomethingBeforeHi(func):
    print("我在 hi() 之前运行")
    print(func())

doSomethingBeforeHi(hi)
我在 hi() 之前运行
hi Python!

第一个装饰器

In [2]:
def a_new_decorator(a_func):

    def wrapTheFunction():
        print("在函数之前做点事情")

        a_func()

        print("在函数之后做点事情")

    return wrapTheFunction

def a_function_requiring_decoration():
    print("我是一个函数")

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

a_function_requiring_decoration()
在函数之前做点事情
我是一个函数
在函数之后做点事情

发现没有,原本a_function_requiring_decoration只打印一句话,但是经过a_new_decorator的包装,在a_function_requiring_decoration的前面和后面分别执行了一些内容.

不仅如此,Python可以以更加简洁的方式执行上述的操作.

In [3]:
@a_new_decorator
def a_function_requiring_decoration():
    """Hey you! Decorate me!"""
    print("我是一个函数")

a_function_requiring_decoration()
在函数之前做点事情
我是一个函数
在函数之后做点事情

这用处可大了去了.比如说:

  • 函数运行之前可以检查一下权限
  • 函数运行之后可以做日志
  • 函数运行之前可以做一些初始化操作
  • 函数运行之后可以做一些垃圾清理
  • 函数运行前后都可以做日志,这是非常好的调试和监控手段.

根据业务需求的不同,装饰器的应用场景可以非常丰富.

下面举一个权限检测的例子,定义装饰器:

In [4]:
from functools import wraps

def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

在调用装饰器后,使用.__name__打印函数名字会显示为装饰器的名字,我们希望调用装饰器后还是显示原函数的信息,这里的@wraps(func)就是将logit的信息改为func的信息.

使用装饰器做日志:

In [5]:
@logit
def test1(x):
    return x + x

@logit
def test2(x):
    return x**2

res = test1(4)
res = test2(4)
test1 was called
test2 was called

带参数的装饰器

上面的装饰器是没有参数的,那么装饰器可以有参数吗?当然可以,之前的@wraps(func)就是一个带参数的装饰器.

如果需要装饰器传参数可以创建一个包裹函数:

In [6]:
from functools import wraps

def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            # 现在将日志打到指定的logfile
            with open(logfile, 'a') as opened_file:
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
In [7]:
@logit()
def myfunc1():
    pass

myfunc1()
In [8]:
@logit(logfile='func2.log')
def myfunc2():
    pass

myfunc2()

装饰器类

类同样可以用作装饰器:

In [9]:
from functools import wraps

class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile

    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 现在将日志打到指定的文件
            with open(self.logfile, 'a') as opened_file:
                opened_file.write(log_string + '\n')
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function

    def notify(self):
        # logit只打日志,不做别的
        pass
In [10]:
@logit()
def myfunc1():
    pass

可以看到类作为装饰器比函数作为装饰器更加整洁.

posted @ 2019-01-29 11:35:36
评论加载中...

发表评论