为什么我们不应该在 py 脚本中使用 sys.setdefaultencoding("utf-8")?
- 2024-12-16 08:35:00
- admin 原创
- 151
问题描述:
我见过一些 py 脚本在脚本顶部使用它。在什么情况下应该使用它?
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
解决方案 1:
根据文档:这允许您从默认的 ASCII 切换到其他编码(例如 UTF-8),每当 Python 运行时必须将字符串缓冲区解码为 unicode 时,它都会使用该编码。
此函数仅在 Python 启动时可用,此时 Python 会扫描环境。它必须在系统范围的模块中调用,sitecustomize.py
评估此模块后,setdefaultencoding()
将从sys
模块中删除该函数。
实际使用它的唯一方法是通过重新加载黑客技术来恢复该属性。
此外,一直不鼓励使用sys.setdefaultencoding()
,它在 py3k 中已成为无操作。py3k 的编码硬编码为“utf-8”,更改它会引发错误。
我建议阅读以下几点:
http://nedbatchelder.com/blog/200401/printing_unicode_from_python.html
http://www.diveintopython3.net/strings.html#one-ring-to-rule-them-all
http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python
解决方案 2:
总结
答案是永远不会!(除非你真的知道自己在做什么)
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,因此"€" = 'xe2x82xac'
,因此 出现异常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
,即在将字符打印到控制台时会显示为 fix 。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")有什么不好?
16 年来,人们一直在使用 Python 2.x 进行开发,并且认为默认编码是 ASCII。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' 编解码器无法解码位置 3131 中的字节 0x80:无效的起始字节
解决方案 3:
#!/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
会起作用
解决方案 4:
第一个危险在于
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()
正确的
它所影响的只是隐式转换str<->unicode
。现在,utf-8
这是地球上最合理的编码(向后兼容 ASCII 和所有内容),转换现在“正常工作”,可能出什么问题呢?
好吧,什么都行。而这正是危险的。
+ 可能有些代码依赖于`UnicodeError`非 ASCII 输入的抛出,或者使用错误处理程序进行转码,现在会产生意外结果。**由于所有代码都使用默认设置进行测试,因此您在这里完全处于“不受支持”的领域**,并且没有人向您保证他们的代码将如何运行。
+ 如果系统上的所有内容并非都使用 UTF-8,则转码可能会产生意外或不可用的结果,因为 Python 2 实际上具有多个独立的“默认字符串编码”。 (请记住,程序必须为客户在其设备上工作。)
- 再说一遍,最糟糕的是**你永远不会知道,*因为转换是隐式的*——你真的不知道它何时何地发生。**(Python Zen,koan 2 ahoy!)你永远不会知道为什么(以及是否)你的代码在一个系统上运行而在另一个系统上中断。(或者更好的是,在 IDE 中运行但在控制台中中断。)