なぜpyスクリプトでsys.setdefaultencoding(“ utf-8”)を使用すべきではないのですか?


166

スクリプトの上部でこれを使用するpyスクリプトをいくつか見ました。どのような場合に使用する必要がありますか?

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

2
これをipythonで使用すると問題が発生し、%timeが動作しなくなりますgithub.com/ipython/ipython/issues/8071
seanv507

3
@ seanv507、回答を読んでください-それを使用することは非常に推奨されません
Alastair McCormack


2
これはsys.setdefaultencoding( 'utf-8')危険の正確な複製ではないのですか?これ(2010)がその日付(2015)を求めるのは、しかし、その質問にも良い答えがあります。何をすべきか?また、明確にするために、この質問は3ではなくPython 2でのみ意味がありますが、タグや言及はどこにもありません。
smci

SOの回答に飛び込む前に読む価値があります:pythonhosted.org/kitchen/unicode-frustrations.html
ccpizza

回答:


141

ドキュメントのとおり:これにより、デフォルトのASCIIからUTF-8などの他のエンコーディングに切り替えることができます。これは、Pythonランタイムが文字列バッファをユニコードにデコードする必要があるときにいつでも使用します。

この関数は、Pythonが環境をスキャンするPythonの起動時にのみ使用できます。システム全体のモジュールで呼び出す必要があります。sitecustomize.pyこのモジュールが評価された後、setdefaultencoding()関数はsysモジュールから削除されます。

実際にそれを使用する唯一の方法は、属性を元に戻すリロードハックを使用することです。

また、の使用sys.setdefaultencoding()は常に推奨されておらず、py3kでは何も行われなくなりました。py3kのエンコーディングは「utf-8」に固定されており、変更するとエラーが発生します。

私は読むためのいくつかの指針を提案します:


6
素晴らしい情報ですが、ここでは情報が多すぎて少し死んでしまいます。私はこの記事に焦点を当てて最も多くを学びました:blog.notdot.net/2010/07/Getting-unicode-right-in-Python
mbb

3
私は、デフォルトのエンコーディングも使用されることを追加したいと思いエンコード(への書き込み時にsys.stdout、それは持っていたときにNonePythonプログラムの出力をリダイレクトするときのようなエンコーディングを、)。
Eric O Lebigot 2013

14
+1 の使用sys.setdefaultencoding()は常に推奨されていません
jfs '12

7
「ハードワイヤードからutf-8」は真実ではありませんUTF-8。ハードワイヤードではなく、常にそうであるとは限りません。 LC_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

59

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に変換します。印刷:Angstrom(Å®)がビジネスを運営します。defaultencodingをutf-8に設定すると、byte_stringがutf-8として解釈できることがコードで検出され、データがマングルされて代わりに返されます。Angstrom(Ů)がビジネスを実行します。

定数であるべきものを変更すると、依存するモジュールに劇的な影響を及ぼします。コードに出入りするデータを修正することをお勧めします。

問題の例

次の例では、defaultencodingをUTF-8に設定することは根本的な原因ではありませんが、問題がどのようにマスクされ、入力エンコーディングが変更されたときに、コードが明白な方法で壊れるのかを示しています: UnicodeDecodeError: 'utf8' codec can位置3131のバイト0x80をデコードしない:無効な開始バイト


2
には驚きがありますがsys.setdefaultencoding("utf-8")、コードをPython 3のように動作させるのは良いことです。現在は2017年です。2015年に回答を書いても、後ろ向きではなく前向きに考えた方がいいと思います。出力がリダイレクトされるかどうかに応じて、Python 2でのコードの動作が異なることがわかったとき、これは実際には最も簡単な解決策でした(Python 2の非常に厄介な問題)。言うまでも# coding: utf-8ありませんが、既にがあり、Python 3の回避策は必要ありません(setdefaultencodingバージョンチェックを使用してマスクする必要があります)。
Yongwei Wu

これはすばらしいことであり、動作しますがsys.setdefaultencoding("utf-8")、Py 2.xコードをPython 3と互換性がありません。デフォルトのエンコーディングがASCIIであると想定している外部モジュールも修正しません。コードをPython 3互換にすることは非常に簡単で、この厄介なハックは必要ありません。これは非常に現実的な問題が発生する理由例えば、Amazonはこの仮定をいじって私の経験を参照してください。stackoverflow.com/questions/39465220/...
アラステア・マコーマック

1
@AlastairMcCormackあなたは素晴らしいです、私のサイトは何ヶ月も前からあり、何をすべきか理解できませんでした。最後に、PYTHONIOENCODING="UTF-8"私のPython2.7 Django-1.11環境を支援しました。ありがとう。
sam

例をコピーしたことは知っていますが、パッケージの内容を見つけることができます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)

シェルでは機能し、sdtout notに送信するので、これはstdoutに書き込むための1つの回避策です。

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
これは、質問されたとおりの回答にはなりません。むしろ、主題に関するいくつかの接線的な考え。
ivan_pozdeev 2018

3
  • 最初の危険はにありますreload(sys)

    モジュールをリロードすると、実際にはランタイムでモジュールのコピーが2つ取得されます。古いモジュールは他のすべてと同様に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入力に対してスローされることに依存するコード、またはエラーハンドラーでトランスコーディングを行うコードが存在する可能性があります。これにより、予期しない結果が生じます。また、すべてのコードはデフォルト設定でテストされているため、ここは厳密に「サポートされていない」領域にあり、コードの動作を保証するものは誰もいません。
    • Python 2には実際には複数の独立した「デフォルトの文字列エンコーディング」があるため、システムのすべてがUTF-8を使用しているわけではない場合でも、トランスコーディングは予期しない結果または使用できない結果をもたらす可能性があります。(プログラムは、顧客の機器上で顧客のために機能する必要があります。)
      • 繰り返しになりますが、最悪のことは、変換が暗黙的であるため、決してそれがわからないことです。変換がいつどこで行われるかは本当にわかりません。(Python Zen、koan 2 ahoy!)コードが1つのシステムで機能し、別のシステムで機能しなくなる理由(およびその場合)はわかりません。(さらに良いことに、IDEで動作し、コンソールで壊れます。)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.