Украсьте свои структуры данных красивым выводом на Python

Оглавление

Работа с данными важна для любого специалиста по питону, но иногда эти данные просто не очень привлекательны. Компьютеры не заботятся о форматировании, но без хорошего форматирования людям может быть трудно что-то прочитать. Результат не очень хорош, когда вы используете print() для больших словарей или длинных списков — это эффективно, но некрасиво.

Модуль pprint в Python - это служебный модуль, который вы можете использовать для печати структур данных в удобочитаемом, красивом, виде. Это часть стандартной библиотеки, которая особенно полезна для отладки кода, работающего с запросами API, большими файлами JSON и данными в целом.

К концу этого урока вы поймете:

  • Понять зачем нужен pprint модуль
  • Узнайте, как использовать pprint(), PrettyPrinter, и их параметров
  • Иметь возможность создать свой собственный экземпляр PrettyPrinter
  • Сохраните форматированный вывод строки вместо того, чтобы печатать его
  • Печать и распознавание рекурсивных структур данных

Попутно вы также увидите HTTP-запрос к общедоступному API и синтаксический анализ JSON в действии.

Понимание необходимости красивого вывода Python

Модуль Python pprint полезен во многих ситуациях. Он пригодится при выполнении запросов к API, работе с файлами JSON или при обработке сложных и вложенных данных. Вы, вероятно, обнаружите, что использование обычной функции print() недостаточно для эффективного анализа ваших данных и отладки вашего приложения. Когда вы используете print() с словарями и списками, выходные данные не содержат новых строк.

Прежде чем вы начнете изучать pprint, вы сначала воспользуетесь urllib, чтобы сделать запрос на получение некоторых данных. Вы отправите запрос в {JSON}-заполнитель для получения некоторой фиктивной информации о пользователе. Первое, что нужно сделать, это отправить HTTP-запрос GET и поместить ответ в словарь:

>>> from urllib import request
>>> response = request.urlopen("https://jsonplaceholder.typicode.com/users")
>>> json_response = response.read()
>>> import json
>>> users = json.loads(json_response)

Здесь вы создаете базовый запрос GET, а затем преобразуете ответ в словарь с помощью json.loads(). Поскольку словарь теперь находится в переменной, следующим обычным шагом является печать содержимого с помощью print():

