Python sys.setdefaultencoding(';utf-8';)的危险
在Python2中,有一种趋势是不鼓励设置Python sys.setdefaultencoding(';utf-8';)的危险,python,encoding,utf-8,python-2.x,Python,Encoding,Utf 8,Python 2.x,在Python2中,有一种趋势是不鼓励设置sys.setdefaultencoding('utf-8')。有人能列举出这方面问题的真实例子吗?像它是有害的或它隐藏bug这样的论点听起来不太令人信服 更新:请注意,此问题仅涉及utf-8,而不是“在一般情况下”更改默认编码 如果可以,请给出一些代码示例 实词示例#1 它在单元测试中不起作用 测试运行程序(nose,py.test,…)首先初始化sys,然后才发现并导入模块。到那时,更改默认编码已经太晚了 出于同样的优点,如果有人将代码作为模块运行,
sys.setdefaultencoding('utf-8')
。有人能列举出这方面问题的真实例子吗?像它是有害的
或它隐藏bug
这样的论点听起来不太令人信服
更新:请注意,此问题仅涉及utf-8
,而不是“在一般情况下”更改默认编码
如果可以,请给出一些代码示例 实词示例#1
它在单元测试中不起作用
测试运行程序(nose
,py.test
,…)首先初始化sys
,然后才发现并导入模块。到那时,更改默认编码已经太晚了
出于同样的优点,如果有人将代码作为模块运行,这是行不通的,因为他们的初始化是第一位的
是的,混合使用
str
和unicode
以及依赖隐式转换只会将问题推向更深的层次。首先:许多反对更改默认enc的人认为,它之所以愚蠢是因为
我认为,公平地说,按照最初的问题,我认为除了偏离Ascii到UTF-8之外,没有人主张其他任何东西
setdefaultencoding('utf-16')示例似乎总是由那些反对更改它的人提出;-)
使用m={'a':1,'e':2}和文件'out.py':
# coding: utf-8
print u'é'
然后:
[*]:结果假设相同。请参见下文
在查看这些操作时,更改程序中的默认编码可能看起来不太糟糕,这样您的结果“更接近”只有Ascii数据
关于散列(in)和len()行为,您可以得到与Ascii相同的结果(更多关于下面的结果)。这些操作还表明unicode和字节字符串之间存在显著差异,如果您忽略这些差异,可能会导致逻辑错误
如前所述:它是一个进程范围的选项,因此您只有一次机会来选择它-这就是为什么库开发人员永远不应该这样做,而是将其内部进行整理,这样他们就不需要依赖python的隐式转换。
他们还需要清楚地记录他们期望的内容,并返回和拒绝他们没有编写lib的输入(如normalize函数,见下文)
=>使用该设置编写程序会使其他人在其代码中使用您程序的模块,至少在不过滤输入的情况下,这样做是有风险的
注意:一些反对者声称def.enc。甚至是一个系统范围的选项(通过sitecustomize.py),但在软件容器化(docker)时代是最新的。每个进程都可以在其完美的环境中启动,而不需要额外的开销
关于散列和len()行为: 它告诉您,即使修改了def.enc。您仍然不能对程序中处理的字符串类型一无所知。u“”和“”是内存中不同的字节序列-不总是,但通常是 因此,在测试时,请确保您的程序在使用非Ascii数据时也能正常运行 有人说,当数据值更改时,哈希值可能变得不相等(尽管由于隐式转换,“==”操作保持相等),这是反对更改def.enc的一个理由 我个人不同意这一点,因为散列行为只是保持不变,不改变它。我还没有看到一个令人信服的例子,说明在我“拥有”的过程中,由于该设置而产生的不受欢迎的行为 总而言之,关于setdefaultencoding(“utf-8”):关于它是否哑的答案应该更加平衡 视情况而定。 虽然它确实避免了崩溃,例如日志语句中的str()操作,但由于错误的类型会使代码的正确运行取决于特定类型,因此以后出现意外结果的可能性更高 在任何情况下,它都不应该是学习您自己代码的字节字符串和unicode字符串之间的差异的替代方法
最后,将默认编码设置为远离Ascii并不会使常见的文本操作(如len()、切片和比较)变得更容易—您应该假设,使用UTF-8进行字符串化可以解决这里的问题 不幸的是,一般来说,情况并非如此 “==”和len()结果比人们想象的要复杂得多,但即使两边的类型都相同 不带def.enc。已更改,对于非Ascii,“==”始终失败,如表中所示。有了它,它就起作用了——有时: Unicode确实对世界上大约一百万个符号进行了标准化,并给了它们一个数字——但不幸的是,在输出设备中显示给用户的字形与生成它们的符号之间没有1:1的双射 激励您:有两个文件,j1和j2,使用相同的程序编写,使用相同的编码,包含用户输入:
>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2
结果:2.7.9何塞·何塞́假(!)
在Py2中使用print作为函数,您可以看到原因:不幸的是,有两种方法可以对同一字符进行编码,即重音“e”:
>>> print (sys.version.split()[0], u1, u2, u1 == u2)
('2.7.9', 'Jos\xc3\xa9', 'Jose\xcc\x81', False)
你可能会说这是一个多么愚蠢的编解码器,但这不是编解码器的错。这是unicode本身的一个问题
因此,即使在Py3中:
>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2
结果:3.4.2何塞́假(!)
=>独立于Py2和Py3,实际上独立于您使用的任何计算语言:要编写高质量的软件,您可能必须“规范化”所有用户输入。unicode标准实现了标准化。
在Python2和Python3中,unicodedata.normalize函数是您的朋友。因为您并不总是希望将字符串自动解码为Unicode,也不希望Unicode对象自动编码为字节。因为您要求的是一个具体的例子,这里有一个:
以wsgiweb应用程序为例;你是buildi
>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2
results = []
content_length = 0
for somevar in some_iterable:
output = some_process_that_produces_utf8(somevar)
content_length += len(output)
results.append(output)
headers = {
'Content-Length': str(content_length),
'Content-Type': 'text/html; charset=utf8',
}
start_response(200, headers)
return results
results = []
content_length = 0
for somevar in some_iterable:
label = translations.get_label(somevar)
output = some_process_that_produces_utf8(somevar)
content_length += len(label) + len(output) + 1
results.append(label + '\n')
results.append(output)
headers = {
'Content-Length': str(content_length),
'Content-Type': 'text/html; charset=utf8',
}
start_response(200, headers)
return results
>>> from 褐褑褒褓褔褕褖褗褘 import *
>>> def 空手(合氣道): あいき(ど(合氣道))
>>> 空手(う힑힜('One thing we should know is
Python 2 use sys.getdefaultencoding()
to decode/encode between str
and unicode
so if we change default encoding, there will be all kinds of incompatible issues. eg:
# coding: utf-8
import sys
print "你好" == u"你好"
# False
reload(sys)
sys.setdefaultencoding("utf-8")
print "你好" == u"你好"
# True