Python 装饰器

原文:https://www.studytonight.com/python/python-decorators

顾名思义,python 中的装饰器是为现有函数或代码添加附加功能的特殊函数。

例如,你有一辆白色汽车,带有基本的车轮设置,机械师将你的汽车颜色改为红色,并安装合金车轮,然后机械师装饰你的汽车,类似地,python 中的装饰器用于装饰(或添加功能或特性)你现有的代码。


先决条件

在我们学习装饰者的概念之前,我们必须了解一些关于功能的事情。

在 Python 中,一切都是对象,可以使用变量名引用。是的,即使是函数也是有属性的对象。

我们可以让多个变量引用同一个函数对象(定义),例如:

def one(msg):
    print(msg)
# calling the function
one("Hello!")

# having a new variable reference it
two = one

# calling the new variable
two("Hello!")

你好!你好!

同样,我们也可以传递一个函数作为参数。例如:

# some function
def first(msg):
    print(msg)

# second function
def second(func, msg):
    func(msg)

# calling the second function with first as argument
second(first, "Hello!")

你好!

虽然在上面的例子中,函数second以函数first为参数并使用它,但是函数也可以返回函数。

当存在嵌套函数(函数内部的函数)并且外部函数返回内部函数时,它在 python 中被称为闭包,这是我们在上一个教程中学习过的。

python 中的 Decorators 是 python 中闭包概念的某种扩展。


在 Python 中使用装饰器

装饰器给函数一个新的行为,而不改变函数本身。装饰器用于向函数或类添加功能。换句话说,python 装饰器包装另一个函数,并扩展包装函数的行为,而不会永久修改它。

现在,让我们用一个例子来理解装饰者,

# a decorator function
def myDecor(func):
    # inner function like in closures
    def wrapper():
        print("Modified function")
        func()
    return wrapper

def myfunc():
    print('Hello!!')

# Calling myfunc()
myfunc()

# decorating the myfunc function
decorated_myfunc = myDecor(myfunc)

# calling the decorated version
decorated_myfunc()

修改功能你好!!

在上面的代码示例中,我们遵循了闭包方法,但是我们传递的是一个函数作为参数,因此用更多的代码语句来执行这个函数。

我们将函数myfunc作为参数传递给函数myDecor来获得函数myfunc的修饰版本。

现在,python 没有将函数作为参数传递给装饰函数,而是为我们提供了一种简单的方法,使用@符号。

例如,

# using the decorator function
@myDecor
def myfunc():
    print('Hello!!')

# Calling myfunc()
myfunc()

修改功能你好!!

在上面的代码示例中,@myDecor用于将myDecor()装饰器附加到您想要的任何函数上。所以当我们调用myfunc()时,不是执行myfunc()函数的实际体,而是作为参数传递给myDecor(),返回修改后的myfunc()版本,执行。

所以基本上 @ < Decorator_name > 是用来将任何名为 Decorator_name 的 Decorator 附加到 python 编程语言中的任何函数上。


有争论的装饰者

到目前为止,我们已经看到使用装饰器来修改没有使用任何参数的函数。现在,让我们看看如何将参数用于要修饰的函数。

为此,我们将使用*args**kwargs作为装饰器内部函数中的参数。

函数定义中的*args用于向任何函数传递可变数量的参数。它用于传递非关键字、可变长度的参数列表。

函数定义中的**kwargs用于传递关键字化的可变长度参数列表。我们用双星来命名夸格斯。原因是双星允许我们传递关键字参数(以及任意数量的参数)。

For example,

 def myDecor(func):
    def wrapper(*args, **kwargs):
        print('Modified function')
        func(*args, **kwargs)
    return wrapper

@myDecor
def myfunc(msg):
    print(msg)

# calling myfunc()
myfunc('Hey')

修改函数嘿

在本例中,myfunc()函数接受一个参数msg,这是一条将要打印的消息。该调用将导致 decorator 对函数进行装饰,传递给它的参数将作为结果传递给wrapper()函数的参数,该参数将在调用myfunc()函数时再次传递这些参数。最后,传递的信息会打印在语句“修改功能”之后。


链接装饰者

我们可以使用多个装饰器通过链接来装饰一个功能。让我们用一个例子来理解它,

# first decorator
def star(f):
    def wrapped():
        return '**' + f() + '**'
    return wrapped

# second decorator
def plus(f):
    def wrapped():
        return '++' + f() + '++'
    return wrapped

@star
@plus
def hello():
    return 'hello'

print(hello())
    • ++你好++ *

在上面的例子中,定义了starplus装饰者,它们可以将 **++ 添加到我们的消息中。这两个都附加到hello()函数,因此它们同时修改了函数,修饰了输出消息。


装饰师的实际应用

装饰器通常用于将定时和日志功能添加到 python 程序的正常功能中。让我们看一个例子,其中我们将为两个功能添加计时功能:

import time

def timing(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = f(*args,**kwargs)
        end = time.time()
        print(f.__name__ +" took " + str((end-start)*1000) + " mil sec")
        return result
    return wrapper

@timing
def calcSquare(numbers):
    result = []
    for number in numbers:
        result.append(number*number)
    return result

@timing
def calcCube(numbers):
    result = []
    for number in numbers:
        result.append(number*number*number)
    return result

# main method
if __name__ == '__main__':
    array = range(1,100000)
    sq = calcSquare(array)
    cube = calcCube(array)

钙立方厘米用了 60.99678039551 密耳秒。钙立方厘米用了 52.99336036061

在上面的例子中,我们创建了两个函数calcCubecalcSquare,分别用于计算数字列表的平方和立方。现在,我们要计算执行这两个函数所需的时间,为此,我们定义了一个装饰器timing,它将计算执行这两个函数所需的时间。

这里我们使用了时间模块和函数启动前的时间来启动变量,函数结束后的时间来结束变量。f.__name__给出正在修饰的当前函数的名称。代码range(1,100000)返回了一个从 1 到 100000 的数字列表。

因此,通过使用装饰器,我们避免了在两个函数中分别使用相同的代码(以获得执行时间)。这有助于我们维护干净的代码,并减少了工作开销。