>>> print(users)
[{'id': 1, 'name': 'Leanne Graham', 'username': 'Bret', 'email': 'Sincere@april.biz', 'address': {'street': 'Kulas Light', 'suite': 'Apt. 556', 'city': 'Gwenborough', 'zipcode': '92998-3874', 'geo': {'lat': '-37.3159', 'lng': '81.1496'}}, 'phone': '1-770-736-8031 x56442', 'website': 'hildegard.org', 'company': {'name': 'Romaguera-Crona', 'catchPhrase': 'Multi-layered client-server neural-net', 'bs': 'harness real-time e-markets'}}, {'id': 2, 'name': 'Ervin Howell', 'username': 'Antonette', 'email': 'Shanna@melissa.tv', 'address': {'street': 'Victor Plains', 'suite': 'Suite 879', 'city': 'Wisokyburgh', 'zipcode': '90566-7771', 'geo': {'lat': '-43.9509', 'lng': '-34.4618'}}, 'phone': '010-692-6593 x09125', 'website': 'anastasia.net', 'company': {'name': 'Deckow-Crist', 'catchPhrase': 'Proactive didactic contingency', 'bs': 'synergize scalable supply-chains'}}, {'id': 3, 'name': 'Clementine Bauch', 'username': 'Samantha', 'email': 'Nathan@yesenia.net', 'address': {'street': 'Douglas Extension', 'suite': 'Suite 847', 'city': 'McKenziehaven', 'zipcode': '59590-4157', 'geo': {'lat': '-68.6102', 'lng': '-47.0653'}}, 'phone': '1-463-123-4447', 'website': 'ramiro.info', 'company': {'name': 'Romaguera-Jacobson', 'catchPhrase': 'Face to face bifurcated interface', 'bs': 'e-enable strategic applications'}}, {'id': 4, 'name': 'Patricia Lebsack', 'username': 'Karianne', 'email': 'Julianne.OConner@kory.org', 'address': {'street': 'Hoeger Mall', 'suite': 'Apt. 692', 'city': 'South Elvis', 'zipcode': '53919-4257', 'geo': {'lat': '29.4572', 'lng': '-164.2990'}}, 'phone': '493-170-9623 x156', 'website': 'kale.biz', 'company': {'name': 'Robel-Corkery', 'catchPhrase': 'Multi-tiered zero tolerance productivity', 'bs': 'transition cutting-edge web services'}}, {'id': 5, 'name': 'Chelsey Dietrich', 'username': 'Kamren', 'email': 'Lucio_Hettinger@annie.ca', 'address': {'street': 'Skiles Walks', 'suite': 'Suite 351', 'city': 'Roscoeview', 'zipcode': '33263', 'geo': {'lat': '-31.8129', 'lng': '62.5342'}}, 'phone': '(254)954-1289', 'website': 'demarco.info', 'company': {'name': 'Keebler LLC', 'catchPhrase': 'User-centric fault-tolerant solution', 'bs': 'revolutionize end-to-end systems'}}, {'id': 6, 'name': 'Mrs. Dennis Schulist', 'username': 'Leopoldo_Corkery', 'email': 'Karley_Dach@jasper.info', 'address': {'street': 'Norberto Crossing', 'suite': 'Apt. 950', 'city': 'South Christy', 'zipcode': '23505-1337', 'geo': {'lat': '-71.4197', 'lng': '71.7478'}}, 'phone': '1-477-935-8478 x6430', 'website': 'ola.org', 'company': {'name': 'Considine-Lockman', 'catchPhrase': 'Synchronised bottom-line interface', 'bs': 'e-enable innovative applications'}}, {'id': 7, 'name': 'Kurtis Weissnat', 'username': 'Elwyn.Skiles', 'email': 'Telly.Hoeger@billy.biz', 'address': {'street': 'Rex Trail', 'suite': 'Suite 280', 'city': 'Howemouth', 'zipcode': '58804-1099', 'geo': {'lat': '24.8918', 'lng': '21.8984'}}, 'phone': '210.067.6132', 'website': 'elvis.io', 'company': {'name': 'Johns Group', 'catchPhrase': 'Configurable multimedia task-force', 'bs': 'generate enterprise e-tailers'}}, {'id': 8, 'name': 'Nicholas Runolfsdottir V', 'username': 'Maxime_Nienow', 'email': 'Sherwood@rosamond.me', 'address': {'street': 'Ellsworth Summit', 'suite': 'Suite 729', 'city': 'Aliyaview', 'zipcode': '45169', 'geo': {'lat': '-14.3990', 'lng': '-120.7677'}}, 'phone': '586.493.6943 x140', 'website': 'jacynthe.com', 'company': {'name': 'Abernathy Group', 'catchPhrase': 'Implemented secondary concept', 'bs': 'e-enable extensible e-tailers'}}, {'id': 9, 'name': 'Glenna Reichert', 'username': 'Delphine', 'email': 'Chaim_McDermott@dana.io', 'address': {'street': 'Dayna Park', 'suite': 'Suite 449', 'city': 'Bartholomebury', 'zipcode': '76495-3109', 'geo': {'lat': '24.6463', 'lng': '-168.8889'}}, 'phone': '(775)976-6794 x41206', 'website': 'conrad.com', 'company': {'name': 'Yost and Sons', 'catchPhrase': 'Switchable contextually-based project', 'bs': 'aggregate real-time technologies'}}, {'id': 10, 'name': 'Clementina DuBuque', 'username': 'Moriah.Stanton', 'email': 'Rey.Padberg@karina.biz', 'address': {'street': 'Kattie Turnpike', 'suite': 'Suite 198', 'city': 'Lebsackbury', 'zipcode': '31428-2261', 'geo': {'lat': '-38.2386', 'lng': '57.2232'}}, 'phone': '024-648-3804', 'website': 'ambrose.net', 'company': {'name': 'Hoeger LLC', 'catchPhrase': 'Centralized empowering task-force', 'bs': 'target end-to-end models'}}]

