Python中的装饰器入门教程

125 阅读5分钟

Python中的装饰器入门

Python装饰器是一种设计模式,它允许开发者在不改变函数代码结构的情况下修改函数的结构。

当你想改变一个函数的行为时,这个概念很有用,所以与其进去改变整个代码,不如创建一个Python装饰器,这将允许你使用一行代码来改变所有不同函数的行为。

前提条件

理解Python编程中的一切都以对象的形式出现,是理解这个概念的关键。读者应该对Python非常熟悉,尽管对初学者的解释简单而精确。

除此之外,你还需要

  • 在你的计算机上安装一个可以工作的Python。
  • 一个代码编辑器,以便试用这些代码片段。

了解什么是装饰器

许多人对Python装饰器的概念并不清楚,尽管它很简单。由于这个原因,你可以把Python装饰器看作是Python中的高级函数,它把另一个函数作为参数,并把另一个函数作为返回值。

Python装饰器显示了Python编程语言是如何在你的小而有益的包中打包了若干功能。装饰器可以应用于类和函数,使编程变得更加迷人。它们可以加快性能,缩短并完全改变代码所能做的动态。

装饰器的核心支柱

由于Python中的所有东西都是一个对象,所以函数也是对象,正因为如此,它们可以被赋值给变量,而且函数可以从它们被赋值的相同变量中访问。

# the first function
def first_function():
    print('The first function')

x = first_function

# calling the function from the variable
x()

输出。

The first function

其次,由于一个函数是一个对象,它可以嵌套在另一个函数中,这样当外部函数被调用时,内部函数也将被执行。

# Outter function
def umbrella_function():

    # inner function
    def inner_function():
        print('I am the inner function')

    # Executing the inner function inside the umbrela function.
    inner_function()

# calling the outer function calls the inner as well
umbrella_function()

输出。

I am the inner function

第三个支柱;一个函数可以是另一个函数的返回值,因为我们可以将它们分配给变量,并嵌套在其他函数中。

def umbrella_function():
   
    name = 'Mia Roberts'
    def inner_function():
        print(name)
    # return a function
    return inner_function

x = umbrella_function()

# calling the function via a variable
x()

输出。

Mia Roberts

接下来,一个函数可以作为一个参数传递给另一个函数,就像我们可以把变量作为参数传递一样。


# main function
def main_function(func):
    func()

#parameter function
def parameter_function():
    print('I am the parameter function')

# call main function passing another functions as a parameter
main_function(parameter_function)

输出。

I am the parameter function

创建 Python 装饰器

这个解释可能看起来很抽象,但如果我们举个例子,就会很容易掌握。让我们通过编写两个不同的函数来演示这些,并使用它们来探索Python装饰器的概念。

例如,如果我们把上面的函数拿出来,我们可以像下面的片段那样对它进行装饰。

def umbrella_function(function):
   
    name = 'Mia Roberts'
    def inner_function():
        print(name)
        function()
    return inner_function

def another_function():
    print('I am a computer science student')

x = umbrella_function(another_function)

x()

输出。

Mia Roberts
I am a computer science student

在上面的例子中,umbrella_function() 是一个装饰器,但是当我们说x = umbrella_function(another_function) 时,函数another_function() 被装饰了,返回的函数被分配到变量x

要在Python中装饰一个函数,我们在装饰函数的名字旁边使用@ 符号,并把它放在函数定义的正上方。

例如。

@umbrella_function
def another_function():
    print('I am a computer science student')

上面的内容与此相同。

def another_function():
    print('I am a computer science student')

x = umbrella_function(another_function)

向装饰器传递参数

有时我们可能需要定义一个接受参数的装饰器函数。我们可以通过将参数传递给包装器函数来实现,然后将其传递给被装饰的函数。

def umbrella_function(function):
   
    def inner_function(args1, args2):   
        print("Arguments passed are: {0}, {1}".format(args1,args2))     
        function(args1, args2)
    return inner_function

@umbrella_function
def another_function(name, age):
   print ('So, {name} is {age} years old and she is a {occupation}'
   .format(name = name, age = age, occupation = 'programmer'))

another_function('Mia', 18)

输出。

Arguments passed are: Mia, 18
So, Mia is 18 years old, and she is a programmer

调试 Python 装饰器

正如所观察到的,装饰器包裹着函数。原始函数和它的参数都被包装器所掩盖。这个问题给调试带来了挑战,要求必须解决这个难题。

然而,Python 提供了一个functools.wraps() 装饰器,它可以将元数据从未装饰的函数复制到已装饰的包装函数中。让我们看看如何做到这一点。

如果我们试图打印出没有functools.wraps() 的伞状函数的元数据,那么返回的是包装器的名字,而不是伞状函数。

def umbrella_function(function):
    """I am the umbrella function """
    # @functools.wraps(function)    
    def inner_function(args1, args2):  
        """Inner docs""" 
        print("Arguments passed are: {0}, {1}".format(args1,args2))     
        function(args1, args2)
    return inner_function

@umbrella_function
def another_function(name, age):
    """I am the another function """
    print('So, {name} is {age} years old and she is a {occupation}'
   .format(name = name, age = age, occupation = 'programmer'))

print(another_function.__name__)
print(another_function.__doc__)

输出。

inner_function
Inner docs

然而,如果我们使用functools.wraps() ,元数据就可以被访问。

def umbrella_function(function):
    """I am the umbrella function """
    @functools.wraps(function)    
    def inner_function(args1, args2):  
        """Inner docs""" 
        print("Arguments passed are: {0}, {1}".format(args1,args2))     
        function(args1, args2)
    return inner_function

@umbrella_function
def another_function(name, age):
    """I am the another function """
    print('So, {name} is {age} years old and she is a {occupation}'
   .format(name = name, age = age, occupation = 'programmer'))

print(another_function.__name__)
print(another_function.__doc__)

输出:如果我们使用,元数据就可以被访问。

another_function
I am another function 
umbrella_function
I am the umbrella function

使用多个装饰器

Python 允许同时使用多个装饰器。这个过程包括用相同或不同的装饰器对一个函数进行多次装饰。

为了使用多个装饰器,我们把装饰器放在所需函数的正上方。Python 会自动完成装饰器的连锁。

def umbrella_function(function):
   
    def inner_function(args1, args2):   
        print("The first argument is: {0}".format(args1))     
        function(args1, args2)
    return inner_function

def umbrella_function2(function):

    def inner_function(args1, args2):   
        print("The second argument passed is: {0}".format(args2))     
        function(args1, args2)
    return inner_function


@umbrella_function
@umbrella_function2
def another_function(name, age):
   print ('So, {name} is {age} years old and she is a {occupation}'
   .format(name = name, age = age, occupation = 'programmer'))

another_function('Mia', 18)

输出。

The first argument is: Mia
The second argument passed is: 18
So, Mia is 18 years old, and she is a programmer

结论

正如我们在上面看到的,Python 装饰器动态地改变了一个函数、方法或类的属性,而不直接改变代码片段。装饰器可以确保你的代码不是多余的,可以用来编写更干净的代码。