Learn about decorators in Python

Offer arrives, dig friends to pick up! I am participating in the 2022 Spring Recruitment Check-In Event, click to view the event details .

foreword

This article will take you to learn how decorators work in Python, if you use decorators in functions and classes, how to use decorators to avoid code repetition (DRY principle, Don't Repeat Yourself).

What are decorators in Python

Decorators are a very powerful and useful tool in Python because they allow programmers to modify the behavior of a function or class. Decorators allow us to wrap another function to extend the behavior of the wrapped function without modifying the underlying function definition. This is also called metaprogramming because the program itself tries to modify another part of itself while the program is running.

Decorators are syntactic sugar: implement more complex functionality in your code with a more concise and fluent syntax.

We know that everything in Python is an object. This means that functions in Python can be used as arguments or passed as arguments. Properties of first-class functions:

  • Functions are instances of type Object.
  • Functions can be stored in variables.
  • This function can be passed as a parameter to another function.
  • Functions can be returned from functions.
  • They can be stored in data structures such as hash tables, lists, etc.

Let's look at an example of this.

def hello():
    print('Welcome to Python Decorator!')
    
another_hello = hello()
another_hello

# Welcome to Python Decorator!
复制代码

A​hello()​​​ function is , then the hello function is assigned to another_hello variable, and then this variable is called, and the result is that the hello function is executed.

Since functions in Python are objects, it is possible to pass a function as an object to another function in addition to being simply called.

def print_welcome():
    print('Welcome to Python Decorator!')
    
def print_hello(func):
    def inner():
        print('Hello!')
        func()
    return inner

decorated = print_hello(print_welcome)
decorated()

# Hello!
# Welcome to Python Decorator!
复制代码

syntactic sugar

However, the above code uses an inner function and we can decorate the function by simply decorating it ​print_hello()​with decorator ​print_welcome()​function​​​.

Decorators can simplify our operations. The functionality is exactly the same, but its code is more concise. That is, the use of decorators is simplified by the​@​​​ symbol , as follows:

def print_hello(func):
    def inner():
        print('Hello!')
        func()
    return inner

@print_hello
def print_welcome():
    print('Welcome to Python Decorator!')
    
print_welcome()

# Hello!
# Welcome to Python Decorator!
复制代码

通过这样做,我们能够消除将一个函数显式传递到另一个函数中的使用。Python 装饰器隐式处理这一点。

使用 Python 装饰器修改函数行为

使用 Python 装饰器对函数进行计时

为了演示它们的实用性,让我们构建一个函数,该函数采用另一个函数并对其执行进行计时。在这里,使用装饰器的好处是它允许我们遵循 DRY 编程原则。

装饰器可用于测量函数执行所需的时间。 如果你定义一个简单的睡眠函数,以计算该函数的运行时。

import time

def timeit(func):
    def timed():
        start = time.time()
        result = func()
        end = time.time()
        print(f'Program took {(end - start) * 1000}s to run')
        return result
    return timed

@timeit
def print_welcome():
    print('Welcome to Python Decorator!')
    
print_welcome()

# Welcome to Python Decorator!
# Program took 0.0s to run
复制代码

分析一下上面的代码:

  • 定义了一个函数 ​​timeit()​​ 接受另一个函数
  • 该函数还有另一个内部函数 ​​timed()​
  • 函数跟踪开始时间,执行修饰函数,跟踪结束时间,计算差值并返回结果
  • 最后,外层函数返回内层函数

当我们将此装饰器函数应用于我们的函数 ​​print_welcome()​​ 时,首先会返回欢迎问候语,然后显示执行时间。

使用 Python 装饰器将有用信息记录到终端

与上面的例子类似,我们可以在程序运行时使用装饰器将有用的信息打印到终端。例如,我们可能想知道正在运行哪个函数以及当前时间。也可以使用装饰器传递到日志文件:

from datetime import datetime
def log_info(func):
    def inner():
        print(f'Starting run at {datetime.now()}')
        print(f'Running {func.__name__}')
        func()
    return inner

@log_info
def print_welcome():
    print('Welcome to Python Decorator!')
    
print_welcome()

# Starting run at 2022-03-27 23:26:38.473310
# Running print_welcome
# Welcome to Python Decorator!
复制代码

在上面的示例中,在运行函数之前,我们的装饰器打印当前日期和时间以及将要运行的函数的名称。如果您正在运行较长的脚本,并且只是想知道程序的位置,这可能很有用。

Web app 中使用的装饰器

让我们以 Web 应用程序的用例为例。当您在 Flask 中构建 Web 应用程序时,您总是会编写 url 路由。 每条路线都是 Web 应用程序中的特定页面。 打开页面 ​​/about​​​ 可能会调用 ​​about_page() ​​方法。

@app.route("/about")
def about_page():
  return "Website about nachos"
复制代码

将参数传递给 Python 装饰器

到目前为止,您已经学习了如何创建一些有用的 Python 装饰器。然而,这些装饰器都没有传入参数。在本节中,您将学习如何创建接受参数的 Python 装饰器。

为此,我们将允许在 Python 语法魔术解压缩。使用 ​​func_name(*args,**kwargs)​​,它将解压缩所有参数和所有关键字参数。通过在装饰器中使用它,可以确保装饰器将接受任意数量的参数或关键字参数。这使得它们在重复使用时更加实用。

def print_function_name(func):
    def inner(*args, **kwargs):
        print(f'Running {func.__name__}...')
        return func(*args, **kwargs)
    return inner

@print_function_name
def add_nums(a, b):
    print(a + b)

add_nums(1, 2)

# Running add_nums...
# 3
复制代码

上述方法的美妙之处在于它同时接受位置和关键字参数。因此,即使我们以以下任何格式执行该函数,该函数也将运行:

  • ​add_nums(1024, 2020)​
  • ​add_nums(1024, b = 2021)​
  • ​add_nums(a = 1024, b = 2222)​

使用多个 Python 装饰器

关于 Python 装饰器的一个有趣的方式是:可以同时使用多个装饰器。这意味着您可以将多个装饰器应用于单个函数。为了理解这一点,来看一个例子:

def one(func):
    def inner(*args, **kwargs):
        print('1')
        return func(*args, **kwargs)
    return inner
    
def two(func):
    def inner(*args, **kwargs):
        print('2')
        return func(*args, **kwargs)
    return inner

@one
@two
def speak(text):
    print(text)
    
speak('Hello')

# 1
# 2
# Hello
复制代码

我们的装饰器函数所做的唯一事情就是打印出数字 1 和数字 2。通过将装饰器 ​​@one​​​ 放在 ​​@two​​​ 之前,您可以将 ​​two()​​​包装的函数包装为 ​​one()​​。为了说明这一点,您可以切换顺序以查看如何修改行为:

# Changing decorator order
@two
@one
def speak(text):
    print(text)
    
speak('Hello')

# 2
# 1
# Hello
复制代码

通过首先放置 ​​@two​​ 装饰器,该函数成为最外层的函数。

总结

在本文中,我们先了解了什么是 Python 装饰器,它代表元编程的语法糖。 Python 装饰器允许我们修改函数行为并允许我们以不同的方式扩展函数。

接着了解装饰器是什么以及如何使用它们。学习了如何允许将参数传递给 Python 装饰器,学习了几个常见的装饰器有用的工具。最后,如何使用多个装饰器以及装饰器的先后顺序。

参考文章:

Guess you like

Origin juejin.im/post/7080549589589164045