python解答之装饰器

2021-07-05 11:07

阅读:682

标签:inf   call   wrap   方式   简单的   orm   函数传参   time   war   

装饰器,简而言之就是在不改变原函数任何代码(包括调用方式)的情况下为函数增加额外的功能。特殊之处在于装饰器的返回值也是一个函数。
一、装饰器介绍
下面先举个简单的栗子:

import time

def count_time(foo_func):
def inner():
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("the func cost:{:.2f}".format(end_time-start_time))
return inner

@count_time
def foo():
# 利用装饰器计算foo函数执行时间
print("foo execute done!!")

if __name__ == ‘__main__‘:
foo() # 调用foo函数

既然说增加了装饰器,那么原函数的代码和调用方式都没改变。
其中 @count_time 就等同于 foo = count_time(foo)

如果要给装饰器传参数。。如下:
import time

def outer(name):
def count_time(foo_func):
def inner():
print("inner func start!")
print("装饰器传进来的参数是:{}".format(name))
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("the func cost:{:.2f}".format(end_time-start_time))
return inner
return count_time

@outer(name="jack")
def foo():
# 利用装饰器计算foo函数执行时间
print("foo execute done!!")

if __name__ == ‘__main__‘:
foo() # 调用foo函数

若要给装饰器传参数,只需要再给函数增加一层函数就行了。

但是由于使用装饰器后会改变函数的一些内置变量比如__name__,__doc__。看栗子:

import time
from functools import wraps

def count_time(foo_func):
# @wraps(foo_func)
def inner():
"""
now in inner func
"""
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("foo func cost:{:.2f}".format(end_time-start_time))
return inner

@count_time
def foo():
"""
now in foo func
"""
# 利用装饰器计算foo函数执行时间
print("foo func doc is {}".format(foo.__doc__)) # 输出 now in inner func
print("foo func name is {}".format(foo.__name__)) # 输出 inner
print("foo execute done!!")

if __name__ == ‘__main__‘:
foo() # 调用foo函数
所以需要纠正回来,该如何呢?来

只需要引入functools模块下的wraps函数
在inner函数上面使用@wraps(func),即再次装饰下inner函数即可
然后输出语句会变为:
print("foo func doc is {}".format(foo.__doc__)) # 输出 now in foo func
print("foo func name is {}".format(foo.__name__)) # 输出 foo

要给被装饰的函数传参数:
栗子如下:
import time
from functools import wraps

def count_time(foo_func):
@wraps(func)
def inner(*args, **kwargs):
"""
now in inner func
"""
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func(*args, **kwargs)
end_time = time.time()
print("foo func cost:{:.2f}".format(end_time-start_time))
return inner

@count_time
def foo(name, age, *args, **kwargs):
"""
now in foo func
"""
# 利用装饰器计算foo函数执行时间
print(name,age)
print(args,kwargs)
print("foo execute done!!")

if __name__ == ‘__main__‘:
foo("jack", 21, "student", department="信工学院") # 调用foo函数

增加的地方有调用时传了四个参数,分别是位置参数和关键字参数(最后一个是关键字参数,关键字参数必须在位置参数之后传入)
在装饰器内部的inner函数的参数那里增加了*args, **kwargs,和inner内部的func(*args, **kwargs)这里
args表示所有的位置参数都在args这个元组里面
kwargs表示所有的关键字参数都在args这个字典里面

二、基于类实现的装饰器
看栗子
class Logging(object):
def __init__(self, func):
self.func = func

def __call__(self, *args, **kwargs):
print("[DEBUG]: enter function {func}()".format(
func=self.func.__name__))
return self.func(*args, **kwargs)

@Logging
def say(something):
print( "say {}!".format(something))

say("hello")

输出:
[DEBUG]: enter function say()
say hello!
解答:如果用类来实现装饰器的话,必须实现类中的内置方法__init__和__call__这两个方法
其中__init__用来接收被装饰函数的名字,__call__用来对函数进行装饰以及增加额外的功能

若要给装饰器传参数。还是看栗子:
class logging(object):
def __init__(self, level=‘INFO‘):
self.level = level

def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print("[{level}]: enter function {func}()".format(
level=self.level,
func=func.__name__))
func(*args, **kwargs)

return wrapper # 返回函数

@logging(level=‘INFO‘)
def say(something):
print("say {}!".format(something))

say("hello")

这里的调用就是给装饰器传参数。
__init__方法参数接收的不再是被装饰函数的函数名,而是装饰器传来的参数
__call__方法参数接收被装饰函数的函数名,里面再嵌套一层函数用来接收装饰函数传的参数

最后,拓展一下wrapt这个装饰器
import wrapt

@wrapt.decorator
def logging(wrapped, instance, args, kwargs): # instance is must need
print("[DEBUG]: enter {}()".format(wrapped.__name__))
return wrapped(*args, **kwargs)

@logging
def say(something): pass

say("hello")

这样看起来装饰器就更加明了了,,不用在装饰器内部再嵌套函数了。
上面的写法必须要按照上面的来
其中wrapped就表示被装饰的函数,
若要给装饰器传参数,也是再嵌套一层就可以了。

python解答之装饰器

标签:inf   call   wrap   方式   简单的   orm   函数传参   time   war   

原文地址:https://www.cnblogs.com/crazy-xf/p/9597764.html


评论


亲,登录后才可以留言!