zoukankan      html  css  js  c++  java
  • python2中打印列表与字典内的中文字符

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

    >>> print {'name': '张三'}
    {'name': 'xe5xbcxa0xe4xb8x89'}
    >>> print {'name': u'张三'}
    {'name': u'u5f20u4e09'}

    当然,我们很难自行脑补这些十六进制的意思,每次转移一下也很麻烦,有没有办法一劳永逸呢。

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

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

    >>> data = {'严': 1, 2: ['如'], 3:'玉'}
    >>> data
    {'xe4xb8xa5': 1, 3: 'xe7x8ex89', 2: ['xe5xa6x82']}
    >>> print data
    {'xe4xb8xa5': 1, 3: 'xe7x8ex89', 2: ['xe5xa6x82']}
    >>> 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__的方法实现不一样,当作为容器(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 = 'UTF-8', ensure_ascii=False)
    >>> print dumped_data
    {"严": 1, "3": "玉", "2": ["如"]}

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

    需要注意的是上面的两个参数(encoing ensure_ascii), 这两个参数都有默认参数(encoding = 'utf-8', ensure_ascii=True)

    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
    {'严': 1, 3: '玉', 2: ['如']}

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

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


    class ForceStr(str):
    def __repr__(self):
    return super(ForceStr, self).__str__()

    def switch_container( data ):
    ret = None
    if isinstance(data, str):
    ret = ForceStr(data)
    elif isinstance(data, list) or isinstance(data, tuple):
    ret = [switch_container(var) for var in data]
    elif isinstance(data, dict):
    ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())
    else:
    ret = data
    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。可以看到,能够顺序打印出中文,格式也没有问题

  • 相关阅读:
    C++中逻辑操作符的重载分析
    不要62
    P1052 过河
    P1029 最大公约数和最小公倍数问题
    P1345 [USACO5.4]奶牛的电信Telecowmunication
    Dining
    Dinic
    [Scoi2015]小凸玩矩阵
    12.16
    bzoj 3529
  • 原文地址:https://www.cnblogs.com/niansi/p/8018810.html
Copyright © 2011-2022 走看看