LINUX.ORG.RU

Есть ли в Python аналог Data::Dumper ?

 ,


1

4

Доброго времени

Сабж. Первый подход к гуглу дал pprint. Он удобен для словарей, но абсолютно бесполезен для объектов

Perl:

use Data::Dumper;
use WWW::Mechanize;

my $browser = WWW::Mechanize->new();
printf "%s\n", Dumper($browser);
вывод:
$VAR1 = bless( {
                 'headers' => {},
                 'ssl_opts' => {
                                 'verify_hostname' => 1
                               },
                 'forms' => undef,
[...]
                 'cookie_jar' => bless( {
                                          'COOKIES' => {}
                                        }, 'HTTP::Cookies' ),
                 'proxy' => {},
                 'max_size' => undef
               }, 'WWW::Mechanize' );

Python:

import pprint
import mechanize

browser = mechanize.Browser()
pprint.pprint(browser)

вывод:

<mechanize._mechanize.Browser instance at 0x15e7c20>

Допустим, я понимаю почему так происходит. В perl ООП сделан через одно место. self, в котором хранятся поля класса - по сути обычная структура данных. Как правило - хэш ( в терминах Python - словарь ). Плюс области видимости позволяют при необходимости залезть и вытащить что угодно. А в Python практически всё - объект. И у объекта определён метод __str__, который приводит его к строке. И если автор класса об этом не подумал, __str__ даст бесполезный текст «блаблабла instance at адрес»

Второй подход к гуглу дал vars(), dirs(), которые вполне годятся для нормального дампа:

    pprint.pprint( vars( browser ) )

вывод:

{'_any_request': {},
 '_any_response': {},
 '_client_cert_manager': <mechanize._auth.HTTPSClientCertMgr instance at 0x1807758>,
 '_factory': <mechanize._html.DefaultFactory instance at 0x17a2f38>,
 '_handle_referer': True,
 '_handler_index_valid': False,
[...]
 'request': None,
 'request_class': <class mechanize._request.Request at 0x165fef0>}

Именно то, что нужно, поля и их значения. Но в дампе опять видим издевательские «instance at». Если хочется идти глубже - нужно вручную их дампить через vars. Наверняка можно пройти по вложенным объектам, пусть даже рекурсивно, и сделать как perl.

Вопрос, есть ли в python готовый модуль, в котором это уже сделано, т.е. модуль, который является полноценной заменой перловому Data::Dumper ?

★★★★★

Не совсем уверен, что правильно тебя понял, но, вроде тебя должно спасти переопределение питоновских методов __str__ и __unicode__

Например:

>>> class Example(object):
...     def __init__(self, content):
...         self.content = content
...     def __str__(self):
...         return self.content
...
>>> test = Example("string with content")
>>> print test, test.content
string with content string with content
>>> class Example(object):
...     def __init__(self, content):
...         self.content = content
...
>>> test = Example("string with content")
>>> print test, test.content
<__main__.Example object at 0xffdc1a8c> string with content

Ну, или можешь написать свою функцию-обертку к print, которая умеет «правильно» печатать экземпляры нужного тебе класса. Готового такого модуля, к сожалению, не знаю.

irr123 ()
Ответ на: комментарий от x3al

Эмм. Ты хочешь рекурсивно пройти по __dict__ ? Это строк пять питона.

И эти пять строк до сих пор никто не сделал в виде модуля?

router ★★★★★ ()

В чём заключается исходная задача? Упрощение отладки? Наглядное изучение внутренностей питона?

zolden ★★★★★ ()
Ответ на: комментарий от router
def obj2dic(obj):
    result = {}
    for field, value in obj.__dict__.items():
        if hasattr(value, '__dict__'):
            result[field] = obj2dic(value)
        else:
            result[field] = str(value)
    return result

Держи. Это сделает из объектов, наследованных от object, словари. Методы и прочая хрень не учитывается.

Можешь фильтровать __мусор__ если хочешь.