О боже! Одна огромная строка без перевода строк. В зависимости от настроек вашей консоли это может выглядеть как одна очень длинная строка. Кроме того, в консольном выводе может быть включен режим переноса слов, что является наиболее распространенной ситуацией. К сожалению, это не делает результат намного более дружелюбным!

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

for user in users:
    print(user)

Этот цикл for выводит каждый объект в отдельной строке, но даже в этом случае каждый объект занимает намного больше места, чем может поместиться в одной строке. Печать таким способом делает работу немного лучше, но это ни в коем случае не идеально. Приведенный выше пример представляет собой относительно простую структуру данных, но что бы вы сделали с глубоко вложенным словарем, размер которого в 100 раз больше?

Конечно, вы могли бы написать функцию, которая использует рекурсию, чтобы найти способ вывести все данные на печать. К сожалению, вы, вероятно, столкнетесь с некоторыми крайними случаями, когда это не сработает. Возможно, вам даже придется написать целый модуль функций, просто чтобы разобраться со структурой данных!

Войдите в модуль pprint!

Работа с pprint

pprint это модуль Python, созданный для удобной печати структур данных. Он уже давно является частью стандартной библиотеки Python, поэтому устанавливать его отдельно нет необходимости. Все, что вам нужно сделать, это импортировать его функцию pprint():

>>> from pprint import pprint

Затем, вместо того, чтобы использовать обычный подход print(users), как вы делали в примере выше, вы можете вызвать свою новую любимую функцию, чтобы сделать вывод красивым:

>>> pprint(users)

Эта функция выводит users — но новым и улучшенным красивым способом:

>>> pprint(users)
[{'address': {'city': 'Gwenborough',
              'geo': {'lat': '-37.3159', 'lng': '81.1496'},
              'street': 'Kulas Light',
              'suite': 'Apt. 556',
              'zipcode': '92998-3874'},
  'company': {'bs': 'harness real-time e-markets',
              'catchPhrase': 'Multi-layered client-server neural-net',
              'name': 'Romaguera-Crona'},
  'email': 'Sincere@april.biz',
  'id': 1,
  'name': 'Leanne Graham',
  'phone': '1-770-736-8031 x56442',
  'username': 'Bret',
  'website': 'hildegard.org'},
 {'address': {'city': 'Wisokyburgh',
              'geo': {'lat': '-43.9509', 'lng': '-34.4618'},
              'street': 'Victor Plains',
              'suite': 'Suite 879',
              'zipcode': '90566-7771'},
  'company': {'bs': 'synergize scalable supply-chains',
              'catchPhrase': 'Proactive didactic contingency',
              'name': 'Deckow-Crist'},
  'email': 'Shanna@melissa.tv',
  'id': 2,
  'name': 'Ervin Howell',
  'phone': '010-692-6593 x09125',
  'username': 'Antonette',
  'website': 'anastasia.net'},

 ...

 {'address': {'city': 'Lebsackbury',
              'geo': {'lat': '-38.2386', 'lng': '57.2232'},
              'street': 'Kattie Turnpike',
              'suite': 'Suite 198',
              'zipcode': '31428-2261'},
  'company': {'bs': 'target end-to-end models',
              'catchPhrase': 'Centralized empowering task-force',
              'name': 'Hoeger LLC'},
  'email': 'Rey.Padberg@karina.biz',
  'id': 10,
  'name': 'Clementina DuBuque',
  'phone': '024-648-3804',
  'username': 'Moriah.Stanton',
  'website': 'ambrose.net'}]

Как красиво! Ключи словарей даже имеют визуальные отступы! Этот вывод значительно упрощает сканирование и визуальный анализ структур данных.

Примечание: Вывод, который вы увидите, будет длиннее, если вы запустите код самостоятельно. Этот блок кода сокращает вывод для удобства чтения.

Если вы предпочитаете печатать как можно меньше, то вам будет приятно узнать, что у pprint() есть псевдоним, pp():

>>> from pprint import pp
>>> pp(users)

