自学内容网 自学内容网

python 装饰器从入门到精通-1

装饰器(decorator)是Python中的设计模式,它允许用户在不修改现有对象结构的情况下向其添加新功能。装饰器通常应用于函数,它们在增强或修改函数的行为方面起着至关重要的作用。传统上,装饰器放在想要装饰的函数的定义之前。在本教程中,我们将演示如何在Python函数中有效地使用装饰器。

Python中函数是一等公民,这意味着它们支持诸如作为参数传递、从函数返回、修改和赋值给变量等操作。这个特性是至关重要的,因为它允许函数像Python中的任何其他对象一样被对待,从而在编程中实现更大的灵活性。

函数特性

函数变量

首先,我们创建一个函数,每当调用该函数时,它都会给数字加1。然后将函数赋值给变量,并使用该变量调用函数。

def plus_one(number):
    return number + 1

add_one = plus_one
add_one(5) 
## 6

嵌套函数

接下来,我们将演示如何在Python中定义函数内部的另一个函数。别着急,很快你就会发现所有这些与在Python中创建和理解装饰器是如何相关的。

def plus_one(number):
    def add_one(number):
        return number + 1

    result = add_one(number)
    return result

plus_one(4) 
## 5

函数参数

函数也可以作为参数传递给其他函数, 请看示例:

def plus_one(number):
    return number + 1

def function_call(function):
    number_to_add = 5
    return function(number_to_add)

function_call(plus_one) 
## 6

返回函数

函数也可以返回另一个函数,请看下面示例说明:

def hello_function():
    def say_hi():
        return "Hi"
    return say_hi

hello = hello_function()
hello() 
## 'Hi'

嵌套函数

Python允许嵌套函数访问嵌套函数的外部作用域。这是装饰器中的关键概念,这种方式称为闭包。

def print_message(message):
    "Enclosong Function"
    def message_sender():
        "Nested Function"
        print(message)

    message_sender()

print_message("Some random message") 
## Some random message

装饰器

有了上面对函数的认识,下面利用这些知识学习如何创建、使用装饰器。

创建装饰器

有了这些先决条件,让我们先创建简单的装饰器,将句子转换为大写。我们在封闭的函数中定义包装器来实现这一点。正如你所看到的,它与我们之前创建的另一个函数中的函数非常相似。

def uppercase_decorator(function):
    def wrapper():
        result = function()
        make_uppercase = result.upper()
        return make_uppercase

    return wrapper

decorator函数接受一个函数作为参数,因此我们应该定义函数并将其传递给decorator。我们之前学过可以将函数赋值给变量。我们将使用这个技巧来调用decorator函数。

def say_hi():
    return 'hello there'

decorate = uppercase_decorator(say_hi)
decorate() 
## 'HELLO THERE'

然而,Python为我们提供了更简单的方法来应用装饰器。我们只需在要修饰的函数之前使用@符号。下面让我们在实践中演示一下。

@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi() 
## 'HELLO THERE'

应用多个装饰器

我们可以对一个函数使用多个装饰器。装饰器将按照我们定义它们的顺序执行。下面我们将定义另一个修饰符,它将句子拆分为一个列表。然后我们将把uppercase_decoratorsplit_string 装饰器应用到单个至函数。

import functools
def split_string(function):
    @functools.wraps(function)
    def wrapper():
        result = function()
        splitted_string = result.split()
        return splitted_string

    return wrapper 

@split_string
@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi() 
## ['HELLO', 'THERE']

从上面的输出中,我们注意到装饰器的应用是自下而上的。如果我们交换了顺序,我们就会看到错误信息,因为列表没有上upper属性。这个句子首先被转换成大写,然后被分成列表。

注意:当堆叠装饰器时,使用functools.wraps是一种常见的做法,这样能保留原函数的元数据。这有助于在调试和理解修饰函数的属性时保持清晰度,后面调试部分你会看到差异。

装饰函数参数

有时我们可能需要定义接受参数的装饰器。我们通过将参数传递给包装器函数来实现,然后将参数传递给调用时正在装饰的函数。

def decorator_with_arguments(function):
    def wrapper_accepting_arguments(arg1, arg2):
        print("My arguments are: {0}, {1}".format(arg1,arg2))
        function(arg1, arg2)
    return wrapper_accepting_arguments


