为什么我们不应该在py脚本中使用sys.setdefaultencoding(“utf-8”)?

时间:2010-09-30 07:46:09

标签: python encoding utf-8 python-2.x sys

我见过很少的py脚本在脚本的顶部使用它。在什么情况下应该使用它?

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

4 个答案:

答案 0 :(得分:132)

根据文档:这允许您从默认ASCII切换到其他编码,例如UTF-8,只要必须将字符串缓冲区解码为unicode,Python运行时就会使用这些编码。

此功能仅在Python启动时可用,当Python扫描环境时。必须在系统范围的模块sitecustomize.py中调用它。在评估此模块后,setdefaultencoding()函数将从sys模块中删除。

实际使用它的唯一方法是使用重新加载hack来恢复属性。

此外,始终不鼓励使用sys.setdefaultencoding() ,并且它已成为py3k中的无操作。 py3k的编码硬连接到“utf-8”并且更改它会引发错误。

我建议阅读一些指示:

答案 1 :(得分:51)

TL;博士

答案是从不(除非你真的知道你在做什么)

解决方案的9/10倍可以通过正确理解编码/解码来解决。

1/10人的语言环境或环境定义不正确,需要设置:

PYTHONIOENCODING="UTF-8"  

在他们的环境中修复控制台打印问题。

它做了什么?

sys.setdefaultencoding("utf-8") (突破以避免重复使用)更改了Python 2.x需要将Unicode()转换为str()时使用的默认编码/解码(和反之亦然)并没有给出编码。即:

str(u"\u20AC")
unicode("€")
"{}".format(u"\u20AC") 

在Python 2.x中,默认编码设置为ASCII,上面的示例将失败:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

(我的控制台配置为UTF-8,因此"€" = '\xe2\x82\xac',因此\xe2上的例外情况)

UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)

sys.setdefaultencoding("utf-8") 将允许这些适用于,但不一定适用于不使用UTF-8的用户。 ASCII的默认值可确保编码假设未被编入代码

控制台

sys.setdefaultencoding("utf-8") 也会出现修复sys.stdout.encoding的副作用,用于将字符打印到控制台时使用。 Python使用用户的语言环境(Linux / OS X / Un * x)或代码页(Windows)来设置它。有时,用户的区域设置已损坏,只需要PYTHONIOENCODING来修复控制台编码

示例:

$ export LANG=en_GB.gibberish
$ python
>>> import sys
>>> sys.stdout.encoding
'ANSI_X3.4-1968'
>>> print u"\u20AC"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
>>> exit()

$ PYTHONIOENCODING=UTF-8 python
>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> print u"\u20AC"
€

sys.setdefaultencoding(“utf-8”)有什么不好?

由于默认编码为ASCII,因此人们已经开发了针对Python 2.x的16年。已编写UnicodeError异常处理方法来处理发现包含非ASCII的字符串的字符串到Unicode转换。

来自https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/

def welcome_message(byte_string):
    try:
        return u"%s runs your business" % byte_string
    except UnicodeError:
        return u"%s runs your business" % unicode(byte_string,
            encoding=detect_encoding(byte_string))

print(welcome_message(u"Angstrom (Å®)".encode("latin-1"))
  

在设置defaultencoding之前,此代码将无法解码ascii编码中的“Å”,然后进入异常处理程序以猜测编码并将其正确地转换为unicode。印刷:Angstrom(Å®)经营您的业务。一旦你将defaultencoding设置为utf-8,代码就会发现byte_string可以解释为utf-8,因此它会破坏数据并返回它:Angstrom(Ů)运行你的业务。

更改应该是常量将对您依赖的模块产生巨大影响。最好只修复进出代码的数据。

示例问题

虽然在下面的示例中将默认编码设置为UTF-8不是根本原因,但它显示了如何屏蔽问题以及当输入编码更改时,代码如何以非显而易见的方式中断: UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 3131: invalid start byte

答案 2 :(得分:18)

#!/usr/bin/env python
#-*- coding: utf-8 -*-
u = u'moçambique'
print u.encode("utf-8")
print u

chmod +x test.py
./test.py
moçambique
moçambique

./test.py > output.txt
Traceback (most recent call last):
  File "./test.py", line 5, in <module>
    print u
UnicodeEncodeError: 'ascii' codec can't encode character 
u'\xe7' in position 2: ordinal not in range(128)
shell上的

工作,不发送到sdtout, 所以这是一个解决方法,写入stdout。

我做了其他方法,如果没有定义sys.stdout.encoding,则不运行,或者换句话说,首先需要导出PYTHONIOENCODING = UTF-8来写入stdout。

import sys
if (sys.stdout.encoding is None):            
    print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout." 
    exit(1)


所以,使用相同的例子:

export PYTHONIOENCODING=UTF-8
./test.py > output.txt

将起作用

答案 3 :(得分:3)

  • 第一个危险在于reload(sys)

    重新加载模块时,实际上在运行时中获得了两个模块的副本。旧模块就像其他所有的Python对象一样,只要有引用它就会保持活动状态。因此,一半的对象将指向旧模块,一半指向新模块。当您进行一些更改时,当一些随机对象看不到更改时,您将永远不会看到它:

    (This is IPython shell)
    
    In [1]: import sys
    
    In [2]: sys.stdout
    Out[2]: <colorama.ansitowin32.StreamWrapper at 0x3a2aac8>
    
    In [3]: reload(sys)
    <module 'sys' (built-in)>
    
    In [4]: sys.stdout
    Out[4]: <open file '<stdout>', mode 'w' at 0x00000000022E20C0>
    
    In [11]: import IPython.terminal
    
    In [14]: IPython.terminal.interactiveshell.sys.stdout
    Out[14]: <colorama.ansitowin32.StreamWrapper at 0x3a9aac8>
    
  • 现在,sys.setdefaultencoding()正确

    All that it affects is implicit conversion str<->unicode。现在,utf-8是地球上最安全的编码(向后兼容ASCII和所有),转换现在“正常”,可能出错?

    好吧,什么都好。这就是危险。

    • 可能有一些代码依赖于为非ASCII输入引发的UnicodeError,或者使用错误处理程序进行转码,现在会产生意外结果。并且由于所有代码都使用默认设置进行测试,因此您严格遵守“不受支持”的区域,并且没有人能够保证代码的行为方式。
    • 如果系统上的所有内容都不使用UTF-8 because Python 2 actually has multiple independent "default string encodings",则转码可能会产生意外或无法使用的结果。 (请记住,程序必须在客户的设备上为客户工作。)
      • 同样,最糟糕的是你永远不会知道,因为转换是隐含的 - 你真的不知道它发生的时间和地点。(Python Zen ,koan 2啊!)你永远不会知道为什么(如果)你的代码在一个系统上运行而在另一个系统上运行。 (或者更好的是,在IDE中运行并在控制台中断。)