1

我有一个主要由一个非常大的嵌套字典组成的对象:

class my_object(object):
    def __init__(self):
        self.the_dict = {}  # Big, nested dictionary

我已经修改 __ str__ 以通过简单地“打印”对象来漂亮地打印顶级字典:

    def __str__(self):
        pp = pprint.PrettyPrinter()
        return pp.pformat(self.the_dict)

我的目标是让用户在使用 IPython 阅读对象时更轻松:

print(the_object)  # Pretty-prints entire dict

这可以向用户显示整个字典,但我也想将此功能扩展到字典的子部分,允许用户从以下命令获得漂亮的打印输出:

print(the_object.the_dict['level1']['level2']['level3'])

(只会打印'level3'子字典)

有没有一种直接的方式来使用 __ str__ (或类似的)来做到这一点?

4

3 回答 3

2

您可以提供一个自定义显示钩子,用于在交互式提示中根据您的喜好打印内置字典和您选择的其他对象:

>>> import sys
>>> oldhook = sys.displayhook
>>> sys.displayhook = your_module.DisplayHook(oldhook)

它不会改变print obj行为。

这个想法是您的用户可以选择他们是否愿意为dicts 使用您的自定义格式。

于 2011-11-15T13:25:26.433 回答
1

当用户说

print(the_object.the_dict['level1']['level2']['level3'])

Python 评估the_object.the_dict['level1']['level2']['level3']并(假设)发现它是一个 dict,并将其传递给print.

由于the_object.the_dict是 dict,其余部分the_object不受控制。当您深入了解level1level2level3时,只有返回的对象类型the_object.the_dict['level1']['level2']['level3']会影响print行为方式。the_object__str__方法不会影响the_object自身以外的任何事物。

此外,在打印嵌套对象时,pprint.pformat使用repr对象的,而不是str对象的。

因此,为了获得我们想要的行为,我们需要the_object.the_dict['level1']['level2']['level3']评估类似 dict 但具有不同__repr__...


您可以制作一个类似 dict 的对象(例如Turtle)并Turtles一直使用:

import collections
import pprint

class Turtle(collections.MutableMapping):
    def __init__(self,*args,**kwargs):
        self._data=dict(*args,**kwargs)
    def __getitem__(self,key):
        return self._data[key]
    def __setitem__(self, key, value):
        self._data[key]=value
    def __delitem__(self, key):
        del self._data[key]
    def __iter__(self):
        return iter(self._data)
    def __len__(self):
        return len(self._data)
    def __contains__(self, x):
        return x in self._data
    def __repr__(self):
        return pprint.pformat(self._data)

class MyObject(object):
    def __init__(self):
        self.the_dict=Turtle()
    def __repr__(self):
        return repr(self.the_dict)

the_object=MyObject()
the_object.the_dict['level1']=Turtle()
the_object.the_dict['level1']['level2']=Turtle()
the_object.the_dict['level1']['level2']['level3']=Turtle({i:i for i in range(20)})
print(the_object)
print(the_object.the_dict['level1']['level2']['level3'])

要使用它,您必须将嵌套 dict 结构中的所有 dict 替换为Turtles。

但实际上(正如你可以从我的奇思妙想中看出的那样),我真的不希望你使用Turtles。字典是非常好的、优化的内置函数,我不想添加这个中间对象只是为了实现漂亮的打印。

如果相反,您可以说服您的用户键入

from pprint import pprint

然后他们可以使用

pprint(the_object.the_dict['level1']['level2']['level3'])

获得漂亮的打印效果。

于 2011-11-15T11:57:46.330 回答
0

您可以将基础词典转换为“漂亮的印刷词典”......也许这样的事情会做:

class my_object( object ):
  _pp = pprint.PrettyPrinter()

  class PP_dict( dict ):
      def __setitem__( self, key, value ):
          if isinstance( value, dict ): value = PP_dict( value )
          super( my_object.PP_dict, self ).__setitem__( key, value )

      def __str__( self ):
          return my_object.pp( self )

  @property
  def the_dict( self ):
      return self.__dict__[ 'the_dict' ]

  @the_dict.setter
  def the_dict( self, value ):
      self.__dict__[ 'the_dict' ] = my_object.PP_dict( value )

该属性只是因为我不知道您如何设置/操作“the_dict”。

这种方法是有限的——例如,如果你将不是字典的字典衍生物放在 the_dict 中,它们将被 PP_dict 替换。此外,如果您对这些 subdicts 有其他引用,它们将不再指向相同的对象。

另一种方法是直接将 a__getitem__放入 my_object 中,它为字典返回一个代理包装器,该包装器在 中漂亮地打印当前对象__str__,覆盖__getitem__以返回子对象的代理,否则将所有访问/操作转发给包装的类。

于 2011-11-15T03:56:21.573 回答