__str__ и довольно печатные (под) словари - PullRequest
1 голос
/ 15 ноября 2011

У меня есть объект, который состоит в основном из очень большого вложенного словаря:

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'sub-dict)

Есть ли прямой способ использовать __str__ (или аналогичный) для этого?

Ответы [ 3 ]

2 голосов
/ 15 ноября 2011

В интерактивном режиме вы можете предоставить настраиваемый дисплейный крючок, который печатает встроенные словари и другие объекты, которые вы выбираете по своему вкусу:

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

Он не меняет print obj поведение.1006 * Идея состоит в том, что ваши пользователи могут выбирать, хотят ли они использовать ваше пользовательское форматирование для dict с или нет.

1 голос
/ 15 ноября 2011

Когда пользователь говорит

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

Python оценивает the_object.the_dict['level1']['level2']['level3'] и (скажем) находит, что это диктат, и передает его на print.

Так как the_object.the_dict является диктатом, остальное находится вне контроля the_object. Когда вы пройдете через level1, level2 и level3, только тип объекта, возвращаемый the_object.the_dict['level1']['level2']['level3'], будет влиять на поведение print. Метод the_object __str__ не повлияет ни на что, кроме the_object.

Более того, при печати вложенных объектов pprint.pformat использует repr объекта, а не str объекта.

Итак, чтобы получить поведение, которое мы хотим, нам нужно the_object.the_dict['level1']['level2']['level3'], чтобы оценить что-то вроде диктата, но с другим __repr__ ...


Вы можете сделать объект, похожий на диктовку (например, 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'])

Чтобы использовать это, вы должны заменить все диктанты в вашей структуре вложенных диктов на Turtle s.

Но на самом деле (как вы можете судить по моим причудливым именам), я не ожидаю, что вы будете использовать Turtle s. Dicts - это такие хорошие, оптимизированные встроенные функции, я бы не хотел добавлять этот промежуточный объект просто для того, чтобы добиться красивой печати.

Если вместо этого вы сможете убедить своих пользователей набрать

from pprint import pprint

тогда они могут просто использовать

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

чтобы получить красивую печать.

0 голосов
/ 15 ноября 2011

вы можете конвертировать базовые словари в "красивые печатные словари" ... возможно, что-то вроде этого подойдет:

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 производные dict, которые не являются dict, они будут заменены на PP_dict.Кроме того, если у вас есть другая ссылка на эти поддикты, они больше не будут указывать на те же объекты.

Другой подход заключается в непосредственном размещении __getitem__ в my_object, который возвращает прокси-оболочку для словаряэто довольно печатает текущий объект в __str__, переопределяет __getitem__, чтобы вернуть прокси для подобъектов, и в противном случае перенаправляет все обращения / манипуляции в обернутый класс.

...