@decorator_with_arguments
def cities(city_one, city_two):
    print("Cities I love are {0} and {1}".format(city_one, city_two))

cities("Nairobi", "Accra")

## My arguments are: Nairobi, Accra Cities I love are Nairobi and Accra

注意: 必须确保装饰器中的参数数量(本例中为arg1, arg2)与包装函数中的参数数量(本例中为cities)匹配。当使用带参数的装饰器时,这种对齐对于避免错误和确保正确的功能至关重要。

通用装饰器

要定义可以应用于任何函数的通用装饰器,我们使用args**kwargsargs**kwargs收集所有位置参数和关键字参数,并将它们存储在argskwargs变量中。argskwargs允许我们在函数调用期间传递任意多的参数。

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
        print('The positional arguments are', args)
        print('The keyword arguments are', kwargs)
        function_to_decorate(*args)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("No arguments here.")

function_with_no_argument()

## The positional arguments are ()
## The keyword arguments are {}
## No arguments here.

现在我们在带有位置参数函数上使用该装饰器:

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
## The positional arguments are (1, 2, 3)
## The keyword arguments are {}
## 1 2 3

关键字参数使用关键字传递。如下所示:

@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
    print("This has shown keyword arguments")

function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
## The positional arguments are ()
## The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
## This has shown keyword arguments

注意: 在装饰器中使用**kwargs允许它处理关键字参数。这使得通用装饰器具有通用性,并且能够在函数调用期间处理各种参数类型。

装饰器传参

现在让我们看看如何将参数传递给装饰器本身。为了实现这点,我们需要定义decorator maker,它接受参数,然后在其中定义decorator。最后就像前面一样在装饰器中定义包装器函数:

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
    def decorator(func):
        def wrapper(function_arg1, function_arg2, function_arg3) :
            "This is the wrapper function"
            print("The wrapper can access all the variables\n"
                  "\t- from the decorator maker: {0} {1} {2}\n"
                  "\t- from the function call: {3} {4} {5}\n"
                  "and pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,decorator_arg3,
                          function_arg1, function_arg2,function_arg3))
            return func(function_arg1, function_arg2,function_arg3)
        return wrapper

    return decorator

pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
    print("This is the decorated function and it only knows about its arguments: {0}"
           " {1}" " {2}".format(function_arg1, function_arg2,function_arg3))

decorated_function_with_arguments(pandas, "Science", "Tools")

执行结果:

The wrapper can access all the variables
    - from the decorator maker: Pandas Numpy Scikit-learn
    - from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools

调试装饰器

可能你已注意到,装饰器包装函数、原始函数名、以及docstring和参数列表都被包装器闭包隐藏了。例如,当我们尝试访问decorated_function_with_arguments元数据时,返回值却是包装器闭包的元数据,这种情况给调试带来挑战。

decorated_function_with_arguments.__name__
## 'wrapper'
decorated_function_with_arguments.__doc__
## 'This is the wrapper function'

为了解决这个问题,Python提供functools.wraps装饰器。该装饰器将丢失的元数据从未装饰的函数复制到装饰过的闭包中。请看示例:

import functools

def uppercase_decorator(func):
    @functools.wraps(func)
    def wrapper():
        return func().upper()
    return wrapper

@uppercase_decorator
def say_hi():
    "This will say hi"
    return 'hello there'

say_hi()
## 'HELLO THERE'

say_hi.__name__
## 'say_hi'
say_hi.__doc__
## 'This will say hi'

总是使用functools.wraps是明智的,也是好的做法。它将在调试时省去很多麻烦。

总结

本文介绍python函数基础知识,然后介绍如何定义函数装饰器。装饰器应用非常广泛,它可以动态地改变函数、方法或类的功能,而不必直接使用子类或更改被装饰函数的源代码。在Python中使用装饰器还可以确保你的代码是DRY(Don’t Repeat Yourself)。下面列举几个应用装饰器的典型用例:

  • Python框架(如Flask和Django)中的授权
  • fastApi中 restful服务实现
  • 日志记录
  • 测量执行时间

要了解有关Python Decorator的更多信息,请查看Python的Decorator库。学习了装饰器的基础知识,后面我们会结合实际场景展示应用案例,包括应用日志、运行时间监控、缓存、重试、限流、异常处理、类型转换等。


原文地址:https://blog.csdn.net/neweastsun/article/details/142877923

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!