Logo

Python装饰器

Avatar

Skyone

科技爱好者

装饰器(Decorators)是 Python 的一个重要部分。举一个不太恰当的比方,装饰器是一个函数,它以函数为参数,先执行一些操作,再调用作为参数的函数,然后再执行以下操作,例如下面的函数:

def my_decorator(func):
    print("Do something before call func.")
    func() # 调用func()
    print("Do something after call func.")

没错,就像C语言里的回调函数一样

可以这样使用它:

def greet():
    print("Hello!")

my_decorator(greed) # 注意这里不是调用 greed
"""输出
Do something before call func.
Hello
Do something after call func.
"""

然而,python作为一个优雅的语言,使用了特殊语法简化了上面的操作,下面的例子和上面的是等效的:

def my_decorator(func):
    print("Do something before call func.")
    func() # 调用func()
    print("Do something after call func.")

@my_decorator # 也不是调用
def greet():
    print("Hello!")

greet
"""输出
Do something before call func.
Hello
Do something after call func.
"""

装饰器的使用

适配带有参数的函数

前面的最简单的装饰器不能修饰带有参数的函数,而且修饰后的函数不能做参数之类的(想一想为什么)

其实也很容易解决,既然函数可以作为另一个函数的参数,那么它可不可以作为返回值呢?

当然可以!这是因为python一切皆对象,函数和变量究其本质都是对象我怎么联想到了Linux一切皆文件@_@

现在我们将其修改一下:

def a_new_decorator(func):
    # python 可以在函数里再定义函数,即嵌套函数
    def wrapped(*args, **kwargs):
        print("Do something before call func.")
        func(*args, **kwargs)
        print("Do something after call func.")

    return wrapped


@a_new_decorator
def greet():
    print("Hello!")


greet()

保持函数名

现在我们在上面的代码后面再加一行:

print(greet.__name__)
"""输出
wrapped
"""

哦不,这不是我们想要的结果,函数名被装饰器改写了!

幸好functiontoolswarps装饰器可以解决这个问题,再改!

from functools import wraps


def a_new_decorator(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        print("Do something before call func.")
        func(*args, **kwargs)
        print("Do something after call func.")

    return wrapped


@a_new_decorator
def greet():
    print("Hello!")


print(greet.__name__)
"""输出
greet
"""

带参数的装饰器

上例中wraps也是装饰器,但他为什么要加括号调用,还可以添加参数?

先看看这个:

def a():
    def b():
        def c():
            def d():
                return 1

            return d

        return c

    return b


response = a()()()()

# response = 1

这样来看就很简单了,就是再嵌套一个函数嘛。。

有了这个思路,再改!

from functools import wraps

def final_decorator(output_filename="greet.txt"):
    def a_new_decorator(func):
        @wraps(func)
        def wrapped(*args, **kwargs):
            with open(output_filename, "a") as file:
                greet_str = func(*args, **kwargs)
                file.write(greet_str)
            return greet_str

        return wrapped

    return a_new_decorator

@final_decorator("233.txt")
def greet(name):
    return "Hello " + name

print(greet("skyone"))
"""输出
Hello skyone
"""
"""文件`123.txt`
Hello skyone
"""

花样写日志

函数做装饰器

from functools import wraps


def logger(logfile="out.log", callback=None):
    def logger_decorator(func):
        @wraps(func)
        def wrapped_func(*args, **kwargs):
            log_string = "[logger] function <" + func.__name__ + "> was called"
            print(log_string)
            with open(logfile, "a", encoding="utf-8") as file:
                file.write(log_string + "\n")
            if callback is not None:
                callback()
            return func(*args, *kwargs)
        return wrapped_func
    return logger_decorator


@logger()
def a():
    return 3


print(a())

类做装饰器

from functools import wraps


class Logger:
    def __init__(self, logfile="out.log", callback=None):
        self.logfile = logfile
        self.callback = callback

    def __call__(self, func):
        @wraps(func)
        def wrapped_func(*args, **kwargs):
            log_string = "[logger] function <" + func.__name__ + "> was called"
            print(log_string)
            with open(self.logfile, "a", encoding="utf-8") as file:
                file.write(log_string + "\n")
            if self.callback is not None:
                self.callback()
            self.notify()
            return func(*args, **kwargs)
        return wrapped_func

    def notify(self):
        pass


@logger()
def a():
    return 3


print(a())

这样做的好处是可以改写,而且比嵌套函数的方式更加整洁,下面的例子来自菜鸟教程

创建一个子类:

class email_logger(Logger):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='[email protected]', *args, **kwargs):
        self.email = email
        super(email_logger, self).__init__(*args, **kwargs)

    def notify(self):
        # 发送Email的实现···
        pass

隐私政策

Copyright © Skyone 2025