为啥我们不应该在 py 脚本中使用 sys.setdefaultencoding("utf-8") ?
Posted
技术标签:
【中文标题】为啥我们不应该在 py 脚本中使用 sys.setdefaultencoding("utf-8") ?【英文标题】:Why should we NOT use sys.setdefaultencoding("utf-8") in a py script?为什么我们不应该在 py 脚本中使用 sys.setdefaultencoding("utf-8") ? 【发布时间】:2011-04-19 05:28:15 【问题描述】:我见过一些在脚本顶部使用它的 py 脚本。在什么情况下应该使用它?
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
【问题讨论】:
在 ipython 中使用这个有问题,%time 停止工作github.com/ipython/ipython/issues/8071 @seanv507,阅读答案 - 严重不鼓励使用它 相关:Dangers of sys.setdefaultencoding('utf-8') 这不是Dangers of sys.setdefaultencoding('utf-8') 的完全复制品吗?虽然这个(2010)问早于那个(2015)?但这个问题也有很好的答案。该怎么办?另外,需要明确的是,这个问题仅在 Python 2 而非 3 上有意义,但没有任何标记或提及。 在深入了解 SO 答案之前值得一读:pythonhosted.org/kitchen/unicode-frustrations.html 【参考方案1】:根据文档:这允许您从默认 ASCII 切换到其他编码,例如 UTF-8,Python 运行时将在必须将字符串缓冲区解码为 unicode 时使用该编码。
此功能仅在 Python 启动时可用,此时 Python 扫描环境。它必须在系统范围的模块sitecustomize.py
中调用。在评估此模块后,setdefaultencoding()
函数将从sys
模块中删除。
真正使用它的唯一方法是使用重新加载技巧来恢复属性。
另外,一直不鼓励使用sys.setdefaultencoding()
,它已经成为 py3k 中的空操作。 py3k 的编码是硬连线到“utf-8”,更改它会引发错误。
我建议一些阅读指南:
http://blog.ianbicking.org/illusive-setdefaultencoding.html http://nedbatchelder.com/blog/200401/printing_unicode_from_python.html http://www.diveintopython3.net/strings.html#one-ring-to-rule-them-all http://boodebr.org/main/python/all-about-python-and-unicode http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python【讨论】:
很棒的东西,虽然这里的信息太多会有点死。我学到最多的只是关注这篇文章:blog.notdot.net/2010/07/Getting-unicode-right-in-Python 我想补充一点,默认编码也用于 encoding (当写入sys.stdout
时,当它具有None
编码时,例如重定向输出时Python 程序)。
+1 for “一直不鼓励使用sys.setdefaultencoding()
”
'硬连线到 utf-8' 不是真的,它不是硬连线的,它并不总是UTF-8
。 LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'
给UTF-8
但LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'
给ANSI_X3.4-1968
(或者可能是别的)
@Tino,控制台编码与默认编码是分开的。【参考方案2】:
#!/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 不, 所以这是一种解决方法,写入标准输出。
我做了其他方法,如果未定义 sys.stdout.encoding 则不会运行,或者换句话说,需要先导出 PYTHONIOENCODING=UTF-8 才能写入标准输出。
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】:tl;博士
答案是从不! (除非你真的知道自己在做什么)
正确理解编码/解码可以解决9/10倍的解决方案。
1/10 人的语言环境或环境定义不正确,需要设置:
PYTHONIOENCODING="UTF-8"
在他们的环境中修复控制台打印问题。
它有什么作用?
(为了避免重复使用而删除)更改了 Python 2.x 需要将 Unicode() 转换为 str() 时使用的默认编码/解码(反之亦然)反之亦然)并且没有给出编码。即:sys.setdefaultencoding("utf-8")
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)
将允许这些为 me 工作,但不一定适用于不使用 UTF-8 的人。 ASCII 的默认设置确保不会将编码假设融入代码sys.setdefaultencoding("utf-8")
控制台
还有一个副作用是似乎修复了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")有什么不好?
人们已经针对 Python 2.x 进行了 16 年的开发,因为他们理解默认编码是 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 (Å®) 经营您的业务。将默认编码设置为 utf-8 后,代码会发现 byte_string 可以解释为 utf-8,因此它将破坏数据并返回:Angstrom (Ů) 运行您的业务。
更改应该是常量的值将对您所依赖的模块产生巨大影响。最好只修复进出代码的数据。
示例问题
虽然将 defaultencoding 设置为 UTF-8 不是以下示例中的根本原因,但它显示了问题是如何被掩盖的,以及当输入编码发生变化时,代码如何以一种不明显的方式中断: UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 3131: invalid start byte
【讨论】:
虽然sys.setdefaultencoding("utf-8")
有惊喜,但最好让代码表现得更像 Python 3。现在是 2017 年。即使您在 2015 年写下答案,我认为向前看比向后看已经更好。这对我来说实际上是最简单的解决方案,当我发现我的代码在 Python 2 中的行为取决于输出是否被重定向时(Python 2 非常讨厌的问题)。不用说,我已经有了# coding: utf-8
,并且我不需要任何针对 Python 3 的解决方法(我实际上必须使用版本检查来屏蔽setdefaultencoding
)。
这很好,它对你有用,但sys.setdefaultencoding("utf-8")
不会使你的 Py 2.x 代码与 Python 3 兼容。它也不会修复假定默认编码为 ASCII 的外部模块。使您的代码与 Python 3 兼容非常简单,不需要这种讨厌的 hack。例如,为什么这会导致非常实际的问题,请参阅我对亚马逊的经验与此假设的混淆:***.com/questions/39465220/…
@AlastairMcCormack 你太棒了,我的网站已经有几个月了,不知道该怎么做。最后,PYTHONIOENCODING="UTF-8"
帮助了我的 Python2.7 Django-1.11 环境。谢谢。
我知道你复制了这个例子,但我可以找到 detect_encoding
的包。
@dlamblin 代码示例是为了证明引用,不应该在您的代码中使用。想象一下detect_encoding
是一种可以根据语言线索检测字符串编码的方法。【参考方案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()
正确
All that it affects is implicit conversion str<->unicode
。现在,utf-8
是地球上最合理的编码(向后兼容 ASCII 和所有编码),现在转换“正常工作”,可能会出现什么问题?
好吧,随便。这就是危险。
可能有一些代码依赖于UnicodeError
被抛出用于非 ASCII 输入,或者使用错误处理程序进行转码,现在会产生意外结果。而且由于所有代码都使用默认设置进行测试,因此您在此处严格处于“不受支持”的领域,没有人向您保证他们的代码将如何运行。
如果系统上并非所有内容都使用 UTF-8 because Python 2 actually has multiple independent "default string encodings",则转码可能会产生意外或不可用的结果。 (请记住,程序必须在客户的设备上为客户工作。)
同样,最糟糕的是你永远不会知道这一点,因为转换是隐式的——你真的不知道它发生的时间和地点。(Python Zen ,koan 2 ahoy!)您永远不会知道为什么(以及是否)您的代码在一个系统上工作并在另一个系统上中断。 (或者更好的是,在 IDE 中工作并在控制台中中断。)
【讨论】:
以上是关于为啥我们不应该在 py 脚本中使用 sys.setdefaultencoding("utf-8") ?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们不应该在 javascript 中使用 ++? [复制]
为啥我们不应该在生产服务器上的 mysql 查询中使用 Select *?
我应该在 NPM package.json 中将我的“启动”脚本设置为啥?
为啥我应该使用 MailChimp 或类似工具而不是自定义脚本? [复制]