You are currently viewing Украсьте свои структуры данных красивой печатью на Python

Украсьте свои структуры данных красивой печатью на Python

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

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

К концу этого урока вы будете:

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

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

Бесплатный бонус: Нажмите здесь, чтобы получить шпаргалку по Python и изучить основы Python 3, такие как работа с типами данных, словарями, списками и функциями Python.

Понимание необходимости красивого шрифта 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 alpha 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 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'}

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

Однако словарь at 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.

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

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

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

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

Довольно-печать этого списка с использованием настроек по умолчанию выводит сокращенную версию в одной строке. Ограничившись width40 символами, вы заставляете 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, Python pprint() сортирует ключи в алфавитном порядке. Это сохраняет выходные данные для словарей последовательными, читабельными и—ну—красивыми!

Когда 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 быстро заметило это, и это было исправлено в выпуске 3.10.1 от декабря 2021 года исправлена ошибка. Ребята из 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() это инструмент, который вы можете использовать , чтобы попасть между симпатичным принтером и выходным потоком.

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

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

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

Спросите себя, что происходит, когда рекурсивная функция сталкивается с рекурсивной структурой данных. Представьте, что у вас есть словарь 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>}}

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

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

Вывод

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

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

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

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

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