打印中文dict list的各种姿势

来源:转载

在开发过程中,我们经常需要打印一些变量的值,便于调试。这个时候就会发现如果在dict list这些容器中,如果包含中文字符,不管是str类型,还是unicode类型,都打印不出来。如下:

>>> print {'name': '张三'}{'name': '/xd5/xc5/xc8/xfd'}>>> print {'name': u'张三'}{'name': u'/u5f20/u4e09'}

当然,作为凡人的我是在无法脑补这些十六进制的意思,每次转移一下也很麻烦,有没有办法一劳永逸呢。google了一把,发现还是有很多姿势的。

注意:本文实验主要基于win7,Python2.7,运行环境如下

>>> import sys,locale>>> sys.getdefaultencoding()'ascii'>>> locale.getdefaultlocale()('zh_CN', 'cp936')>>> sys.stdin.encoding'cp936'>>> sys.stdout.encoding'cp936'

  本文地址:http://www.cnblogs.com/xybaby/p/7854126.html

str类型的中文

首先让我们分析一下为什么无法包含中文的container(dict list tuple)

 >>> data = {'严': 1, 2: ['如'], 3:'玉'}

>>> data{2: ['/xc8/xe7'], 3: '/xd3/xf1', '/xd1/xcf': 1}>>> print data{2: ['/xc8/xe7'], 3: '/xd3/xf1', '/xd1/xcf': 1}>>> print data[3]玉

  上面data在key value中包含中文,而且也有嵌套的list,后文都使用这个data

可以看到不管是直接输出data(调用dict.__repr__),还是print data(调用dict.__str__),都无法输出中文,而是像str.__repr__的结果。即调用容器的__str__时,事实上调用的是容器元素的__repr__方法,这个很好验证:

>>> class OBJ(object):... def __str__(self):...    return 'OBJ str'... def __repr__(self):...    return 'OBJ repr'...>>> lst = [OBJ()]>>> print lst[OBJ repr]>>>

OBJ这个自定义的类,__str__ __repr__的方法实现不一样,当作为container(list)的元素时,明显调用的是OBJ.__repr__

在stackoverflow上的一个问题print-a-list-that-contains-chinese-characters-in-python给出了答案

When you print foo, what gets printed out is str(foo).However, if foo is a list, str(foo) uses repr(bar) for each element bar, not str(bar).

当然,这个问题早就被人发现了,在PEP3140 str(container) should call str(item), not repr(item) ,在这个提议中,就建议在打印容器的时候,使用__str__而不是__repr__。但是被Guido(Python之父)无情的拒绝了,原因是:

Guido said this would cause too much disturbance too close to beta

虽然提议被reject了,但是需求还是照样存在的啊,于是有了各种解决办法

第一种姿势:逐个打印

直接print容器中的元素

>>> lst = ['张三', '李四']>>> print '[' + ', '.join(["asdf", "中文"]) + ']'[asdf, 中文]>>> for k, v in {'name': '张三'}.items():... print k, v...name 张三

对于简单的容器对象,还是很方便的,但是对于嵌套的容器对象,比如上面data的例子,就很麻烦了

第二种姿势: json dumps

这个方法在网上推荐得较多

>>> import json>>> dumped_data = json.dumps(data, encoding = 'gbk', ensure_ascii=False)>>> print dumped_data{"2": ["如"], "3": "玉", "严": 1}

可以看到,虽然打印出了中文,但是2 3都被加上了引号,感觉怪怪的

需要注意的是上面的两个参数(encoing ensure_ascii), 这两个参数都有默认参数(encoding = 'utf-8', ensure_ascii=True),跟我们这里使用的都不一样。

>>> dumped_data = json.dumps(data) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "D:/Python27.9/lib/json/__init__.py", line 243, in dumps return _default_encoder.encode(obj) File "D:/Python27.9/lib/json/encoder.py", line 207, in encode chunks = self.iterencode(o, _one_shot=True) File "D:/Python27.9/lib/json/encoder.py", line 270, in iterencode return _iterencode(o, 0)UnicodeDecodeError: 'utf8' codec can't decode byte 0xc8 in position 0: invalid continuation byte

当然,为什么这里爆出了UnicodeDecodeError,可以参考这篇文章《不想再被鄙视?那就看进来! 一文搞懂Python2字符编码》

ensure_ascii参数也很关键

