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

@decorated
def g():
    pass

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

>>> g.__name__
'g'

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

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

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

4

2 回答 2

10

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

>>> g.__name__
'g'
>>> g.__code__.co_name
'wrapper'

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

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

您必须创建一个全新的代码对象来重命名它,请参阅我以前的答案,其中我定义了一个函数来执行此操作;在您的装饰功能上使用该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)

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

于 2015-04-28T12:47:37.110 回答
1

Martijn 的回答涵盖了您的前两个问题,但有一个更好的解决方案:不要对f的论点做出任何假设,而是将所有论点从wrapper()to转发f()

import functools

def decorated(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):  # <- Take any arguments
        return f(*args, **kwargs)  # <- Forward
    return wrapper

@decorated
def g():
    pass

g(1)

输出:

Traceback (most recent call last):
  File "/home/wja/testdir/tmp.py", line 15, in <module>
    g(1)
  File "/home/wja/testdir/tmp.py", line 8, in wrapper
    return f(*args, **kwargs)
TypeError: g() takes 0 positional arguments but 1 was given
于 2021-11-02T17:58:50.443 回答