如何为 Python 类(而非实例)提供非正式字符串表示形式?

6

我知道如何提供一个对象实例的非正式表示,但我想提供一个类名称的非正式字符串表示。

具体地说,我想在打印类(__main__.SomeClass)时覆盖返回的内容。

>>> class SomeClass:
...   def __str__(self):
...     return 'I am a SomeClass instance.'
... 
>>> SomeClass
<class __main__.SomeClass at 0x2ba2f0fd3b30>
>>> print SomeClass
__main__.SomeClass
>>> 
>>> x = SomeClass()
>>> x
<__main__.SomeClass instance at 0x2ba2f0ff3f38>
>>> print x
I am a SomeClass instance.
5个回答

8
你的问题被称为元类混淆。对于类A,如果A.__str__(self)是A实例方法的模板,那么如何为A本身提供一个__str__()方法呢?元类来解救。
以下链接比我在这里能更好地解释这个问题。 http://gnosis.cx/publish/programming/metaclass_1.html http://gnosis.cx/publish/programming/metaclass_2.html 下面是一个简短的例子:
class AMeta(type):
    def __str__(self):
        return "I am the truly remarkable class A"

class A(object):
    __metaclass__ = AMeta
    def __str__(self):
        return "I am an A instance"

print A
I am the truly remarkable class A
print A()
I am an A instance

顺便说一下,您可以对__repr__执行相同的操作。

我认为你在class A中的def str(self):应该改为def __str__(self): - chaos.ct
如果在定义__str__时调用repr(A),它将不会使用您自定义的消息。但是,如果您定义了__repr__并且调用了str(A),如果未定义str(默认元类type没有定义),它将使用您自定义的消息。 - agf
@agif:我的意思是覆盖一个__repr__(repr(A))需要相同的元类技巧。如果其中某个方法未定义,则有几种回退方法,但这是另一个话题,并在std lib参考文档中有很好的记录。 - Jürgen Strobel

3

您需要覆盖元类(metaclass)__str__方法。我不确定为什么您想要这样做,但无论如何,以下是方法。

>>> class InformalType(type):
...     def __str__(self):
...             return self.__name__
... 
>>> class MyFoo(object):
...     __metaclass__ = InformalType
...     pass
... 
>>> MyFoo
<class '__main__.MyFoo'>
>>> print MyFoo
MyFoo
>>> foo = MyFoo()
>>> foo
<__main__.MyFoo object at 0x7fdf9581f910>
>>> print foo
<__main__.MyFoo object at 0x7fdf9581f910>
>>> 

2

更改类的字符串表示:

class MC(type):
    def __repr__(cls):
        return 'I am Test'

class Test:
    __metaclass__ = MC
   pass

print Test

运行良好。

如果在定义__str__时调用repr(Test),它不会使用您自定义的消息。

然而,如果像我这样定义了__repr__,并且调用了str(Test),它使用您自定义的消息,因为__repr__是后备,而__str__没有在type中定义。

如果您只想更改名称:

def renamer(name):
    def wrapper(func):
        func.__name__ = name
        return func
    return wrapper

@renamer('Not Test')
class Test: pass
print Test.__name__

Test.__name__ = 'Test Again'
print Test.__name__    

两者都可以用来更改类名。


他想改变类的打印方式(作为str),而不是它的名称。 - Jürgen Strobel
看看我的第一个代码示例,它确切地做到了那一点。第二个示例是更改名称的示例。事实上,在你之前,我的答案就已经有了这个。 - agf

1

应该重写 __repr__ 方法。

>>> class SomeClass(object):
...   def __repr__(self):
...     return 'I am a SomeClass instance.'

不,这会覆盖实例表示。 - Jürgen Strobel
__repr__也适用于实例。它并不像他说的那样工作,我尝试使用@classmethod修饰__repr__,但没有成功 :)。 - utdemir
你的代码仍然无法工作,因为要对repr(SomeClass)有用,你的SomeClass.__repr__不能有self参数。 - Jürgen Strobel
1
repr 用于对象的正式表示。我正在寻找类的非正式表示。 - adam
我误解了问题。 - OneOfOne

1

你可以通过元类来实现这个功能。

观察:

>>> class MyMeta(type):
...     def __init__(cls, name, bases, dct):
...             super(MyMeta, cls).__init__(name, bases, dct)
...
...     def __repr__(self):
...             return "MyMeta is Cool: " + self.__name__
...
>>> class FooType(metaclass=MyMeta):
...     pass
...
>>> FooType
MyMeta is Cool: FooType

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