如何为类对象创建自定义字符串表示形式?


202

考虑此类:

class foo(object):
    pass

默认的字符串表示形式如下所示:

>>> str(foo)
"<class '__main__.foo'>"

如何使它显示自定义字符串?

Answers:


272

在类的元类中实施__str__()__repr__()

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object):
  __metaclass__ = MC

print C

使用__str__,如果你说的是可读的字串,使用__repr__了明确的表示。


2
我想创建某种类型的类装饰器,因此我可以轻松地为我的类设置自定义字符串表示形式,而不必为每个类编写一个元类。我对Python的元类不是很熟悉,所以您能给我那里的任何指针吗?
比约恩博动

不幸的是,这不能用类装饰器来完成。定义类时必须设置它。
伊格纳西奥·巴斯克斯

10
@ Space_C0wb0y:您可以_representation在类主体和元类return self._representation__repr__()方法中添加类似的字符串。
Sven Marnach

@BjörnPollex您也许可以使用decorator来解决这个问题,但是我希望您不得不在语言的许多微妙之处上挣扎。即使您这样做,也仍然必须以一种或另一种方式使用元类,因为您不想使用默认值__repr__来表示C_representation创建成员的替代方法是创建一个元类工厂,该工厂将生成具有适当元数据的元类__repr__(如果您经常使用它,那可能会很好)。
2013年


22
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"

10
这将更改类的字符串表示形式instances,而不是类本身的字符串表示形式。
tauran 2011年

抱歉,没有看到您帖子的第二部分。使用上面的方法。
Andrey Gubarev 2011年

6
@RobertSiemer为什么?尽管他的回答并非专门针对OP的问题,但仍然很有帮助。它帮助了我。乍一看,我没有看到任何要求实例实现的问题。因此,人们可能会首先进入此页面。
akinuri

14

如果必须在第一个之间进行选择__repr__或选择__str__第一个,例如,默认情况下,实现在未定义时__str__调用__repr__

自定义Vector3示例:

class Vector3(object):
    def __init__(self, args):
        self.x = args[0]
        self.y = args[1]
        self.z = args[2]

    def __repr__(self):
        return "Vector3([{0},{1},{2}])".format(self.x, self.y, self.z)

    def __str__(self):
        return "x: {0}, y: {1}, z: {2}".format(self.x, self.y, self.z)

在此示例中,repr再次返回可以直接使用/执行的字符串,而str作为调试输出更为有用。

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3

1
尽管您关于__repr__vs 的观点__str__是正确的,但这并不能回答实际的问题,该问题是关于类对象而不是实例的。
比约恩·波莱克斯(BjörnPollex)

感谢您的反馈,完全监督了这一点。让我回顾一下我的答案。
user1767754

我认为您的repr和str的实现已互换。
jspencer

4

伊格纳西奥·巴斯克斯(Ignacio Vazquez-Abrams)的批准答案是正确的。但是,它来自Python 2代。当前Python 3的更新为:

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object, metaclass=MC):
    pass

print(C)

如果您想要同时在Python 2和Python 3上运行的代码,则需要介绍以下六个模块:

from __future__ import print_function
from six import with_metaclass

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(with_metaclass(MC)):
    pass

print(C)

最后,如果您想拥有一个自定义静态repr的类,则上述基于类的方法效果很好。但是,如果您有几个,则必须MC为每个生成一个类似的元类,这可能会很累。在这种情况下,将元编程再进一步一步并创建一个元类工厂会使事情变得更加整洁:

from __future__ import print_function
from six import with_metaclass

def custom_class_repr(name):
    """
    Factory that returns custom metaclass with a class ``__repr__`` that
    returns ``name``.
    """
    return type('whatever', (type,), {'__repr__': lambda self: name})

class C(with_metaclass(custom_class_repr('Wahaha!'))): pass

class D(with_metaclass(custom_class_repr('Booyah!'))): pass

class E(with_metaclass(custom_class_repr('Gotcha!'))): pass

print(C, D, E)

印刷品:

Wahaha! Booyah! Gotcha!

元编程不是您通常每天都需要的东西,但是,当您需要它时,它真的很实用!


0

只需添加所有好的答案,我的版本就会带有修饰:

from __future__ import print_function
import six

def classrep(rep):
    def decorate(cls):
        class RepMetaclass(type):
            def __repr__(self):
                return rep

        class Decorated(six.with_metaclass(RepMetaclass, cls)):
            pass

        return Decorated
    return decorate


@classrep("Wahaha!")
class C(object):
    pass

print(C)

标准输出:

Wahaha!

缺点:

  1. C没有超类就不能声明(no class C:
  2. C实例将是一些奇怪派生的实例,因此也最好__repr__为这些实例添加一个。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.