Please don't use the pprint printing dictionary anymore

Keywords: Go Mobile Android Python network

WeChat official account: Python programming time

Online blog address: http://python.iswbm.com/en/latest/c02/c02_14.html

1. make complaints about problems

You should be familiar with pprint in Python, right?

Most people will recommend you to use this product when you search on search engines about how to print beautiful dictionaries or format strings.

For example, the following json string or dictionary (I found it on the Internet) can't be read without formatting and beautifying it.

[{"id":1580615,"name":"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, tender head"},{"id":1540629,"name":"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"Fish fighting 271934. Don't miss it. There are the best chickens here"}]

If you don't want to see a bunch of dense words, use pprint, which is highly recommended by everyone, to see what effect it will have (as shown in Python 2, Python 3 is different).

>>> info=[{"id":1580615,"name":"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head"},{"id":1540629,"name":"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"Fish fighting 271934. Don't miss it. There are the best chickens here"}]
>>> 
>>> from pprint import pprint
>>> pprint(info)
[{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92',
  'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
  'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
  'id': 1580615,
  'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b',
  'packageName': 'com.renren.mobile.android',
  'size': 21803987,
  'stars': 2},
 {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf',
  'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
  'iconUrl': 'app/com.ct.client/icon.jpg',
  'id': 1540629,
  'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84',
  'packageName': 'com.ct.client',
  'size': 4794202,
  'stars': 2}]

It seems to have some effect. It's really "artifact".

But you tell me, what are these things? What was meant to improve readability is now completely unreadable.

Fortunately, I understand the coding of Python 2. I know that the default string format (without u) in Python 2 is str type and also bytes type. It is stored in bytes.

OK, it seems that I'm wrong. I'll use unicode type to define Chinese string instead.

>>> info = [{"id":1580615,"name":u"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, tender head"},{"id":1540629,"name":u"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"Fish fighting 271934. Don't miss it. There are the best chickens here"}]
>>> 
>>> from pprint import pprint
>>> pprint(info)
[{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752',
  'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
  'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
  'id': 1580615,
  'name': u'\u76ae\u7684\u561b',
  'packageName': 'com.renren.mobile.android',
  'size': 21803987,
  'stars': 2},
 {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f',
  'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
  'iconUrl': 'app/com.ct.client/icon.jpg',
  'id': 1540629,
  'name': u'\u4e0d\u5b58\u5728\u7684',
  'packageName': 'com.ct.client',
  'size': 4794202,
  'stars': 2}]

It's better, but seeing the following, I'm broken. How can I know what the devil is? Am I too busy? When I was a computer?

u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f'

In addition, we know that the strict requirement of json is to use double quotation marks. When I define a dictionary, I also use double quotation marks. Why is the single quotation mark printed out? I'm too hard, am I unable to control my own code?

Here, we know two problems brought by pprint:

  1. Can't print Chinese in Python 2
  2. Unable to output formatted content in JSON standard format (double quotes)

2. Problem solving

Print Chinese

If you use Python 3, you will find that Chinese can be displayed normally.

# Python3.7
>>> info = [{"id":1580615,"name":u"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head"},{"id":1540629,"name":u"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"Fish fighting 271934. Don't miss it. There are the best chickens here"}]
>>> 
>>> from pprint import pprint
>>> pprint(info)
[{'des': '2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head',
  'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
  'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
  'id': 1580615,
  'name': 'Leather',
  'packageName': 'com.renren.mobile.android',
  'size': 21803987,
  'stars': 2},
 {'des': 'Fish fighting 271934. Don't miss it. There are the best chickens here',
  'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
  'iconUrl': 'app/com.ct.client/icon.jpg',
  'id': 1540629,
  'name': 'Nonexistent',
  'packageName': 'com.ct.client',
  'size': 4794202,
  'stars': 2}]
>>> 

But most of the time (on some servers in the company) you can't choose which version of Python you want to use. I could have chosen not to use it because there are better alternatives (this will be discussed later).

But out of curiosity, I just wrote an article about coding two days ago. I think I am proficient in coding, so I want to solve this problem.

Simply look at the source code of pprint. I really found a solution. If you want to challenge it, just stop here and study how to implement it. I believe it will help you to read the source code.

Here is my solution for your reference:

Write a printer object of your own, inherited from PrettyPrinter (printer used by pprint)

And copy the format method to determine whether the string object passed in is of str type. If it is not of str type but of unicode type, it is encoded as str type with uft8.

# coding: utf-8
from pprint import PrettyPrinter

# Inherit PrettyPrinter, copy format method
class MyPrettyPrinter(PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, unicode):
            return (object.encode('utf8'), True, False)
        return PrettyPrinter.format(self, object, context, maxlevels, level)

info = [{"id":1580615,"name":u"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head"},{"id":1540629,"name":u"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"Fish fighting 271934. Don't miss it. There are the best chickens here"}]

MyPrettyPrinter().pprint(info)

The output is as follows, which has solved the problem of Chinese display:

Print double quotes

After solving the Chinese problem, let's see how to make pprint print double quotes.

When instantiating the PrettyPrinter object, you can receive a stream object, which indicates where you want to output the content. The default is to use the stream sys.stdout, which is standard output.

Now we need to modify the output, that is, replace the single quotation mark with double quotation mark.

Then we can define a stream type object by ourselves. The object does not need to inherit any parent class, as long as you implement the write method.

With ideas, you can start to write code, as follows:

# coding: utf-8
from pprint import PrettyPrinter

class MyPrettyPrinter(PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, unicode):
            return (object.encode('utf8'), True, False)
        return PrettyPrinter.format(self, object, context, maxlevels, level)

class MyStream():
    def write(self, text):
        print text.replace('\'', '"')

info = [{"id":1580615,"name":u"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head"},{"id":1540629,"name":u"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"Fish fighting 271934. Don't miss it. There are the best chickens here"}]
MyPrettyPrinter(stream=MyStream()).pprint(info)

Try to carry out the next, my God, how is it like this.

[
{
"des"
: 
2011-2017 your tietouwa has been here. The largest real name SNS network platform in China
,
  "downloadUrl": 
"app/com.renren.mobile.android/com.renren.mobile.android.apk"
,
  "iconUrl": 
"app/com.renren.mobile.android/icon.jpg"
,
  "id": 
1580615
,
  "name": 
Leather
,
  "packageName": 
"com.renren.mobile.android"
,
  "size": 
21803987
,
  "stars": 
2
}
,
 
{
"des"
: 
Fish fighting 271934. Don't miss it. There are the best chickens here
,
  "downloadUrl": 
"app/com.ct.client/com.ct.client.apk"
,
  "iconUrl": 
"app/com.ct.client/icon.jpg"
,
  "id": 
1540629
,
  "name": 
Nonexistent
,
  "packageName": 
"com.ct.client"
,
  "size": 
4794202
,
  "stars": 
2
}
]

After some research, we know that the print function will add a line break after the printed content by default.

How to make the print function print without line wrapping?

The method is very simple, but I believe many people don't know, just add a comma after the content of print.

It's like this.

If you know the problem, modify the code

# coding: utf-8
from pprint import PrettyPrinter

class MyPrettyPrinter(PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, unicode):
            return (object.encode('utf8'), True, False)
        return PrettyPrinter.format(self, object, context, maxlevels, level)

class MyStream():
    def write(self, text):
        print text.replace('\'', '"'),

info = [{"id":1580615,"name":u"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, green head"},{"id":1540629,"name":u"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"Fish fighting 271934. Don't miss it. There are the best chickens here"}]

MyPrettyPrinter(stream=MyStream()).pprint(info)

It's not easy to succeed at last.

3. Why bother

Through the above twists and turns, I finally realized my dream of demand.

The price is that it took me two hours to realize it. For Xiaobai, I may not have confidence or patience to do such a thing.

So what I want to say is that the pprint under Python 2 is really no longer used.

Why do I say that? Because there is a better alternative. Life is too short. Since Python is used, how can it be simple and how can it be used? Why bother yourself? What can be solved by one line of code is to write two classes. Isn't that asking for trouble? Am I scolding myself?

If you are willing to abandon pprint, I recommend json.dumps. I promise you don't want to use pprint any more.

Print Chinese

In fact, it can't print Chinese. It's a big hole brought by Python 2. It's not all about pprint.

But the same problem, in json.dumps, it's better to add a parameter. It's not too much simpler than pprint.

Specific code examples are as follows:

>>> info = [{"id":1580615,"name":"Leather","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, tender head"},{"id":1540629,"name":"Nonexistent","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"Fish fighting 271934. Don't miss it. There are the best chickens here"}]
>>> 
>>> import json
>>> 
>>> 
>>> print json.dumps(info, indent=4, ensure_ascii=False)
[
    {
        "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", 
        "iconUrl": "app/com.renren.mobile.android/icon.jpg", 
        "name": "Leather", 
        "stars": 2, 
        "packageName": "com.renren.mobile.android", 
        "des": "2011-2017 Your tietouwa has been here. China's largest real name system SNS Network platform, tender head", 
        "id": 1580615, 
        "size": 21803987
    }, 
    {
        "downloadUrl": "app/com.ct.client/com.ct.client.apk", 
        "iconUrl": "app/com.ct.client/icon.jpg", 
        "name": "Nonexistent", 
        "stars": 2, 
        "packageName": "com.ct.client", 
        "des": "Fish fighting 271934. Don't miss it. There are the best chickens here", 
        "id": 1540629, 
        "size": 4794202
    }
]
>>> 

There are two key parameters of json.dumps:

  • indent=4: indented by 4 spaces
  • ensure_ascii=False: receive non ASCII characters so that you can use Chinese

Compared with pprint, json.dumps is perfect:

  1. Two parameters can achieve all my requirements (print Chinese and double quotes)
  2. Even in Python 2, you don't need to write in u 'Chinese' to use Chinese
  3. Python 2 and python 3 are written in exactly the same way, so there is no need to consider compatibility for this

4. Summary

In order to prove how difficult it is for pprint to realize those two requirements, I spent a lot of time to study the source code of pprint (all kinds of processing are quite complicated), but I can also get some results in the end.

Here's the sharing of this article. Reading this article, I think you can get three knowledge points

  1. Core point: don't use pprint under Python 2
  2. If you really want to use it and have the same transformation requirements, please refer to my implementation
  3. Comma can be added after the print statement in Python 2

above. I hope this article can help you.

Posted by hostfreak on Fri, 08 May 2020 18:16:20 -0700