有没有一种方法可以在Python中自动生成__str__()实现?

36

手动编写类的字符串表示方法让我感到疲惫不堪,我想知道是否有一种Pythonic的方法可以自动化实现。

我希望输出包含类的所有属性和类名。以下是一个示例:

class Foo(object):
    attribute_1 = None
    attribute_2 = None
    def __init__(self, value_1, value_2):
         self.attribute_1 = value_1
         self.attribute_2 = value_2

结果为:

bar = Foo("baz", "ping")
print(str(bar)) # desired: Foo(attribute_1=baz, attribute_2=ping)

在一些Java项目中使用Project Lombok的@ToString后,我想到了这个问题。


Project Lombok 对 Java 有什么作用? - Max Malysh
减少样板代码。请在此处查看功能:https://projectlombok.org/features/index.html - Marco Ferrari
1
实际上,“样板代码减少”并没有什么意义。Lombok处理特定的Java问题。寻找“类似”的工具是无用的,最好询问更具体的内容。 - Max Malysh
1
Python 一个默认的实现方式来处理 __str__,它会转发到 __repr____repr__ 也有一个默认的实现方式,其中提到了 type(my_object)id(my_object) 的结果。如果你想使用其他默认值,你可以 a) 使用继承,b) 编写自己的类装饰器,或者 c) 在类体中分配 __str__ 给一些现有的函数(通过执行 __str__ = something 而不是 def __str__(self))。 - lvc
3个回答

63

你可以使用 varsdir 等方法来迭代实例属性:

def auto_str(cls):
    def __str__(self):
        return '%s(%s)' % (
            type(self).__name__,
            ', '.join('%s=%s' % item for item in vars(self).items())
        )
    cls.__str__ = __str__
    return cls

@auto_str
class Foo(object):
    def __init__(self, value_1, value_2):
        self.attribute_1 = value_1
        self.attribute_2 = value_2

应用:

>>> str(Foo('bar', 'ping'))
'Foo(attribute_2=ping, attribute_1=bar)'

这对于像List[MyClass]这样的复杂类属性不起作用。 - Sniper
@Sniper,是的,这个解决方案不会递归。使用数据类怎么样? - falsetru
顺便问一下,你是否在 MyClass 上使用了 @auto_str 装饰器? - falsetru
是的,我做了。 - Sniper
@Sniper,将__str__替换为__repr__即可得到您想要的结果。(list使用__repr__将列表项表示为字符串,而不是__str__ - falsetru

4

您可以使用@dataclass,它会自动生成__init__()__repr__()__str__()等方法。您只需要在您的类上添加一个@dataclass装饰器,并对成员变量添加类型注释。甚至可以将您的__init__()实现删除。

from dataclasses import dataclass

@dataclass
class Foo(object):
    attribute_1 : str
    attribute_2 : str

bar = Foo("baz", "ping")
print(str(bar)) # Prints: Foo(attribute_1='baz', attribute_2='ping')

2

在 falsetru 回答时,我写下了这篇文章。我们的想法是一样的,我的阅读难度非常适合初学者,而他的实现方式则更加优美。

class stringMe(object):
        def __str__(self):
            attributes = dir(self)
            res = self.__class__.__name__ + "("
            first = True
            for attr in attributes:
                if attr.startswith("__") and attr.endswith("__"):
                    continue

                if(first):
                    first = False
                else:
                    res += ", "

                res += attr + " = " + str( getattr(self, attr))

            res += ")"
            return res

    class Foo(stringMe):
        attribute_1 = None
        attribute_2 = None
        def __init__(self, value_1, value_2):
             self.attribute_1 = value_1
             self.attribute_2 = value_2


bar = Foo("baz", "ping")
print(str(bar)) # desired: Foo(attribute_1=baz, attribute_2=ping)

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