将每个实例属性的__str__用作类的__str__

时间:2018-11-29 00:54:25

标签: python python-3.x

Python 3中是否有一种方法可以以编程方式递归地调用私有成员变量唯一的__str__函数?像这样:

class A:
  def __str__(self):
    return "A"

class B:
  def __str__(self):
    return "B"

class C:
  def __init__(self):
    self._A = A()
    self._B = B()

  def __str__(self):
    for var in vars(self):
      return str(var)

呼叫个别私人成员时,它可以正常工作。但是,想要一种动态地执行此操作的方法。

非常感谢。

3 个答案:

答案 0 :(得分:2)

vars函数返回一个字典,其中的键是变量名(作为字符串),值是变量的值。因此,迭代这些值应该可以。

class A:
  def __str__(self):
    return "A"

class B:
  def __str__(self):
    return "B"

class C:
  def __init__(self):
    self._A = A()
    self._B = B()

  def __str__(self):
    output = ""
    for _,var in vars(self).items(): #Iterate over the values
      output += str(var) #Use the str() function here to make the object return its string form
    return output #Need to return instead of print since that is what the __str__() function should do

如果需要,可以在值之间添加某种分隔符(例如\n)。只需将str(var)替换为str(var) + "\n"

答案 1 :(得分:1)

您也可以使用字典键; vars()self.__dict__

>>> class A:
...     def __str__(self):
...         return self.__class__.__name__
... 
>>> class B:
...     def __str__(self):
...         return self.__class__.__name__
... 
>>> str(A())
'A'
>>> repr(A())  # "long-form" the hex-string is id()
'<__main__.A object at 0x10f65a908>'

>>> class C:
...     def __init__(self):
...        self.A = A()
...        self.B = B()
...     def __str__(self):
...         return '\n'.join(self.__dict__)
... 
>>> C()
<__main__.C object at 0x10f65aa58>
>>> print(C())  # Uses str(C())
A
B

vars(self)实际上是self。反过来,self.__dict__是用于存储对象(可写)属性的dict

>>> C().__dict__
{'A': <__main__.A object at 0x10f65aa90>, 'B': <__main__.B object at 0x10f65aac8>}

签名为'\n'.join(iterable),当您遍历字典时,您遍历其键,在这种情况下就足够了。

关于数据类的说明

我不确定(Python 3.7+)数据类在这里是否更容易解决。这是因为据我所知,它们会自动实现__repr__()而不是__str__()

>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class C:
...     _A: object = A()
...     _B: object = B()
... 
>>> c = C()  # still uses repr() for each field
>>> str(c)
'C(_A=<__main__.A object at 0x10f373828>, _B=<__main__.B object at 0x10f373940>)'

换句话说,您需要将A.__str__替换为A.__repr__(与B相同,对于这些内容,您可能一开始就不想这样做两课。

答案 2 :(得分:0)

您确定不应该使用__repr__吗?

无论如何,这是一个使用attrs的示例,因为我被困在Python 3.5上。使用dataclasses,它将以类似的方式工作。

import attr


class A:
    def __str__(self):
        return 'A'

class B:
    def __str__(self):
        return 'B'

@attr.s
class C:
    a = attr.ib(default=attr.Factory(A))
    b = attr.ib(default=attr.Factory(B))


if __name__ == '__main__':
    c = C()
    print(c) # __str__ defaults to __repr__

    def __str__(self):
        bits = ['<C']
        for a in self.__attrs_attrs__:
            bits.append(' %s=%s' % (a.name, getattr(self, a.name)))
        bits.append('>')
        return ''.join(bits)
    C.__str__ = __str__
    print(c) # custom __str__