使用functools.wraps修饰的函数使用包装器的名称引发TypeError.为什么?怎么避免?

jac*_*ev6 11 python decorator functools python-decorators

def decorated(f):
    @functools.wraps(f)
    def wrapper():
        return f()
    return wrapper

@decorated
def g():
    pass
Run Code Online (Sandbox Code Playgroud)

functools.wraps它的工作是保留以下名称g:

>>> g.__name__
'g'
Run Code Online (Sandbox Code Playgroud)

但是如果我传递一个参数g,我得到一个TypeError包含包装器的名称:

>>> g(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: wrapper() takes no arguments (1 given)
Run Code Online (Sandbox Code Playgroud)

这个名字来自哪里?它保存在哪里?有没有办法使异常看起来像g() takes no arguments

Mar*_*ers 9

名称来自代码对象; 函数和代码对象(包含要执行的字节码等)都包含该名称:

>>> g.__name__
'g'
>>> g.__code__.co_name
'wrapper'
Run Code Online (Sandbox Code Playgroud)

代码对象的属性是只读的:

>>> g.__code__.co_name = 'g'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute
Run Code Online (Sandbox Code Playgroud)

您必须创建一个全新的代码对象来重命名,请参阅我之前的答案,我在其中定义了一个函数来执行此操作; 使用rename_code_object()装饰函数上的函数:

>>> g = rename_code_object(g, 'g')
>>> g(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: g() takes no arguments (1 given)
Run Code Online (Sandbox Code Playgroud)

但请注意,这将完全掩盖正在运行的代码!您通常希望看到涉及装饰器包装器; 毕竟,它是抛出异常而不是原始函数的包装器.