首發於微信公衆號:Python編程時光html
在線博客地址:python.iswbm.com/en/latest/c…python
Python 裏的 pprint 你應該很熟悉了吧?android
隨便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串時,大部分人都會推薦你使用這貨 。編程
好比這下面這個 json 字符串或者說字典(我隨便在網上找的),若是不格式化美化一下,根本沒法閱讀。json
[{"id":1580615,"name":"皮的嘛","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 你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":"不存在的","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":"鬥魚271934 走過路過不要錯過,這裏有最好的雞兒"}]
複製代碼
若是你不想看到一堆密密麻麻的字,那就使用大夥都極力推薦的 pprint 看下什麼效果(如下在 Python 2 中演示,Python 3 中是不同的效果)。bash
>>> info=[{"id":1580615,"name":"皮的嘛","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 你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":"不存在的","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":"鬥魚271934 走過路過不要錯過,這裏有最好的雞兒"}]
>>>
>>> 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}]
複製代碼
好像有點效果,真的是 「神器」呀。服務器
可是你告訴我, \xe4\xbd\xa0\xe7\x9a 這些是什麼玩意?原本想提升可讀性的,如今變成徹底不可讀了。微信
好在我懂點 Python 2 的編碼,知道 Python 2 中默認(不帶u)的字符串格式都是 str 類型,也是 bytes 類型,它是以 byte 存儲的。網絡
行吧,好像是我錯了,我改了下,使用 unicode 類型來定義中文字符串吧。app
>>> info = [{"id":1580615,"name":u"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":u"不存在的","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"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
>>>
>>> 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}]
複製代碼
確實是有好點了,可是看到下面這些,我崩潰了,我哪裏知道這是什麼鬼,難道是我太菜了嗎?當我是計算機呀?
u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f'
複製代碼
除此以外,咱們知道 json 的嚴格要求必須使用 雙引號,而我定義字典時,也使用了雙引號了,爲何打印出來的爲何是 單引號?我也太難了吧,我連本身的代碼都沒法控制了嗎?
到這裏,咱們知道了 pprint 帶來的兩個問題:
若是你是在 Python 3 下使用,你會發現中文是能夠正常顯示的。
# Python3.7
>>> info = [{"id":1580615,"name":u"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":u"不存在的","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"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
>>>
>>> from pprint import pprint
>>> pprint(info)
[{'des': '2011-2017你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青',
'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
'id': 1580615,
'name': '皮的嘛',
'packageName': 'com.renren.mobile.android',
'size': 21803987,
'stars': 2},
{'des': '鬥魚271934走過路過不要錯過,這裏有最好的雞兒',
'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
'iconUrl': 'app/com.ct.client/icon.jpg',
'id': 1540629,
'name': '不存在的',
'packageName': 'com.ct.client',
'size': 4794202,
'stars': 2}]
>>>
複製代碼
可是不少時候(在公司的一些服務器)你沒法選擇本身使用哪一個版本的 Python,原本我能夠選擇不用的,由於有更好的替代方案(這個後面會講)。
可是我出於獵奇,正好前兩天不是寫過一篇關於 編碼 的文章嗎,我自認爲本身對於 編碼仍是掌握比較熟練的,就想着來解決一下這個問題。
索性就來看下 pprint 的源代碼,還真被我找到了解決方法,若是你也想挑戰一下,不防在這裏停住,本身研究一下如何實現,我相信對你閱讀源碼會有幫助。
如下是個人解決方案,供你參考:
寫一個本身的 printer 對象,繼承自 PrettyPrinter (pprint 使用的printer)
而且複寫 format 方法,判斷傳進來的字符串對象是否 str 類型,若是不是 str 類型,而是 unicode 類型,就用 uft8 編碼成 str 類型。
# coding: utf-8
from pprint import PrettyPrinter
# 繼承 PrettyPrinter,複寫 format 方法
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"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":u"不存在的","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"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
MyPrettyPrinter().pprint(info)
複製代碼
輸出以下,已經解決了中文的顯示問題:
解決了中文問題後,再來看看如何讓 pprint 打印雙引號。
在實例化 PrettyPrinter 對象的時候,能夠接收一個 stream 對象,它表示你要將內容輸出到哪裏,默認是使用 sys.stdout 這個 stream,也就是標準輸出。
如今咱們要修改輸出的內容,也就是將輸出的單引號替換成雙引號。
那咱們徹底能夠本身定義一個 stream 類型的對象,該對象不須要繼承任何父類,只要你實現 write 方法就能夠。
有了思路,就能夠開始寫代碼了,以下:
# 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"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":u"不存在的","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"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
MyPrettyPrinter(stream=MyStream()).pprint(info)
複製代碼
嘗試執行了下,個人天,怎麼是這樣子的。
[
{
"des"
:
2011-2017你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青
,
"downloadUrl":
"app/com.renren.mobile.android/com.renren.mobile.android.apk"
,
"iconUrl":
"app/com.renren.mobile.android/icon.jpg"
,
"id":
1580615
,
"name":
皮的嘛
,
"packageName":
"com.renren.mobile.android"
,
"size":
21803987
,
"stars":
2
}
,
{
"des"
:
鬥魚271934走過路過不要錯過,這裏有最好的雞兒
,
"downloadUrl":
"app/com.ct.client/com.ct.client.apk"
,
"iconUrl":
"app/com.ct.client/icon.jpg"
,
"id":
1540629
,
"name":
不存在的
,
"packageName":
"com.ct.client"
,
"size":
4794202
,
"stars":
2
}
]
複製代碼
通過一番研究,才知道是由於 print 函數默認會將打印的內容後面加個 換行符。
那如何將使 print 函數打印的內容,不進行換行呢?
方法很簡單,可是我相信不少人都不知道,只要在 print 的內容後加一個 逗號 就行。
就像下面這樣。
知道了問題所在,再修改下代碼
# 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"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":u"不存在的","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"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
MyPrettyPrinter(stream=MyStream()).pprint(info)
複製代碼
終於成功了,太不容易了吧。
經過上面的一番折騰,我終於實現了我 求之不得 的需求。
代價就是我整整花費了兩個小時,才得以實現,而對於小白來講,可能沒有信心,也沒有耐心去作這樣的事情。
因此我想說的是,Python 2 下的 pprint ,真的不要再用了。
爲何我要用這麼 說,由於明明有更好的替代品,人生苦短,既然用了 Python ,固然是怎麼簡單怎麼來咯,何須爲難本身呢,一行代碼能夠解決的事情,恰恰要去寫兩個類,那不是自討苦吃嗎?(我這是在罵本身嗎?
若是你願意拋棄 pprint ,那我推薦你用 json.dumps ,我保證你不再想用 pprint 了。
其實沒法打印中文,是 Python 2 引來的大坑,並不能全怪 pprint 。
可是一樣的問題,在 json.dumps 這裏,卻只要加個參數就行了,可比 pprint 簡單得不要太多。
具體的代碼示例以下:
>>> info = [{"id":1580615,"name":"皮的嘛","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你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青"},{"id":1540629,"name":"不存在的","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":"鬥魚271934走過路過不要錯過,這裏有最好的雞兒"}]
>>>
>>> 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": "皮的嘛",
"stars": 2,
"packageName": "com.renren.mobile.android",
"des": "2011-2017你的鐵頭娃一直在這兒。中國最大的實名制SNS網絡平臺,嫩頭青",
"id": 1580615,
"size": 21803987
},
{
"downloadUrl": "app/com.ct.client/com.ct.client.apk",
"iconUrl": "app/com.ct.client/icon.jpg",
"name": "不存在的",
"stars": 2,
"packageName": "com.ct.client",
"des": "鬥魚271934走過路過不要錯過,這裏有最好的雞兒",
"id": 1540629,
"size": 4794202
}
]
>>>
複製代碼
json.dumps 的關鍵參數有兩個:
與 pprint 相比 json.dumps 能夠說完勝:
u'中文'
這種寫法原本很簡單的一個觀點,我爲了證實 pprint 實現那兩個需求有多麼困難,花了不少的時間去研究了 pprint 的源碼(各類處理其實仍是挺複雜的),不過好在最後也能有所收穫。
本文的分享就到這裏,閱讀本文,我認爲你能夠獲取到三個知識點
以上。但願此文能對你有幫助。