Eli*_*ght 978
当您使用装饰器时,您将一个功能替换为另一个功能.换句话说,如果你有一个装饰者
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
Run Code Online (Sandbox Code Playgroud)
然后当你说
@logged
def f(x):
"""does some math"""
return x + x * x
Run Code Online (Sandbox Code Playgroud)
这跟说的完全一样
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
Run Code Online (Sandbox Code Playgroud)
并且您的函数f
被替换为函数with_logging.不幸的是,这意味着,如果你再说
print(f.__name__)
Run Code Online (Sandbox Code Playgroud)
它会打印,with_logging
因为这是你的新功能的名称.事实上,如果你查看docstring f
,它将是空白的,因为with_logging
没有docstring,所以你写的docstring将不再存在.另外,如果你查看该函数的pydoc结果,它将不会被列为带一个参数x
; 相反,它将被列为采取*args
,**kwargs
因为这是with_logging所采取的.
如果使用装饰器总是意味着丢失有关函数的信息,那将是一个严重的问题.这就是我们拥有的原因functools.wraps
.这需要在装饰器中使用的函数,并添加复制功能名称,docstring,参数列表等的功能.由于wraps
它本身就是一个装饰器,下面的代码做了正确的事情:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'
Run Code Online (Sandbox Code Playgroud)
XIA*_* LI 46
def mydeco(func):\n def wrapper(*args, **kwargs):\n return f\'{func(*args, **kwargs)}!!!\'\n return wrapper\n
Run Code Online (Sandbox Code Playgroud)\n@mydeco\ndef add(a, b):\n \'\'\'Add two objects together, the long way\'\'\'\n return a + b\n\n@mydeco\ndef mysum(*args):\n \'\'\'Sum any numbers together, the long way\'\'\'\n total = 0\n for one_item in args:\n total += one_item\n return total\n
Run Code Online (Sandbox Code Playgroud)\n>>> add(10,20)\n\'30!!!\'\n\n>>> mysum(1,2,3,4)\n\'10!!!!\'\n
Run Code Online (Sandbox Code Playgroud)\n>>>add.__name__\n\'wrapper`\n\n>>>mysum.__name__\n\'wrapper\'\n
Run Code Online (Sandbox Code Playgroud)\n>>> help(add)\nHelp on function wrapper in module __main__:\nwrapper(*args, **kwargs)\n\n>>> help(mysum)\nHelp on function wrapper in module __main__:\nwrapper(*args, **kwargs)\n
Run Code Online (Sandbox Code Playgroud)\ndef mydeco(func):\n def wrapper(*args, **kwargs):\n return f\'{func(*args, **kwargs)}!!!\'\n wrapper.__name__ = func.__name__\n wrapper.__doc__ = func.__doc__\n return wrapper\n
Run Code Online (Sandbox Code Playgroud)\n>>> help(add)\nHelp on function add in module __main__:\n\nadd(*args, **kwargs)\n Add two objects together, the long way\n\n>>> help(mysum)\nHelp on function mysum in module __main__:\n\nmysum(*args, **kwargs)\n Sum any numbers together, the long way\n\n
Run Code Online (Sandbox Code Playgroud)\nfrom functools import wraps\n\ndef mydeco(func):\n @wraps(func)\n def wrapper(*args, **kwargs):\n return f\'{func(*args, **kwargs)}!!!\'\n return wrapper\n
Run Code Online (Sandbox Code Playgroud)\n>>> help(add)\nHelp on function add in module main:\nadd(a, b)\n Add two objects together, the long way\n\n>>> help(mysum)\nHelp on function mysum in module main:\nmysum(*args)\n Sum any numbers together, the long way\n
Run Code Online (Sandbox Code Playgroud)\n\n
Jos*_*osh 21
我经常为我的装饰者使用类而不是函数.我遇到了一些麻烦,因为一个对象不具有与函数相同的所有属性.例如,对象不具有该属性__name__
.我有一个特定的问题,很难追踪Django报告错误"对象没有属性' __name__
'"的地方.不幸的是,对于类式装饰器,我不相信@wrap会完成这项工作.我改为创建了一个基类装饰器类,如下所示:
class DecBase(object):
func = None
def __init__(self, func):
self.__func = func
def __getattribute__(self, name):
if name == "func":
return super(DecBase, self).__getattribute__(name)
return self.func.__getattribute__(name)
def __setattr__(self, name, value):
if name == "func":
return super(DecBase, self).__setattr__(name, value)
return self.func.__setattr__(name, value)
Run Code Online (Sandbox Code Playgroud)
此类将所有属性调用代理到正在装饰的函数.所以,你现在可以创建一个简单的装饰器来检查这两个参数是这样指定的:
class process_login(DecBase):
def __call__(self, *args):
if len(args) != 2:
raise Exception("You can only specify two arguments")
return self.func(*args)
Run Code Online (Sandbox Code Playgroud)
从python 3.5开始:
@functools.wraps(f)
def g():
pass
Run Code Online (Sandbox Code Playgroud)
是的别名g = functools.update_wrapper(g, f)
。它确实完成了三件事:
__module__
,__name__
,__qualname__
,__doc__
,和__annotations__
属性f
上g
。该默认列表位于其中WRAPPER_ASSIGNMENTS
,您可以在functools源代码中看到它。__dict__
of 。(请参见源代码)g
f.__dict__
WRAPPER_UPDATES
__wrapped__=f
属性g
结果是g
显示的名称,文档字符串,模块名称和签名与相同f
。唯一的问题是,关于签名,这实际上不是真的:inspect.signature
默认情况下,只是遵循包装器链。您可以inspect.signature(g, follow_wrapped=False)
按照文档中的说明进行检查。这会产生令人讨厌的后果:
Signature.bind()
。现在functools.wraps
,装饰器之间有些混乱,因为开发装饰器的一个非常常见的用例是包装功能。但是两者都是完全独立的概念。如果您有兴趣了解它们之间的区别,则可以为这两种方法实现帮助程序库:decopatch可以轻松编写装饰器,而makefun可以提供保留签名的替代方法@wraps
。注意,它makefun
依赖于与著名decorator
库相同的可靠技巧。
每当我们使用 For 例如:@wraps 后跟我们自己的包装函数。根据此链接中给出的详细信息中给出的详细信息,它说
\n\n\nfunctools.wraps 是在定义包装器函数时调用 update_wrapper() 作为函数装饰器的便捷函数。
\n它相当于部分(update_wrapper,wrapped =wrapped,指派=指派,更新=更新)。
\n
所以@wraps装饰器实际上调用了functools.partial(func[,*args][,**keywords])。
\nfunctools.partial() 定义表示
\n\n\npartial() 用于部分函数应用,它\xe2\x80\x9c 冻结\xe2\x80\x9d 函数\xe2\x80\x99s 参数和/或关键字的某些部分,从而产生具有简化签名的新对象。例如,partial() 可用于创建一个可调用函数,其行为类似于 int() 函数,其中基本参数默认为 2:
\n
>>> from functools import partial\n>>> basetwo = partial(int, base=2)\n>>> basetwo.__doc__ = \'Convert base 2 string to an int.\'\n>>> basetwo(\'10010\')\n18\n
Run Code Online (Sandbox Code Playgroud)\n这让我得出的结论是,@wraps 调用了partial(),并将包装器函数作为参数传递给它。partial() 最终返回简化版本,即包装函数内部的对象,而不是包装函数本身。
\n小智 6
这是关于包装的源代码:
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)
Run Code Online (Sandbox Code Playgroud)