先复习下装饰器
# coding=utf-8
def logged(func):
def with_logging(*args, **kwargs):
"""
哈哈哈,这里是with_logging
:param args:
:param kwargs:
:return:
"""
print func.__name__, " was called..."
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""
哈哈,这是f
:param x:
:return:
"""
return x + x * x
print f.__name__
print f.__doc__
打印结果是:
(venv) ➜ myApp git:(v1.6_dev) ✗ python 1.py
with_logging
哈哈哈,这里是with_logging
:param args:
:param kwargs:
:return:
理论上这是不对的,因为函数f的信息已经获取不到了。。都被with_logging取代了。
解决方案:
# coding=utf-8
import functools
def logged(func):
@functools.wraps(func)
def with_logging(*args, **kwargs):
"""
哈哈哈,这里是with_logging
:param args:
:param kwargs:
:return:
"""
print func.__name__, " was called..."
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""
哈哈,这是f
:param x:
:return:
"""
return x + x * x
print f.__name__
print f.__doc__
打印结果是:
(venv) ➜ myApp git:(v1.6_dev) ✗ python 1.py
f
哈哈,这是f
:param x:
:return:
以上是别的教程写的解决方案,突然对这个functools.wraps()有点兴趣,它到底是如何做到的呢?
functools.warp方法就是把被wrapped的函数的属性传递给wrapper的函数。
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
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:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# 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)
这个乍一看不是很理解,这个怎么和普通的装饰器不大一样呀。普通的长得像:
def out_wrapper(func):
def in_wrapper(*args, **kwargs):
print ‘in_wrapper’
return func(*args, **kwargs)
return in_wrapper
不过剥丝抽茧,先去除注释:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper
def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
嗯,看起来简单清爽多了。考虑到偏函数,上面代码可以转成:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper
def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):
def in_wrapper(wrapper, wrapped=wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):
return update_wrapper(wrapper, wrapped=wrapped, assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES)
return in_wrapper
一般带参数的装饰器,是三层的,类似:
def wrapper(x):
def out_wrapper(func):
def in_wrapper(*args, **kwargs):
print ‘in_wrapper’
return func(*args, **kwargs)
return in_wrapper
return out_wrapper
虽然长得不一样,但思路就是返回一个能包装函数的函数变量,这帮作者写代码真是骚。
end...