欢迎访问宙启技术站
智能推送

如何使用装饰器定义函数?

发布时间:2023-06-01 01:25:39

装饰器是在 Python 语言中实现元编程的一种方法。简单地说,装饰器是一个函数,可以接受另一个函数或类作为参数,并返回新的函数或类,以对原始函数或类进行修改或扩展。

在 Python 中,装饰器使用 @ 符号进行标记,可以对函数或类进行修饰,以提高代码的可复用性和可维护性。下面我们将详细介绍如何使用装饰器来定义函数。

1. 使用装饰器的基本语法

在 Python 中,装饰器定义有一个固定的结构:

def decorator_name(func):
    def wrapper(*args, **kwargs):
        # code to be executed before the decorated function is called
        res = func(*args, **kwargs)
        # code to be executed after the decorated function is called
        return res
    return wrapper

@decorator_name
def my_func():
    # function code here

这里,装饰器的定义包含了一个被传入原函数作为参数的内部函数 wrapper,这个函数可以在调用原函数前后执行一些操作。例如,我们可以在 wrapper 中记录函数运行时间、检查函数输入参数或者输出结果等等。

然后,使用 @ 符号将装饰器添加到原函数前面,这相当于将原函数作为参数传入装饰器函数进行修饰并返回新的函数,然后再将新的函数重新赋值给原函数名。

2. 装饰一个无参数函数

接下来,我们实现一个简单的装饰器,用来记录程序的运行时间。这个装饰器可以使用装饰器语法来装饰无参数的函数,比如下面的 demo:

import time

def timer(func):
    def wrapper():
        start_time = time.time()
        res = func()
        end_time = time.time()
        print("Time taken by function {}: {} seconds".format(func.__name__, end_time - start_time))
        return res
    return wrapper

@timer
def demo():
    for i in range(5000000):
        pass

demo()

这个程序使用 time 库记录了 demo 函数的运行时间,在函数运行前和运行后打印了时间差。在调用 demo 函数时,会自动加上装饰器,这样就可以在计算运行时间的同时,保持原函数不变。我们可以看到,使用装饰器并不影响 demo 函数的调用和功能。

3. 装饰一个带参数的函数

装饰带有参数的函数需要注意参数的传递。一种简单的方法是将装饰器的 wrapper 函数定义成可变参数的形式,同时对原函数和其传入的参数进行包装,比如下面这个例子:

def logger(func):
    def wrapper(*args, **kwargs):
        print("Arguments were: {}, {}".format(args, kwargs))
        res = func(*args, **kwargs)
        print("Output was: {}".format(res))
        return res
    return wrapper

@logger
def add(x, y):
    return x + y

add(3, 4)

这个程序使用 logger 装饰器来打印程序输入和输出的值,同时保持原 add 函数不变。我们可以看到,在调用 add 函数时,logger 装饰器自动包装了输入参数,并将输入和输出值打印出来。

4. 装饰一个类

装饰类可以用来添加属性或重写方法,也可以用来添加方法。下面是一个示例程序:

def logger(cls):
    for name, value in vars(cls).items():
        if callable(value):
            setattr(cls, name, addlog(value))
    return cls

def addlog(func):
    def wrapper(*args, **kwargs):
        print("{} was called with args: {}, kwargs: {}".format(func.__name__, args, kwargs))
        return func(*args, **kwargs)
    return wrapper

@logger
class Demo:
    def __init__(self, x):
        self.x = x
    
    def method1(self, y):
        return self.x + y
    
    def method2(self, z):
        return self.x * z

d = Demo(10)
d.method1(5)
d.method2(3)

这个程序定义了 logger 装饰器,可以用来自动添加日志记录功能。在 logger 装饰器中遍历 Demo 类的属性,如果属性是一个方法,则将其重写为经过 addlog 装饰器装饰之后的新方法。addlog 装饰器用来记录函数调用,并在函数返回时记录返回值。

在 Demo 类中,我们定义了两个方法 method1 和 method2,和一个初始化函数 __init__。使用 @logger 对 Demo 类进行修饰之后,所有方法和属性都可以自动记录日志。在调用 Demo 的方法时,会自动记录日志并输出函数调用的输入和输出值。

5. 普通函数装饰器和类装饰器的区别

普通函数装饰器和类装饰器在使用上没有固定的限制,一般来说,如果装饰器需要重新定义函数或类的属性或方法,则使用类装饰器,否则使用普通函数装饰器。

类装饰器使用上相对复杂一些,需要用到类的一些特殊方法,同时需要注意装饰器返回的是类还是方法。在使用类装饰器时,必须让它返回一个修改后的类对象,否则原始类将不受影响。而普通函数装饰器只需要返回一个修改后的函数即可。

总之,装饰器是 Python 中极为强大和灵活的元编程工具,它可以大大提高代码的可维护性和可复用性,同时也使代码更加清晰简洁。通过对装饰器的使用和理解,我们可以更好地进行程序设计和维护。