独家 | 让你的代码事半功倍——Python装饰器

liftword3周前 (12-04)技术文章16
作者:  Ayush Thakur
翻译:王可汗校对:潘玏妤

本文约2400字,建议阅读5分钟

本文介绍了用Python装饰器的基本概念和示例。


大家好,欢迎来到我的博客!今天我将与大家分享一些神奇的Python装饰器,它们可以将你的代码量减少一半。听起来太好了,不过这是真的,对吧?那么,让我向你展示它们是如何工作的,以及为什么你应该在你的项目中使用它们。


什么是Python装饰器?


Python装饰器是一种强大的功能,它允许你修改函数或类的行为,而不更改其源代码。实质上,装饰器是接受另一个函数作为参数并返回一个包装原始函数的新函数。这样,你可以在不修改原始函数的情况下,添加一些额外的功能或逻辑。


举个例子,假设你有一个函数,它将消息打印到控制台:


def hello():
    print("Hello, world!")


现在,假设你想要计算执行该函数所需的时间。你可以编写另一个函数,使用time模块来计算执行时间,然后调用原始函数:


importtime


def measure_time(func):
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(f"Execution time: {end - start} seconds")
    return wrapper


请注意,measure_time函数返回另一个名为wrapper的函数,这个函数是原始函数的修改版本。wrapper函数有两个作用:记录执行的开始时间和结束时间,以及调用原始函数。


现在,要使用这个函数,你可以这样做:


hello = measure_time(hello)
hello()


这将输出如下内容:


Hello, world!
Execution time: 0.000123456789 seconds


正如你所看到的,我们成功地添加了一些额外的功能到hello函数中,而无需更改其代码。然而,使用装饰器有一种更优雅且更简洁的方式来实现这一点。装饰器只是使用`@`符号将一个函数应用到另一个函数的语法糖。例如,我们可以将上述代码重写为:


@measure_time
def hello():
print("Hello, world!")
hello()


这将产生与之前相同的输出结果,但需要的代码更少。@measure_time这一行等同于hello = measure_time(hello),但看起来更简洁和易读。


为什么要使用python装饰器呢?


使用Python装饰器的好处有很多,例如:


¨它们允许你重复使用代码,避免冗余。例如,如果你有很多需要计算执行时间的函数,你可以简单地将相同的装饰器应用到它们上,而不是一遍遍地编写相同的代码。


¨它们允许你将关注点分离,并遵循单一职责原则。例如,如果你有一个执行一些复杂计算的函数,你可以使用装饰器处理日志记录、错误处理、缓存或输入输出验证,而不会将主要逻辑混杂其中。


¨它们允许你在不修改源代码的情况下扩展现有函数或类的功能。例如,如果你正在使用第三方库提供的一些有用的函数或类,但你想要为它们添加一些额外的功能或行为,你可以使用装饰器来包装它们,并按需进行自定义。


一些Python装饰器的例子


Python中有许多内置的装饰器,例如@staticmethod、@classmethod、@property、@functools.lru_cache、@functools.singledispatch等。你还可以为不同的目的创建自己的自定义装饰器。以下是一些可以帮助你精简代码的Python装饰器示例:


1.@timer装饰器


这个装饰器类似于之前我们见过的@measure_time装饰器,但它可以应用于任何接受任意数量参数并返回任意值的函数。它还使用functools.wraps装饰器来保留原始函数的名称和文档字符串。以下是代码示例:


import time
from functools import wraps


def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Execution time of {func.__name__}: {end - start} seconds")
        return result
    return wrapper


现在,您可以使用此装饰器来测量任何函数的执行时间,例如:


@timer
def factorial(n):
"""Returns the factorial of n"""
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
@timer
def fibonacci(n):
"""Returns the nth Fibonacci number"""
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))


这将输出如下内容:


Execution time of factorial: 1.1920928955078125e-06 seconds
3628800
Execution time of fibonacci: 0.000123456789 seconds
55


2.@debug装饰器


这个装饰器用于调试目的,因为它会打印包装的函数的名称、参数和返回值。它还使用了functools.wraps装饰器来保留原始函数的名称和文档字符串。以下是代码示例:


from functools import wraps
def debug(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args} and kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned: {result}")
return result
return wrapper


现在,您可以使用此装饰器来调试任何函数,例如:


@debug
def add(x, y):
"""Returns the sum of x and y"""
return x + y
@debug
def greet(name, message="Hello"):
"""Returns a greeting message with the name"""
return f"{message}, {name}!"
print(add(2, 3))
print(greet("Alice"))
print(greet("Bob", message="Hi"))


这将输出如下内容:


Calling add with args: (2, 3) and kwargs: {}
add returned: 5
5
Calling greet with args: ('Alice',) and kwargs: {}
greet returned: Hello, Alice!
Hello, Alice!
Calling greet with args: ('Bob',) and kwargs: {'message': 'Hi'}
greet returned: Hi, Bob!
Hi, Bob!


3.@memoize装饰器


这个装饰器对于优化递归或耗时函数的性能非常有用,因为它会缓存先前调用的结果,并在再次传递相同参数时返回它们。它还使用functools.wraps装饰器来保留原始函数的名称和文档字符串。以下是代码示例:


from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
else:
result = func(*args)
cache[args] = result
return result
return wrapper


现在,您可以使用此装饰器来对任何函数进行记忆化处理,例如:


@memoize
def factorial(n):
"""Returns the factorial of n"""
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
@memoize
def fibonacci(n):
"""Returns the nth Fibonacci number"""
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))


这将输出与之前相同的结果,但执行时间更快,因为结果被缓存并重复使用。


结论


Python装饰器是一种强大而优雅的方式,可以在不更改函数或类的源代码的情况下修改它们的行为。它们可以帮助您减少代码量,改善代码可读性,重复使用代码,分离关注点以及扩展现有代码的功能。希望您喜欢这篇博客文章并学到了一些新知识。如果您有任何问题或意见,请随时在下方留言。并且不要忘记与您的朋友和同事们分享这篇文章,他们可能对学习更多关于Python装饰器的知识感兴趣。感谢您的阅读!


原文标题:

Python Decorators That Can Reduce Your Code By Half

原文链接:

https://medium.com/@ayush-thakur02/python-decorators-that-can-reduce-your-code-by-half-b19f673bc7d8

相关文章

Rich命令行美化神器:Python终端输出必备工具

Rich命令行美化神器:Python终端输出必备工具,让你的命令行程序瞬间高大上,展示效果惊人!大家好,我是蜗牛哥。今天要给大家介绍一个让我爱不释手的Python库 —— Rich。作为一名经常需要开...