在Python中是否可以更改函数的repr?

23

我只看到了对于类定义中设置 __repr__ 方法的例子。是否可能在函数定义或定义后更改函数的 __repr__

我已经尝试过但没有成功...

>>> def f():
    pass
>>> f
<function f at 0x1026730c8>
>>> f.__repr__ = lambda: '<New repr>'
>>> f
<function __main__.f>

虽然装饰器很有趣,但更简单的方法似乎是不要首先创建一个函数,而是直接创建一个带有__repr____call__方法的类,然后可以像调用函数一样调用它。 - GKFX
4个回答

29

是的,如果你愿意放弃函数的实际功能,这是可行的。

首先,为我们的新类型定义一个类:

import functools
class reprwrapper(object):
    def __init__(self, repr, func):
        self._repr = repr
        self._func = func
        functools.update_wrapper(self, func)
    def __call__(self, *args, **kw):
        return self._func(*args, **kw)
    def __repr__(self):
        return self._repr(self._func)

添加一个装饰器函数:

def withrepr(reprfun):
    def _wrap(func):
        return reprwrapper(reprfun, func)
    return _wrap

现在,我们可以定义 repr 函数:

@withrepr(lambda x: "<Func: %s>" % x.__name__)
def mul42(y):
    return y*42

现在repr(mul42)的输出结果为'<Func: mul42>'


4
请在装饰器中使用 functools.wraps 来更新被装饰函数的名称和文档字符串,以保持功能不变但更易理解。 - schlamar
1
问题在于 print mul42.__name__ 会引发一个 AttributeError,这对于一个函数来说是不应该的。因此,解决方法是:return wraps(func)(reprwrapper(reprfun, func)) - schlamar
1
在这种情况下,我认为update_wrapper稍微更合适/直接。我已经修改了包装器类,使其在构造函数中执行此操作。这样即使您直接使用该类而不是使用withrepr装饰器,更新也会发生。 - kwatford

9
不可以,因为repr(f)是通过type(f).__repr__(f)来执行的。

5
为了实现这个目标,你需要更改给定类的 __repr__ 函数,而在本例中,该类是内置函数类 (types.FunctionType)。由于在 Python 中您不能编辑内置类,只能对其进行子类化,因此您无法这样做。
然而,您可以采用以下两种方法:
  1. Wrap some functions as kwatford suggested
  2. Create your own representation protocol with your own repr function. For example, you could define a myrepr function that looks for __myrepr__ methods first, which you cannot add to the function class but you can add it to individual function objects as you suggest (as well as your custom classes and objects), then defaults to repr if __myrepr__ is not found. A possible implementation for this would be:

    def myrepr(x):
      try:
        x.__myrepr__
      except AttributeError:
        return repr(x)
      else:
        return x.__myrepr__()
    

    Then you could define __myrepr__ methods and use the myrepr function. Alternatively, you could also do __builtins__.repr = myrepr to make your function the default repr and keep using repr. This approach would end up doing exactly what you want, though editing __builtins__ may not always be desirable.


自定义repr协议很有趣,但它稍微复杂一些,因为您还需要考虑列表、字典或类似对象内部的嵌套对象。 - mara004

1

这似乎很困难。 Kwatford的方法只部分解决了这个问题,因为它不适用于类中的函数,因为self会像位置参数一样被处理,如装饰Python类方法 - 如何将实例传递给装饰器?所述 - 然而,那个问题的解决方案不幸的是不适用于这种情况,因为使用__get__()functools.partial会覆盖自定义的__repr__()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接