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


165

我在脚本顶部看到了几个使用此脚本的py脚本。在什么情况下应该使用它?

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

2
在ipython中使用它存在问题,%time停止工作github.com/ipython/ipython/issues/8071
seanv507 2015年

3
@ seanv507,请阅读答案-严重不鼓励使用它
Alastair McCormack


2
这不是sys.setdefaultencoding('utf-8')危险的确切副本吗?尽管这个问题(2010)早于那个问题(2015)?但是那个询问也有很好的答案。该怎么办?另外,要明确一点,这个问题仅在Python 2而不是3才有意义,但这没有标记或提及。
smci

在深入了解SO答案之前值得阅读:pythonhosted.org/kitchen/unicode-frustrations.html
ccpizza

Answers:


141

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

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

实际使用它的唯一方法是通过将属性重新带回的重载hack。

此外,使用sys.setdefaultencoding()一直气馁,它已成为一个无操作的py3k。py3k的编码硬连线到“ utf-8”,更改它会引发错误。

我建议您阅读一些指针:


6
很棒的东西,尽管这里太多的信息可能会导致死亡。我学到了最专注于这篇文章的文章:blog.notdot.net/2010/07/Getting-unicode-right-in-Python
mbb 2012年

3
我想补充一点,默认编码也用于编码(当写入sys.stdout具有None编码时,例如重定向Python程序的输出时)。
Eric O Lebigot

14
sys.setdefaultencoding()始终不鼓励使用”
jfs

7
“硬连线到utf-8”不是正确的,它不是硬连线的,而且并非总是如此UTF-8LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'给予UTF-8LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'给予ANSI_X3.4-1968(或其他)
Tino

7
@Tino,控制台编码与默认编码是分开的。
Alastair McCormack

58

tl; dr

答案是永不(除非您真的知道自己在做什么)

在正确理解编码/解码的情况下,可以解决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在将字符打印到控制台时,也具有出现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”)有什么不好?

人们已经认识到默认的编码是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。打印:埃斯特朗(Å®)经营您的业务。将defaultencoding设置为utf-8后,代码将发现byte_string可以解释为utf-8,因此它将处理数据并返回该值:Angstrom(Ů)经营您的业务。

更改应为常数的值将对您依赖的模块产生巨大影响。最好只修复代码中传入和传出的数据。

示例问题

虽然在以下示例中将defaultencoding设置为UTF-8并不是根本原因,但它显示了如何掩盖问题以及如何在输入编码更改时以不明显的方式中断代码: UnicodeDecodeError:'utf8'编解码器可以在位置3131中解码字节0x80:无效的起始字节


2
尽管令人惊讶sys.setdefaultencoding("utf-8"),但最好使代码的行为更像Python3。现在是2017年。即使您在2015年写下答案时,我认为前瞻而不是后退已经更好。当我发现我的代码在Python 2中的行为取决于是否重定向输出时,这实际上是对我来说最简单的解决方案(Python 2的问题非常棘手)。不用说,我已经有了# coding: utf-8,并且我不需要Python 3的任何解决方法(实际上我必须掩盖setdefaultencoding使用版本检查)。
雍永

很好,它对sys.setdefaultencoding("utf-8")您有用,但不能使您的Py 2.x代码与Python 3兼容。它也不能修复假定默认编码为ASCII的外部模块。使您的代码与Python 3兼容非常简单,不需要这种讨厌的技巧。例如,为什么这会导致非常现实的问题,看到我与亚马逊的经验与这一假设搞乱:stackoverflow.com/questions/39465220/...
阿拉斯泰尔·麦科马克

1
@AlastairMcCormack,您的想法令人震惊,我的网站已经有几个月了,无法确定该怎么做。最后,PYTHONIOENCODING="UTF-8"帮助了我的Python2.7 Django-1.11环境。谢谢。
山姆

我知道您复制了示例,但是我可以找到包中包含的内容detect_encoding
dlamblin

@dlamblin该代码示例用于证明引号,并且不应在您的代码中使用。想象一下,这detect_encoding是一种可以根据语言线索检测字符串编码的方法。
Alastair McCormack

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
这不能回答所要求的问题。而是一些与此主题相切的想法。
ivan_pozdeev

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()适当的

    它所影响的只是隐式转换str<->unicode。现在,这utf-8是地球上最聪明的编码(向后兼容ASCII和所有语言),现在转换“正常”了,可能出什么问题了吗?

    好吧,什么都可以。那就是危险。

    • 可能有些代码依赖于UnicodeError为非ASCII输入抛出的代码,或者使用错误处理程序进行代码转换,这现在会产生意外结果。而且,由于所有代码都是使用默认设置进行测试的,因此您在此处严格处于“不受支持”的范围,并且没人能保证它们的代码将如何运行。
    • 如果系统上并非所有组件都使用UTF-8,则转码可能会产生意外或无法使用的结果,因为Python 2实际上具有多个独立的“默认字符串编码”。(请记住,程序必须在客户的设备上为客户工作。)
      • 同样,最糟糕的是您永远不会知道,因为转换是隐式的 -您实际上并不知道转换的时间和地点。(Python Zen,koan 2 ahoy!)您将永远不知道为什么(如果)代码可以在一个系统上运行而在另一个系统上中断。(或者更好的是,可以在IDE中工作,并且可以在控制台中中断。)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.