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

考虑这个类:

class foo(object):
pass

默认的字符串表示形式是这样的:

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

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

273830 次浏览
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"

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

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


class C(object):
__metaclass__ = MC


print(C)

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

如果你必须在__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

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!

元编程不是你每天都需要的东西,但是当你需要它的时候,它真的很合适!

只是在所有的好答案之外,加上我的装饰版本:

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)

stdout:

Wahaha!

缺点:

  1. 没有超类就不能声明C(没有class C:)
  2. C实例将是一些奇怪的派生实例,因此为实例添加__repr__可能是一个好主意。

因为你需要一个元类来做这件事,但是你需要元类本身有一个参数,你可以用一个元类来通过词法作用域捕获名字。

我发现这比一些替代方案更容易阅读/遵循。


class type_: pass


def create_type(name):
# we do this so that we can print the class type out
# otherwise we must instantiate it to get a proper print out
class type_metaclass(type):
def __repr__(self):
return f'<{name}>'


class actual_type(type_, metaclass=type_metaclass):
pass
return actual_type


my_type = create_type('my_type')


print(my_type)
# prints "<my_type>"