五分钟理解Python装饰器,让代码更优雅

开发
通过掌握基础装饰器、参数化装饰器、装饰器链,以及实现特定功能如缓存的高级应用,你将能够编写出更加优雅和高效的Python程序。

什么是装饰器?

想象一下,你有一个漂亮的房间,但你想让它看起来更加特别,于是你决定挂上一些装饰画。在编程的世界里,Python装饰器就是那些“装饰画”,它们能让你的函数或类“房间”看起来或工作得更加出色,而无需改变其内部结构。

def say_hello(name):
    print(f"Hello, {name}!")

# 这就是我们的装饰画
def fancy_decorator(func):
    def wrapper():
        print("Adding some fancy touch...")
        func()
        print("Decoration ends here.")
    return wrapper

# 挂上装饰画
fancy_say_hello = fancy_decorator(say_hello)

fancy_say_hello("World")

输出:

Adding some fancy touch...
Hello, World!
Decoration ends here.

这段代码中,fancy_decorator就是一个装饰器,它包装了原始的say_hello函数,给它加上了前后打印信息的功能。

装饰器的魔法

装饰器的“魔法”在于它们使用了函数的高阶特性,即函数可以接受另一个函数作为参数,并返回一个新的函数。当你在函数定义前使用@符号,实际上是执行了一个赋值操作,将装饰器的结果赋予了原函数名。

@fancy_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

输出:

Adding some fancy touch...
Hello, Alice!
Decoration ends here.

这里,@fancy_decorator等同于手动执行了say_hello = fancy_decorator(say_hello),使得每次调用say_hello时,都会先经过fancy_decorator的处理。

参数化的装饰器

有时候,装饰器也需要根据不同的情况调整行为,这就需要参数化的装饰器。

def repeat(n):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(3)
def greet(name):
    print(f"Hi, {name}")

greet("Bob")

输出:

Hi, Bob
Hi, Bob
Hi, Bob

这里,repeat是一个参数化的装饰器工厂,它接收一个参数n,并返回一个具体的装饰器,这个装饰器会让被装饰的函数重复执行n次。

高级装饰器技巧:带参数的进阶

之前提到的参数化装饰器,其实还可以做得更灵活。如果想要装饰器本身能够接收多个参数,可以通过额外的闭包层来实现。

def with_args(arg1, arg2):
    def decorator_with_args(func):
        def wrapper(*args, **kwargs):
            print(f"Using arguments: {arg1}, {arg2}")
            return func(*args, **kwargs)
        return wrapper
    return decorator_with_args

@with_args('A', 'B')
def show_args():
    print("Inside the function")

show_args()

输出:

Using arguments: A, B
Inside the function

这里,with_args不仅能够装饰函数,还能在装饰时接收参数,实现了高度的定制化。

装饰器链:复合装饰器的力量

装饰器可以堆叠使用,形成装饰器链,为函数添加多层功能。

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} is called")
        return func(*args, **kwargs)
    return wrapper

def timer_decorator(func):
    import time
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time:.2f} seconds")
        return result
    return wrapper

@log_decorator
@timer_decorator
def example_function(n):
    sum(range(n))

example_function(10000)

输出示例:

Function example_function is called
example_function took 0.00 seconds

通过这种方式,我们可以轻松地为函数添加日志记录、性能测试等多种功能,且各装饰器之间的功能相互独立,易于维护。

实战案例:缓存机制

让我们来看一个实用场景——使用装饰器实现函数结果的缓存,以提高效率。

def cache_decorator(func):
    cache = {}
    
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    
    return wrapper

@cache_decorator
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))
print(fibonacci(10)) # 第二次调用应该更快

在这个例子中,cache_decorator装饰器用于存储fibonacci函数的计算结果,避免重复计算,显著提高了递归计算斐波那契数列的效率。

总结与实践技巧

装饰器是Python编程中的高级技巧,它们让代码更加简洁、模块化。通过掌握基础装饰器、参数化装饰器、装饰器链,以及实现特定功能如缓存的高级应用,你将能够编写出更加优雅和高效的Python程序。

  • 练习技巧:尝试为自己的函数编写装饰器,比如计时装饰器,用于监控函数执行时间。
  • 方法提示:在设计装饰器时,考虑其通用性和可扩展性,以便在多个场景下重用。
  • 使用技巧:注意装饰器的执行顺序是从下往上,这影响着函数最终的行为。
  • 注意事项:对于有状态的装饰器,要小心管理状态,避免在并发环境下产生意料之外的行为。
责任编辑:赵宁宁 来源: PythonAI与图像处理
相关推荐

2024-09-03 09:47:09

闭包装饰器Python

2011-08-08 09:22:10

Python

2024-09-18 08:21:24

JavaScriptTypeScriptprototype

2009-11-06 16:05:37

WCF回调契约

2023-07-26 07:11:50

LVM底层抽象

2024-02-21 21:19:18

切片Python语言

2020-04-03 14:55:39

Python 代码编程

2024-07-12 14:21:32

Python编码

2009-10-29 11:39:52

ADO.NET连接池

2023-12-06 08:48:36

Kubernetes组件

2009-11-05 10:56:31

WCF通讯

2024-07-10 18:55:09

Python定时

2024-07-03 08:13:56

规则执行器代码

2009-11-16 11:30:55

PHP上传文件代码

2022-03-08 06:41:35

css代码

2017-12-20 09:42:39

PythonNginx日志

2009-11-04 16:13:18

ADO.NET数据表

2020-05-12 09:10:24

浏览器服务器网络

2009-11-16 10:53:30

Oracle Hint

2024-12-11 07:00:00

面向对象代码
点赞
收藏

51CTO技术栈公众号