>>> dumped_data = json.dumps(data, encoding = 'gbk')>>> print dumped_data{"2": ["/u5982"], "3": "/u7389", "/u4e25": 1}

python document是有描述的;

If ensure_ascii is True (the default), all non-ASCII characters in the output are escaped with /uXXXX sequences, and the result is a str instance consisting of ASCII characters only.

第三种姿势: repr string_escape

>>> decoded_data = repr(data).decode('string_escape')>>> print decoded_data{2: ['如'], 3: '玉', '严': 1}

既然repr的输出是十六进制的str,那么就可以使用string_escape进行转换,具体也可以参见上文

第四种姿势:PEP3140

虽然PEP3140被reject了,但我们还是可以利用其思想吧,那就是强制调用str.__str__而不是str.__repr__

 1 class ForceStr(str): 2 def __repr__(self): 3 return super(ForceStr, self).__str__() 4 5 def switch_container( data ): 6 ret = None 7 if isinstance(data, str): 8 ret = ForceStr(data) 9 elif isinstance(data, list) or isinstance(data, tuple):10 ret = [switch_container(var) for var in data]11 elif isinstance(data, dict):12 ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())13 else:14 ret = data15 return ret

>>> switched_data = switch_container(data)>>> print switched_data{2: [如], 3: 玉, 严: 1}>>> switched_data{2: [如], 3: 玉, 严: 1}

ForceStr继承自str,然后ForceStr.__repr__调用str.__str__。然后递归将容器里面的str类型的元素替换成ForceStr。可以看到,能够顺序打印出中文,格式也没有问题

unicode类型的中文

基本姿势于上一章节是一样的,下面直接给出答案

同上第二种姿势

>>> udata = {u'严': 1, 2: [u'如'], 3:u'玉'}>>> print json.dumps(udata, encoding = 'gbk', ensure_ascii=False){"2": ["如"], "3": "玉", "严": 1}

同上第三种姿势

>>> print repr(udata).decode('unicode_escape'){2: [u'如'], 3: u'玉', u'严': 1}>>>

同上第四种姿势

 1 def switch_container( data ): 2 ret = None 3 if isinstance(data, unicode): 4 ret = ForceStr(data.encode(sys.stdout.encoding)) 5 elif isinstance(data, list) or isinstance(data, tuple): 6 ret = [switch_container(var) for var in data] 7 elif isinstance(data, dict): 8 ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems()) 9 else:10 ret = data11 return ret

>>>>>> print switch_container(udata){2: [如], 3: 玉, 严: 1}

当str与unicode中文并存时

同上第二种姿势

>>> data[4] = u'啊'>>> print json.dumps(data, encoding = 'gbk', ensure_ascii=False){"2": ["如"], "3": "玉", "4": "啊", "严": 1}

同上第三种姿势

>>> print repr(data).decode('string_escape'){2: ['如'], 3: '玉', 4: u'/u554a', '严': 1}

呃,unicode中文打印不出来

>>> print repr(data).decode('unicode_escape')Traceback (most recent call last): File "<stdin>", line 1, in <module>UnicodeEncodeError: 'gbk' codec can't encode character u'/xc8' in position 6: illegal multibyte sequence>>>

擦,也许有正确的姿势,不过我没有试出来

同上第四种姿势

 1 def switch_container( data ): 2 ret = None 3 if isinstance(data, str): 4 ret = ForceStr(data) 5 elif isinstance(data, unicode): 6 ret = ForceStr(data.encode(sys.stdout.encoding)) 7 elif isinstance(data, list) or isinstance(data, tuple): 8 ret = [switch_container(var) for var in data] 9 elif isinstance(data, dict):10 ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())11 else:12 ret = data13 return ret

>>> print switch_container(data){2: [如], 3: 玉, 4: 啊, 严: 1}

总结

json.dumps版本还算可以,能够处理str中文,unicode中文, str与unicode中文并存三种情况,不过显示结果与真实有点差异

string_escape(unicode_escape)只使用只有str(unicode)中文的情况,使用较为受限

自己实现的switch_container版本,能够友好支持str中文,unicode中文,str与unicode中文并存三种情况

str与unicode并存真是一件蛋疼的事情!

reference

print-a-list-that-contains-chinese-characters-in-python

不想再被鄙视?那就看进来! 一文搞懂Python2字符编码

分享给朋友:
您可能感兴趣的文章:
随机阅读: