神奇的Python装饰器:可将代码减少一半!

神奇的Python装饰器:可将代码减少一半!

今天,我将与你分享一些神奇的Python装饰器,它们可以将你的代码减少一半。听起来好得难以置信,对吗?让我来告诉你它们是如何工作的,以及为什么要在你的项目中使用它们。如果你想了解更多关于Python的相关内容,可以阅读以下这些文章:
Excel中的Python:将重塑数据分析师的工作方式
5个超棒的Python项目!
数据科学家提高Python代码质量指南
从优秀到卓越:数据科学家的Python技能进化之路

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

例如,假设你有一个向控制台打印一条信息的函数:

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

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

import time

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中有许多内置的装饰器,例如@staticmethod、@classmethod、@property、@functools.lru_cache、@functools.singledispatch等。你还可以为各种目的创建自己的自定义装饰器。下面是一些Python装饰器的例子,它们可以将代码减少一半:

1.@timer装饰器

该装饰器与我们之前看到的@measure_time装饰器类似,但它可以应用于任何接收任意数量参数并返回任意值的函数。它还使用了functools.wraps装饰器来保留原始函数的名称和docstring。代码如下:

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装饰器来保留原始函数的名称和docstring。代码如下:

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装饰器来保留原始函数的名称和docstring。代码如下:

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,例如:

@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装饰器是一种强大而优雅的方法,可以在不改变源代码的情况下修改函数或类的行为。它们可以帮助你减少一半的代码,提高代码的可读性,重用代码,分离关注点,并扩展现有代码的功能。

感谢阅读!你还可以订阅我们的YouTube频道,观看大量大数据行业相关公开课:https://www.youtube.com/channel/UCa8NLpvi70mHVsW4J_x9OeQ;在LinkedIn上关注我们,扩展你的人际网络!https://www.linkedin.com/company/dataapplab/

原文作者:Ayush Thakur
翻译作者:Qing
美工编辑:过儿
校对审稿:Jason
原文链接:https://medium.com/@ayush-thakur02/python-decorators-that-can-reduce-your-code-by-half-b19f673bc7d8