如何为类本身(而不是类的实例)选择自定义字符串表示形式?
- 2025-01-03 08:41:00
- admin 原创
- 30
问题描述:
考虑这个类:
class foo(object):
pass
默认的字符串表示形式如下所示:
>>> str(foo)
"<class '__main__.foo'>"
我怎样才能使该显示成为自定义字符串?
请参阅如何使用 print() 打印类的实例?以了解有关类实例的相应问题。
事实上,这个问题实际上是那个问题的一个特例——因为在 Python 中,类本身也是属于它们自己类的对象——但是如何应用这个建议并不明显,因为默认的“类的类”是预先定义的。
解决方案 1:
在类的元类中实现__str__()
或。__repr__()
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object):
__metaclass__ = MC
print(C)
__str__
如果您想要一个可读的字符串化,请使用它来__repr__
进行明确的表示。
编辑:Python 3 版本
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
解决方案 2:
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
解决方案 3:
如果您必须在第一个之间进行选择__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
解决方案 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!
元编程并不是你每天都需要的东西——但是当你需要它时,它真的能帮到你!
解决方案 5:
除了所有好的答案外,我的版本还添加了装饰:
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!
缺点:
C
如果没有超类,则不能声明(否class C:
)C
`__repr__`实例将是某些奇怪派生的实例,因此为实例添加可能也是个好主意。
解决方案 6:
另一个答案是:
装饰器
类型(这样你就可以保留 IDE 中的自动完成功能)
从 v3.10 开始运行
import typing
class ClassReprMeta(type):
def __repr__(self):
attrs_str = ", ".join(
f"{key}={getattr(self, key)}"
for key in dir(self)
if not key.startswith("_")
)
return f"{self.__name__}({attrs_str})"
T = typing.TypeVar("T")
def printable_class(cls: T) -> T:
"""Decorator to make a class object printable"""
return ClassReprMeta(cls.__name__, cls.__bases__, dict(cls.__dict__))
@printable_class
class CONFIG:
FIRST = 1
SECOND = 2
print(CONFIG) # CONFIG(FIRST=1, SECOND=2)
解决方案 7:
因为您需要一个元类来执行此操作,但是您需要元类本身有一个参数,所以您可以使用通过词法范围捕获名称的元类来完成此操作。
我发现与其他一些替代方案相比,这更容易阅读/理解。
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>"
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件