装饰器 decorator
官方文档定义
decorator -- 装饰器 返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。装饰
器的常见例子包括 classmethod() 和 staticmethod()。
装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:
def f(...):
...
f = staticmethod(f)
@staticmethod
def f(...):
...
同的样概念也适用于类,但通常较少这样使用。有关装饰器的详情可参见 函数定义和 类定义的文
档。
相关知识点
- 函数,类都是对象
函数可以当做参数传递或返回值
- 闭包
内部函数使用外部函数定义的变量
返回内部函数对象在外边调用
参考范例
- 不带参数的装饰器
import time
def showtime(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))
return wrapper
@showtime #foo = showtime(foo)
def foo():
print('foo..')
time.sleep(3)
@showtime #doo = showtime(doo)
def doo():
print('doo..')
time.sleep(2)
foo()
doo()
- 被装饰的函数带参数
import time
def showtime(func):
def wrapper(a, b):
start_time = time.time()
func(a,b)
end_time = time.time()
print('spend is {}'.format(end_time - start_time))
return wrapper
@showtime #add = showtime(add)
def add(a, b):
print(a+b)
time.sleep(1)
@showtime #sub = showtime(sub)
def sub(a,b):
print(a-b)
time.sleep(1)
add(5,4)
sub(3,2)
- 带参数的装饰器
import time
def time_logger(flag = 0):
def showtime(func):
def wrapper(a, b):
start_time = time.time()
func(a,b)
end_time = time.time()
print('spend is {}'.format(end_time - start_time))
if flag:
print('将此操作保留至日志')
return wrapper
return showtime
@time_logger(2) #得到闭包函数showtime,add = showtime(add)
def add(a, b):
print(a+b)
time.sleep(1)
add(3,4)
- 定义类装饰器装饰函数
import time
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
start_time = time.time()
self._func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))
@Foo #bar = Foo(bar)
def bar():
print('bar..')
time.sleep(2)
bar()
- 装饰器装饰类
# 定义一个装饰器
def decorator(cls):
print("这里可以写被装饰类新增的功能")
return cls
# 定义一个类 A,并使用decorator装饰器装饰
@decorator # 装饰器的本质 A = decorator(A),装饰器返回类本身,还是之前的类,只是在返回之前增加了额外的功能
class A(object):
def __init__(self):
pass
def test(self):
print("test")
描述器 descriptor
官方文档定义
descriptor -- 描述器 任何定义了 get(), set() 或 delete() 方法的对象。当一个类属
性为描述器时,它的特殊绑定行为就会在属性查找时被触发。通常情况下,使用 a.b 来获取、设置
或删除一个属性时会在 a 的类字典中查找名称为 b 的对象,但如果 b 是一个描述器,则会调用对应
的描述器方法。理解描述器的概念是更深层次理解 Python 的关键,因为这是许多重要特性的基础,
包括函数、方法、属性、类方法、静态方法以及对超类的引用等等。
有关描述符的方法的详情可参看 descriptors。
@property属性
@property是个描述器 descriptor
参考源码
class property(object):
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
"""
...
@wrap,update_wrapper
修改被装饰函数的元信息
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
参考源码
################################################################################
### update_wrapper() and wraps() decorator
################################################################################
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes of the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
try:
value = getattr(wrapped, attr)
except AttributeError:
pass
else:
setattr(wrapper, attr, value)
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# Issue #17482: set __wrapped__ last so we don't inadvertently copy it
# from the wrapped function when updating __dict__
wrapper.__wrapped__ = wrapped
# Return the wrapper so this can be used as a decorator via partial()
return wrapper
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function
Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying partial() to
update_wrapper().
"""
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)