Python类的repr输出

8 投票
4 回答
3666 浏览
提问于 2025-04-17 18:48

根据Python 2 的文档,关于 __repr__ 的说明是:

如果可能的话,__repr__ 应该看起来像一个有效的 Python 表达式,这个表达式可以用来重新创建一个具有相同值的对象(在适当的环境下)。

那么,为什么内置类的 __repr__ 不遵循这个指导原则呢?

举个例子

>>> class A(object):
...   pass
>>> repr(A)
"<class 'A'>"

为了符合这个指导原则,默认的 __repr__ 应该返回 "A",也就是一般来说应该返回 A.__name__。那为什么它的表现却不一样呢?我觉得这样实现起来应该很简单。


补充:'重现'的范围

我在回答中看到,讨论中并不清楚 repr 应该返回什么。我的理解是,repr 函数应该返回一个字符串,这个字符串能让你重现这个对象:

  1. 在任意上下文中,并且
  2. 自动地(也就是说,不需要手动)。

关于第一点:看看一个内置类的例子(取自这个问题):

>>> from datetime import date
>>>
>>> repr(date.today())        # calls date.today().__repr__()
'datetime.date(2009, 1, 16)'

显然,假设的上下文是你使用基本的导入形式,也就是 import datetime,因为如果你尝试 eval(repr(date.today()))datetime 将无法识别。所以关键是 __repr__ 不需要从头开始表示这个对象。 只要在社区达成一致的上下文中不模糊就可以了,比如使用直接的模块类型和函数。这听起来合理吧?

关于第二点:我认为,仅仅给出如何重建对象的印象是不够的。str 的目的是帮助调试。

总结

所以我对 repr 的期望是,它能让我对结果使用 eval。在类的情况下,我不希望得到从头重建类的完整代码。相反,我希望在我的作用域中有一个明确的类引用。"Module.Class" 就足够了。抱歉,Python,但 "<class 'Module.Class'>" 真的不够直观。

4 个回答

1

我知道这个问题有点老了,但我找到了一种方法来解决它。

我知道的唯一方法是使用一种叫做元类的东西,像这样:

class A(object):
    secret = 'a'

    class _metaA(type):
        @classmethod
        def __repr__(cls):
            return "<Repr for A: secret:{}>".format(A.secret)
    __metaclass__ =_metaA

输出结果是:

>>> A
<Repr for A: secret:a>
2

因为默认的 __repr__ 方法并不知道是用什么语句来创建这个类的。

你引用的文档开头提到“如果可能的话”。由于无法以某种方式表示自定义类以便重新创建它们,所以使用了不同的格式,这种格式适用于所有不容易重建的东西。

如果 repr(A) 只是返回 'A',那就没有意义了。你并不是在重新创建 A,而只是在引用它。"type('A', (object,), {})" 更接近于反映类的构造函数,但这会让不熟悉 Python 类是 type 实例的人感到困惑,而且也无法准确反映方法和属性。

可以把输出和 repr(type)repr(int) 的结果进行比较,它们遵循的是相同的模式。

3

考虑一个稍微复杂一点的类:

class B(object):
    def __init__(self):
        self.foo=3

repr 需要返回类似这样的内容:

type("B", (object,), { "__init__": lambda self: setattr(self, "foo", 3) })

你会发现一个问题:并不是所有用 def 语句定义的函数都能用一个简单的 lambda 表达式来表示。稍微改动一下 B

class B(object):
    def __init__(self, x=2, y, **kwargs):
        print "in B.__init__"

你怎么写一个 表达式 来定义 B.__init__? 你不能使用

lambda self: print "in B.__init__"

因为 lambda 表达式不能包含语句。对于这个简单的类来说,已经不可能用一个单独的 表达式 来完全定义这个类了。

撰写回答