pp() это просто оболочка вокруг pprint(), и она будет вести себя точно так же.

Примечание: Python включил этот псевдоним начиная с альфа-версии 3.8.0 2.

Однако даже в выходных данных по умолчанию может оказаться слишком много информации для первого просмотра. Возможно, все, что вам действительно нужно, - это убедиться, что вы имеете дело со списком простых объектов. Для этого вам нужно будет немного изменить выходные данные.

Для таких ситуаций существуют различные параметры, которые вы можете передать в pprint(), чтобы сделать привлекательными даже самые лаконичные структуры данных.

Изучение необязательных параметров pprint()

В этом разделе вы узнаете обо всех параметрах, доступных для pprint(). Существует семь параметров, которые вы можете использовать для настройки вашего принтера Pythonic pretty. Вам не обязательно использовать их все, некоторые из них будут более полезными, чем другие. Наиболее ценным для вас, вероятно, будет depth.

Обобщение Ваших данных: depth

Один из самых удобных параметров, с которым можно поиграть, - это depth. Следующая команда Python выведет полное содержимое users только в том случае, если структура данных находится на заданной глубине или ниже нее — при этом, конечно, все будет выглядеть красиво. Содержимое более глубоких структур данных заменяется тремя точками:

>>> pprint(users, depth=1)
[{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}]

Теперь вы можете сразу увидеть, что это действительно список словарей. Для дальнейшего изучения структуры данных вы можете увеличить глубину на один уровень, что позволит вывести все ключи верхнего уровня словарей в users:

>>> pprint(users, depth=2)
[{'address': {...},
  'company': {...},
  'email': 'Sincere@april.biz',
  'id': 1,
  'name': 'Leanne Graham',
  'phone': '1-770-736-8031 x56442',
  'username': 'Bret',
  'website': 'hildegard.org'},
 {'address': {...},
  'company': {...},
  'email': 'Shanna@melissa.tv',
  'id': 2,
  'name': 'Ervin Howell',
  'phone': '010-692-6593 x09125',
  'username': 'Antonette',
  'website': 'anastasia.net'},

  ...

 {'address': {...},
  'company': {...},
  'email': 'Rey.Padberg@karina.biz',
  'id': 10,
  'name': 'Clementina DuBuque',
  'phone': '024-648-3804',
  'username': 'Moriah.Stanton',
  'website': 'ambrose.net'}]

Теперь вы можете быстро проверить, все ли словари имеют общие ключи верхнего уровня. Это ценное замечание, особенно если перед вами стоит задача разработать приложение, которое использует подобные данные.

Предоставление пространства для ваших данных: indent

Параметр indent определяет, с каким отступом будет отображаться каждый уровень красиво напечатанного представления в выходных данных. Отступ по умолчанию равен 1, что переводится как один символ пробела:

>>> pprint(users[0], depth=1)
{'address': {...},
 'company': {...},
 'email': 'Sincere@april.biz',
 'id': 1,
 'name': 'Leanne Graham',
 'phone': '1-770-736-8031 x56442',
 'username': 'Bret',
 'website': 'hildegard.org'}

>>> pprint(users[0], depth=1, indent=4)
{   'address': {...},
    'company': {...},
    'email': 'Sincere@april.biz',
    'id': 1,
    'name': 'Leanne Graham',
    'phone': '1-770-736-8031 x56442',
    'username': 'Bret',
    'website': 'hildegard.org'}

Наиболее важной частью настройки отступов в pprint() является визуальное выравнивание всех клавиш. Размер используемого отступа зависит как от параметра indent, так и от того, где находится ключ.