x3al ★★★★★ ()
Ответ на: комментарий от router

упрощение отладки

Тогда __repr__ на свои объекты. И не надо ничего велосипедить.

anonymous ()
Ответ на: комментарий от x3al

Пропускает словари и списки, но идею понял

Плюс похоже что питон очень любит рекурсию в структурах данных, пришлось ограничить максимальную глубину рекурсии. Потом добавлю запоминание адресов уже обработанных структур в дополнение к maxdepth

def obj2dict(obj,maxdepth):
    obj_type = type(obj).__name__
    result = {}

    if maxdepth <= 0:
        return obj
    maxdepth -= 1

    if ( obj_type == 'instance' ) :
        for field, value in obj.__dict__.items():
            result[field] = obj2dict(value, maxdepth)
    elif ( obj_type == 'dict' ):
        for field in obj.keys():
            result[field] = obj2dict( obj[field], maxdepth )
    elif ( obj_type == 'list' ):
        result = [];
        for item in obj:
            result.append( obj2dict( item, maxdepth))
    elif ( obj_type in [ 'NoneType', 'bool', 'str', 'int' ] ) :
        result = obj
    elif ( obj_type in ['classobj'] ):
        result = str(obj)
    else:
        result = {
            'type' : obj_type,
            'str' : str(obj),
            'addr' : hex(id(obj)),
        }
    return result
router ★★★★★ ()
Последнее исправление: router (всего исправлений: 2 )
Ответ на: комментарий от ei-grad

Модули отсутствовали в debian, поэтому было лень проверять %)

А вот последняя ссылка - то что нужно. Код не запускал, но прочёл с интересом и дополнил вариант x3al

router ★★★★★ ()
Ответ на: комментарий от Virtuos86

Не совсем то. Выдаёт не структуру инстанса, а справку по соответствующему классу. Но тоже полезно

router ★★★★★ ()
Ответ на: комментарий от router

Проверку на maxdepth перенести в начало функции, код перед ней на нее не влияет, а идейно будет выглядеть лучше; type(obj).__name__ заменить на obj.__class__.__name__, мне кажется, так будет на 1 миллигвидо питоничнее.

И раз уж ты знаешь про vars, то vars(obj) равносилен obj.__dict__, но прячет несколько андерскоров в коде, всё плюс.

Virtuos86 ★★★★★ ()
Последнее исправление: Virtuos86 (всего исправлений: 1 )
Ответ на: комментарий от nwalker

значит с чем-то другим были проблемы. Вот например для одного из полей того же mechanize.Browser

print obj.__class__.__name__ AttributeError: class HeadParser has no attribute '__class__'

Посмотрел внимательнее, появился ещё один аргумент: obj.__class__.__name__ не имеет никакого отношения к type(obj).__name__ ;)

$ python 
Python 2.7.3 (default, Mar 13 2014, 11:03:55) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mechanize
>>> browser = mechanize.Browser()
>>> browser.__class__.__name__
'Browser'
>>> type(browser).__name__
'instance'
>>> 
router ★★★★★ ()
Ответ на: комментарий от nwalker

глупость какая. появляется возможность ошибиться в нескольких местах сразу вместо одного

router ★★★★★ ()
Ответ на: комментарий от router

bool, если что, субкласс int. Но вообще ты прав, делай по-своему.

Virtuos86 ★★★★★ ()
23 января 2016 г.
Ответ на: комментарий от router

Дальнейшее знакомство с python показало, что str() относится к устаревшим функциям, которые категорически запрещено использовать, т.к. они не умеют работать с unicode

http://stackoverflow.com/questions/9942594/unicodeencodeerror-ascii-codec-can...

заменил все «str(obj)» просто на «obj»

router ★★★★★ ()
Последнее исправление: router (всего исправлений: 1 )
Ответ на: комментарий от router

str() относится к устаревшим функциям, которые категорически запрещено использовать

Что за дурь?

Deleted ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.