Python 装饰器
顾名思义,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())
- ++你好++ *
在上面的例子中,定义了star和plus装饰者,它们可以将 ** 和 ++ 添加到我们的消息中。这两个都附加到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
在上面的例子中,我们创建了两个函数calcCube和calcSquare,分别用于计算数字列表的平方和立方。现在,我们要计算执行这两个函数所需的时间,为此,我们定义了一个装饰器timing,它将计算执行这两个函数所需的时间。
这里我们使用了时间模块和函数启动前的时间来启动变量,函数结束后的时间来结束变量。f.__name__给出正在修饰的当前函数的名称。代码range(1,100000)返回了一个从 1 到 100000 的数字列表。
因此,通过使用装饰器,我们避免了在两个函数中分别使用相同的代码(以获得执行时间)。这有助于我们维护干净的代码,并减少了工作开销。