Поскольку в приведенных выше примерах нет вложенности, величина отступа полностью зависит от параметра indent. В обоих примерах обратите внимание, что открывающая фигурная скобка ({) считается единицей отступа для первой клавиши. В первом примере открывающая одинарная кавычка для первого ключа ставится сразу после { без каких-либо пробелов между ними, поскольку отступ установлен равным 1.

Однако при вложении

отступ применяется к первому элементу в строке, и pprint() затем все последующие элементы выравниваются по первому элементу. Таким образом, если вы установите для параметра indent значение 4 при печати users, первый элемент будет иметь отступ в четыре символа, в то время как вложенные элементы будут иметь отступ более чем на восемь символов, поскольку отступ начинается с конца строки. первый ключ:

>>> pprint(users[0], depth=2, indent=4)
{   'address': {   'city': 'Gwenborough',
                   'geo': {...},
                   'street': 'Kulas Light',
                   'suite': 'Apt. 556',
                   'zipcode': '92998-3874'},
    'company': {   'bs': 'harness real-time e-markets',
                   'catchPhrase': 'Multi-layered client-server neural-net',
                   'name': 'Romaguera-Crona'},
    'email': 'Sincere@april.biz',
    'id': 1,
    'name': 'Leanne Graham',
    'phone': '1-770-736-8031 x56442',
    'username': 'Bret',
    'website': 'hildegard.org'}

Это всего лишь еще одна часть красивого в Python's pprint()!

Ограничение длины вашей строки: width

По умолчанию pprint() выводит не более восьмидесяти символов в строке. Вы можете настроить это значение, передав аргумент width. pprint() постараемся уместить содержимое в одной строке. Если содержимое структуры данных превышает это ограничение, то каждый элемент текущей структуры данных будет напечатан в новой строке:

>>> pprint(users[0])
{'address': {'city': 'Gwenborough',
             'geo': {'lat': '-37.3159', 'lng': '81.1496'},
             'street': 'Kulas Light',
             'suite': 'Apt. 556',
             'zipcode': '92998-3874'},
 'company': {'bs': 'harness real-time e-markets',
             'catchPhrase': 'Multi-layered client-server neural-net',
             'name': 'Romaguera-Crona'},
 'email': 'Sincere@april.biz',
 'id': 1,
 'name': 'Leanne Graham',
 'phone': '1-770-736-8031 x56442',
 'username': 'Bret',
 'website': 'hildegard.org'}

Если оставить значение ширины по умолчанию равным восьмидесяти символам, словарь в users[0]['address']['geo'] будет содержать только атрибуты 'lat' и 'lng'. Это означает, что сумма отступа и количества символов, необходимых для распечатки словаря, включая пробелы между ними, составляет менее восьмидесяти символов. Поскольку это меньше восьмидесяти символов, ширина по умолчанию pprint() помещается в одну строку.

Однако словарь в users[0]['company'] будет превышать ширину по умолчанию, поэтому pprint() помещает каждый ключ в новую строку. Это справедливо для словарей, списков, кортежей и наборов:

>>> pprint(users[0], width=160)
{'address': {'city': 'Gwenborough', 'geo': {'lat': '-37.3159', 'lng': '81.1496'}, 'street': 'Kulas Light', 'suite': 'Apt. 556', 'zipcode': '92998-3874'},
 'company': {'bs': 'harness real-time e-markets', 'catchPhrase': 'Multi-layered client-server neural-net', 'name': 'Romaguera-Crona'},
 'email': 'Sincere@april.biz',
 'id': 1,
 'name': 'Leanne Graham',
 'phone': '1-770-736-8031 x56442',
 'username': 'Bret',
 'website': 'hildegard.org'}

Если вы установите для ширины большое значение, например 160, то все вложенные словари поместятся в одной строке. Вы даже можете довести это до крайности и использовать огромное значение, например 500, которое в данном примере выводит весь словарь в одной строке:

>>> pprint(users[0], width=500)
{'address': {'city': 'Gwenborough', 'geo': {'lat': '-37.3159', 'lng': '81.1496'}, 'street': 'Kulas Light', 'suite': 'Apt. 556', 'zipcode': '92998-3874'}, 'company': {'bs': 'harness real-time e-markets', 'catchPhrase': 'Multi-layered client-server neural-net', 'name': 'Romaguera-Crona'}, 'email': 'Sincere@april.biz', 'id': 1, 'name': 'Leanne Graham', 'phone': '1-770-736-8031 x56442', 'username': 'Bret', 'website': 'hildegard.org'}

Здесь вы получаете эффект от установки для width относительно большого значения. Вы можете пойти другим путем и установить для width низкое значение, например, 1. Однако основной эффект, который это даст, заключается в том, что каждая структура данных будет отображать свои компоненты в отдельных строках. Вы по-прежнему получите визуальный отступ, который выравнивает компоненты:

>>> pprint(users[0], width=5)
{'address': {'city': 'Gwenborough',
             'geo': {'lat': '-37.3159',
                     'lng': '81.1496'},
             'street': 'Kulas '
                       'Light',
             'suite': 'Apt. '
                      '556',
             'zipcode': '92998-3874'},
 'company': {'bs': 'harness '
                   'real-time '
                   'e-markets',
             'catchPhrase': 'Multi-layered '
                            'client-server '
                            'neural-net',
             'name': 'Romaguera-Crona'},
 'email': 'Sincere@april.biz',
 'id': 1,
 'name': 'Leanne '
         'Graham',
 'phone': '1-770-736-8031 '
          'x56442',
 'username': 'Bret',
 'website': 'hildegard.org'}

Трудно заставить Python pprint() печатать некрасиво. Он сделает все возможное, чтобы было красиво!

В этом примере вы узнаете не только о width, но и о том, как принтер разбивает длинные строки текста. Обратите внимание, что users[0]["company"]["catchPhrase"], который изначально был 'Multi-layered client-server neural-net', был разделен на пробелы. Принтер избегает разделять эту строку на полуслове, поскольку это затруднит ее чтение.

Сжатие ваших длинных последовательностей: compact

Вы можете подумать, что compact относится к поведению, которое вы изучали в разделе о width — то есть, отображает ли compact структуры данных в одной строке или в отдельных строках. Однако compact влияет на вывод только в том случае, если строка проходит через строку width.

Примечание: compact влияет только на вывод последовательностей: списков, наборов и кортежей, а не словари. Это сделано намеренно, хотя и непонятно, почему было принято такое решение. Обсуждение этого вопроса продолжается в Выпуске Python #34798.

Если compact равно True, то выходные данные будут перенесены в следующую строку. По умолчанию каждый элемент отображается в отдельной строке, если длина структуры данных превышает ширину:

>>> pprint(users, depth=1)
[{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}]

>>> pprint(users, depth=1, width=40)
[{...},
 {...},
 {...},
 {...},
 {...},
 {...},
 {...},
 {...},
 {...},
 {...}]

>>> pprint(users, depth=1, width=40, compact=True)
[{...}, {...}, {...}, {...}, {...},
 {...}, {...}, {...}, {...}, {...}]

Pretty - при печати этого списка с использованием настроек по умолчанию сокращенная версия выводится в одну строку. Ограничивая width символами 40, вы заставляете pprint() выводить все элементы списка в отдельных строках. Если вы затем установите compact=True, то список будет состоять из сорока символов и будет более компактным, чем обычно.

Примечание: Имейте в виду, что установка ширины менее семи символов, что в данном случае эквивалентно выводу [{...},, по—видимому, обходит depth аргументируется полностью, и pprint() в итоге печатается все без каких-либо сворачиваний. Об этом было сообщено как об ошибке #45611.

compact полезно для длинных последовательностей с короткими элементами, которые в противном случае занимали бы много строк и делали вывод менее читабельным.

Управление вашими выводами: stream

Параметр stream ссылается на выходные данные pprint(). По умолчанию он отправляется в то же место, что и print(). В частности, это относится к sys.stdout,, который на самом деле является файловым объектом в Python. Однако вы можете перенаправить это сообщение на любой файловый объект, точно так же, как вы можете это сделать с помощью print():

>>> with open("output.txt", mode="w") as file_object:
...     pprint(users, stream=file_object)

Здесь вы создаете файловый объект с помощью open(),, а затем устанавливаете параметр stream в pprint() для этого файлового объекта . Если вы затем откроете файл output.txt, то увидите, что все, что указано в users, было красиво напечатано.

У Python действительно есть свой собственный модуль ведения журнала. Однако вы также можете использовать pprint() для отправки красивых выходных данных в файлы и использовать их в качестве журналов, если хотите.

Предотвращение сортировки по словарю: sort_dicts

Хотя словари обычно считаются неупорядоченными структурами данных, начиная с версии Python 3.6, словари упорядочиваются путем вставки.

pprint() упорядочивает ключи для печати в алфавитном порядке:

>>> pprint(users[0], depth=1)
{'address': {...},
 'company': {...},
 'email': 'Sincere@april.biz',
 'id': 1,
 'name': 'Leanne Graham',
 'phone': '1-770-736-8031 x56442',
 'username': 'Bret',
 'website': 'hildegard.org'}

>>> pprint(users[0], depth=1, sort_dicts=False)
{'id': 1,
 'name': 'Leanne Graham',
 'username': 'Bret',
 'email': 'Sincere@april.biz',
 'address': {...},
 'phone': '1-770-736-8031 x56442',
 'website': 'hildegard.org',
 'company': {...}}

Если для параметра sort_dicts не задано значение False, pprint() в Python сортирует ключи в алфавитном порядке. Это обеспечивает согласованность, удобочитаемость и — что ж — привлекательность выходных данных для словарей!

Когда pprint() был впервые реализован, словари были неупорядочены. Без упорядочения ключей в алфавитном порядке ключи словаря теоретически могли отличаться при каждом вводе.

Приукрашиваем Свои цифры: underscore_numbers

Параметр underscore_numbers - это функция, введенная в Python 3.10, которая делает длинные числа более удобочитаемыми. Учитывая, что пример, который вы использовали до сих пор, не содержит длинных чисел, вам понадобится новый пример, чтобы опробовать его:

>>> number_list = [123456789, 10000000000000]
>>> pprint(number_list, underscore_numbers=True)
[123_456_789, 10_000_000_000_000]

Если вы попытались выполнить этот вызов для pprint() и получили сообщение об ошибке, вы не одиноки. С октября 2021 года этот аргумент не работает при прямом вызове pprint(). Сообщество Python быстро заметило это, и это было исправлено в декабре 2021 года в выпуске исправлений 3.10.1. Ребята из Python заботятся о своем прекрасном принтере! Они, вероятно, исправят это к тому времени, когда вы будете читать это руководство.

Если underscore_numbers не работает при прямом вызове pprint() и вам действительно нужны красивые цифры, есть обходной путь: когда вы создаете свой собственный объект PrettyPrinter, этот параметр должен работать просто как это происходит в приведенном выше примере.

Далее вы узнаете, как создать объект PrettyPrinter.

Создание пользовательского объекта PrettyPrinter

Можно создать экземпляр PrettyPrinter, который имеет определенные вами значения по умолчанию. Как только у вас будет этот новый экземпляр вашего пользовательского объекта PrettyPrinter, вы сможете использовать его, вызвав метод .pprint() для экземпляра PrettyPrinter:

>>> from pprint import PrettyPrinter
>>> custom_printer = PrettyPrinter(
...     indent=4,
...     width=100,
...     depth=2,
...     compact=True,
...     sort_dicts=False,
...     underscore_numbers=True
... )
...
>>> custom_printer.pprint(users[0])
{   'id': 1,
    'name': 'Leanne Graham',
    'username': 'Bret',
    'email': 'Sincere@april.biz',
    'address': {   'street': 'Kulas Light',
                   'suite': 'Apt. 556',
                   'city': 'Gwenborough',
                   'zipcode': '92998-3874',
                   'geo': {...}},
    'phone': '1-770-736-8031 x56442',
    'website': 'hildegard.org',
    'company': {   'name': 'Romaguera-Crona',
                   'catchPhrase': 'Multi-layered client-server neural-net',
                   'bs': 'harness real-time e-markets'}}
>>> number_list = [123456789, 10000000000000]
>>> custom_printer.pprint(number_list)
[123_456_789, 10_000_000_000_000]

С помощью этих команд вы:

  • Импортировано PrettyPrinter, которое является определением класса
  • Создал новый экземпляр этого класса с определенными параметрами
  • Напечатано первый пользователь в users
  • Определил список из пары длинных чисел
  • Напечатано number_list, что также демонстрирует underscore_numbers в действии

Обратите внимание, что аргументы, которые вы передали в PrettyPrinter, в точности совпадают с аргументами по умолчанию pprint(), за исключением того, что вы пропустили первый параметр. В pprint() это объект, который вы хотите напечатать.

Таким образом, вы можете иметь различные настройки принтера — возможно, некоторые из них будут использоваться в разных потоках — и вызывать их, когда они вам понадобятся.

Получаем красивую строку с pformat()

Что делать, если вы не хотите отправлять красивые выходные данные pprint() в поток? Возможно, вы хотите выполнить некоторое сопоставление регулярных выражений и заменить определенные ключи. Что касается обычных словарей, то, возможно, вам захочется убрать квадратные скобки и кавычки, чтобы они выглядели еще более удобочитаемыми.

Что бы вы ни хотели сделать с предварительным выводом строки, вы можете получить ее с помощью pformat():

>>> from pprint import pformat
>>> address = pformat(users[0]["address"])
>>> chars_to_remove = ["{", "}", "'"]
>>> for char in chars_to_remove:
...     address = address.replace(char, "")
...
>>> print(address)
city: Gwenborough,
 geo: lat: -37.3159, lng: 81.1496,
 street: Kulas Light,
 suite: Apt. 556,
 zipcode: 92998-3874

pformat() это инструмент, который вы можете использовать для переключения между принтером pretty и потоком вывода.

Другим вариантом использования этого может быть, если вы создаете API и хотите отправить красивое строковое представление строки JSON. Ваши конечные пользователи, вероятно, оценили бы это по достоинству!

Обработка рекурсивных структур данных

pprint() в Python является рекурсивным, что означает, что он выводит все содержимое словаря, все содержимое любых дочерних словарей и так далее.

Спросите себя, что происходит, когда рекурсивная функция сталкивается с рекурсивной структурой данных. Представьте, что у вас есть словарь A и словарь B:

  • A имеет один атрибут .link, который указывает на B.
  • B имеет один атрибут, .link, который указывает на A.

Если ваша воображаемая рекурсивная функция не сможет обработать эту циклическую ссылку, она никогда не завершит печать! Она напечатает A, а затем ее дочернюю функцию B. Но у B также есть A в детстве, так что это продолжалось бы до бесконечности.

К счастью, как обычная функция print(), так и функция pprint() прекрасно справляются с этим:

>>> A = {}
>>> B = {"link": A}
>>> A["link"] = B
>>> print(A)
{'link': {'link': {...}}}
>>> from pprint import pprint
>>> pprint(A)
{'link': {'link': <Recursion on dict with id=3032338942464>}}

В то время как обычный print() в Python просто сокращает вывод, pprint() явно уведомляет вас о рекурсии, а также добавляет идентификатор словаря.

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

Заключение

Вы изучили основное использование модуля pprint в Python и некоторые способы работы с pprint() и PrettyPrinter. Вы обнаружите, что pprint() особенно удобен, когда вы разрабатываете что-то, связанное со сложными структурами данных. Возможно, вы разрабатываете приложение, использующее незнакомый API. Возможно, у вас есть хранилище данных, заполненное глубоко вложенными файлами JSON. Все это ситуации, в которых pprint может пригодиться.

В этом руководстве вы узнали, как:

  • Импортировать pprint для использования в ваших программах
  • Используйте pprint() вместо обычного print()
  • Ознакомьтесь со всеми параметрами, которые вы можете использовать для настройки красивого печатного текста
  • Получите отформатированный результат в виде строки перед его печатью
  • Создайте пользовательский экземпляр PrettyPrinter
  • Распознавать рекурсивные структуры данных и как pprint() с ними обращаться

Чтобы помочь вам разобраться с функцией и параметрами, вы использовали пример структуры данных, представляющей некоторых пользователей. Вы также рассмотрели некоторые ситуации, в которых вы могли бы использовать pprint().

Поздравляем! Теперь вы лучше подготовлены к работе со сложными данными, используя модуль Python pprint.

Back to Top