web-dev-qa-db-ja.com

sys.setdefaultencoding( 'utf-8')の危険性

sys.setdefaultencoding('utf-8') in Python 2.の設定を推奨しない傾向があります。それに関する問題の実際の例をリストできますか?_it is harmful_または_it hides bugs_あまり説得力がないように思われます。

[〜#〜] update [〜#〜]:この質問は_utf-8_のみに関するものであり、デフォルトのエンコーディングを「一般的な場合」に変更することではないことに注意してください。

可能であれば、コードを使用していくつかの例を示してください。

33

元のポスターは、スイッチが有害であることを示すコードを要求しました。スイッチに関係のないバグを「隠す」ことをexcept

結論の要約

私が収集した経験と証拠の両方に基づいて、私が到達した結論を以下に示します。

  1. 現在、defaultencodingをUTF-8に設定するのはsafeです。ただし、特殊なアプリケーションを除き、Unicode対応でないシステムからのファイルを処理します。

  2. スイッチの「公式」拒否は、もはや関係のない理由に基づいており、大部分のエンドユーザー(ライブラリプロバイダーではない)のため、ユーザーが設定するのをやめないでください。それ。

  3. Unicodeをデフォルトで適切に処理するモデルでの作業は、Unicode APIを手動で使用するよりも、inter-systems communicationのアプリケーションにより適しています。

実質的に、デフォルトのエンコーディングveryveryを変更することで、ほとんどのユースケースで多くの ユーザーの頭痛 を回避できます。はい、複数のエンコーディングを処理するプログラムが静かに誤動作する場合がありますが、このスイッチを少しずつ有効にできるため、これはエンドユーザーコードの問題ではありません.

さらに重要なことは、このフラグを有効にすることは本当の利点です、ユーザーのコードです。Unicode変換を手動で処理するオーバーヘッドを減らし、コードを乱雑にし、読みにくくします。プログラマーがすべての場合にこれを適切に実行できない場合に潜在的なバグを回避します。


これらの主張は、Pythonの公式の通信ラインとは正反対であるため、これらの結論の説明は正当化されると思います。

修正されたdefaultencodingを野生でうまく使用する例

  1. FedoraのDave Malcomは、それが alwaysright。 であると信じていました。==彼は、リスクを調査した後、allFedoraユーザー。

    ただし、whyPythonが破られるハードな事実は、ハッシュ動作I リスト のみです。ユーザーチケットで作業するときに、心配する理由として、または 同じ人物 でさえ、コアコミュニティ。

    Resume of Fedora :確かに、変更自体はコア開発者に「非常に人気がない」と説明され、以前のバージョンと矛盾していると非難されました。

  2. openhubで3000プロジェクトだけ 実行しています。彼らは遅い検索フロントエンドを持っていますが、それをスキャンすると、98%がUTF-8を使用していると推定します。厄介な驚きについては何も見つかりませんでした。

  3. 18000(!)githubマスターブランチ があり、変更されました。

    この変更はコアコミュニティでは「 npopular 」ですが、ユーザーベースではpretty人気があります。ユーザーはハッキングソリューションを使用することが知られているため、これは無視できますが、次の点から、これが関連する議論だとは思いません。

  4. これにより、GitHubには 150個のバグレポート 合計しかありません。実質的に100%の割合で、変化はマイナスではなくプラスのように見えます。

    人々haveが遭遇する既存の問題を要約するために、前述のすべてのチケットをスキャンしました。

    • Defing.encの変更通常、UTF-8はintroducedですが、問題解決プロセスでは削除されず、ほとんどの場合はソリューションとして削除されます。 一時的な修正 としてそれを免除するいくつかのより大きなもの、それが持っている「悪いプレス」を考慮して、はるかに多くのバグ報告者は justglad about- 修正

    • いくつかの(1〜5の?)プロジェクトでは、デフォルトを変更する必要がないように、型変換を行うコードを手動で変更しました。

    • 2つの場合、def.encでそれを主張している人がいます。 UTF-8に設定すると、テストのセットアップを説明することなく、完全に欠落します 出力全体 になります。私はその主張を検証できませんでした。私は主張をテストしましたが、反対のことが真実であることがわかりました。

    • 1つ 主張 彼の「システム」は、それを変更しないことに依存するかもしれませんが、その理由はわかりません。

    • 1つ(およびonlyの1つ)には、回避する本当の理由がありました。 ipython は、サードパーティモジュールを使用するか、テストランナーがプロセスを制御されない方法で変更しました( def.enc。の変更がproponentsによってインタープリターのセットアップ時のみ、つまりプロセスを「所有」している場合にのみ提唱されることに異論はありません。

  5. 'é'とu'é 'の異なるハッシュが実際のコードで問題を引き起こすという兆候はゼロでした。

  6. Pythonはnot "break"します

    設定をUTF-8に変更した後、単体テストでカバーされるPythonの機能は、スイッチなしの場合と異なる動作をしません。ただし、スイッチitselfはまったくテストされていません。

  7. Bugs.python.orgでフラストレーションのあるユーザーにお勧めします

    herehere または here (多くの場合、公式の警告行に接続されています)

    最初の例は、アジアでスイッチがどのように確立されているかを示しています(github引数とも比較してください)。

  8. Ian Bicking 公開済み この動作を有効にするalwaysのサポート。

    システムと通信を一貫してUTF-8にすることができ、状況は改善されます。本当にマイナス面はありません。しかし、どうしてPythonになったのかSODAMN HARD [...]誰かが自分よりも賢いと決めたように感じますが、信じられません。

  9. Martijn Fassenは、Ianに反論しながら、 承認済み そもそもASCIIが間違っていた可能性があります。

    たとえば、Python2.5がデフォルトのエンコーディングであるUTF-8で出荷されていれば、実際には何も壊れないでしょう。しかし、Pythonでそれを行った場合、他の人にコードを渡したときにすぐに問題が発生します。

  10. Python3では、彼らは「彼らが説教することを実践しない」

    Def.encに反対しながら環境に依存するコードまたは暗黙性のために非常に厳しく変更するため、議論 herePython の問題 'unicode sandwich' パラダイムと対応する必要な暗黙の仮定。

    さらに、彼らは次のような有効なPython3コードを書く可能性を生み出しました。

    >>> from 褐褑褒褓褔褕褖褗褘 import *        
    >>> def 空手(合氣道): あいき((合氣道))
    >>> 空手(う힑힜('???? ') +)
    ????
    
  11. DiveIntoPython 推奨

  12. このスレッドで 、Guido自身 アドバイス a プロのエンドユーザー 「カスタムを作成するように設定されたスイッチでプロセス固有の環境を使用するPython各プロジェクトの環境。」

    Python 2.x標準ライブラリの設計者がアプリでデフォルトエンコーディングを設定できないようにする基本的な理由は、デフォルトエンコーディングが固定されているという前提で標準ライブラリが記述されていることであり、変更すると、標準ライブラリが正しく機能するようになります。この状況のテストはありません。いつ失敗するかは誰にもわかりません。そして、標準ライブラリが予期しないことを突然実行し始めた場合、あなた(または、さらに悪いことに、ユーザー)は苦情を返して私たちに戻ってきます。

  13. Jythonは、モジュール内であってもオンザフライで変更することを提案しています。

  14. PyPyは not reload(sys)をサポートしましたが、- 返品 on ユーザーリクエスト 質問なしで1日以内に行いました。 CPythonの「 あなたは間違っている 」の態度と比較してください。 claiming それは「悪の根」であるという証拠がありません。


このリストの終わりに、1つcouldが、変更されたインタープリター構成のbecauseをクラッシュさせるモジュールを作成することを確認します。

def is_clean_ascii(s):
    """ [Stupid] type agnostic checker if only ASCII chars are contained in s"""
    try:
        unicode(str(s))
        # we end here also for NON ascii if the def.enc. was changed
        return True
    except Exception, ex:
        return False    

if is_clean_ascii(mystr):
    <code relying on mystr to be ASCII>

この二重型受け入れモジュールを書いた人は明らかにASCII対非ASCII文字列を認識しており、エンコードとデコードを認識しているため、これは有効な引数ではないと思います。

この証拠は、この設定を変更してもほとんどの場合vastで実際のコードベースで問題が発生しないことを十分に示していると思います。

22
Red Pill

常にwantを使用して文字列を自動的にUnicodeにデコードするわけではないため、またはUnicodeオブジェクトを自動的にバイトにエンコードする必要はありません。あなたは具体的な例を求めているので、ここに一つあります:

WSGI Webアプリケーションを使用してください。外部プロセスの生成物をループにリストに追加することで応答を構築し、その外部プロセスはUTF-8エンコードバイトを提供します。

_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
_

これを英語でテストしましたが、すべて正常に動作します。

ただし、translations.get_label()ライブラリーは実際にnicode値を返し、ロケールを切り替えると、ラベルに非ASCII文字が含まれます。

setdefaultencoding()をUTF-8に設定しているが長さのため、WSGIライブラリはそれらの結果をソケットに書き出し、すべてのUnicode値が自動的にエンコードされます計算は完全に間違っています。 UTF-8はASCII範囲外のすべてを1バイト以上でエンコードするため、短すぎます。

これはすべて、実際には別のコーデックでデータを操作している可能性を無視しています。あなたはLatin-1 + Unicodeを書き出すことができ、そして今、あなたは間違った長さのヘッダーデータエンコーディングのミックスを持っています。

sys.setdefaultencoding()を使用していなかった場合、例外が発生し、バグがあることはわかっていましたが、クライアントは不完全な応答について不平を言っています。ページの最後に欠落しているバイトがあり、それがどのように起こったのかよくわかりません。

このシナリオには、ASCIIのままのデフォルトに依存する場合もしない場合もあるサードパーティのライブラリも含まれないことに注意してください。 sys.setdefaultencoding()設定はglobalで、インタープリターで実行中のallコードに適用されます。暗黙のエンコードまたはデコードに関連するこれらのライブラリに問題がないことをどのように確認しますか?

Python 2はstr型とunicode型の間でエンコードとデコードを暗黙的に行い、ASCII =データのみ。ただし、Unicodeとバイト文字列データを誤って混在させている場合、グローバルブラシと希望で塗りつぶすのではなく、本当に知る必要があります最高の。

16
Martijn Pieters

まず第一に:デフォルト変更の多くの反対者は、その asciiの比較を変更することさえ

元の質問に準拠して、AsciiからUTF-8に逸脱すること以外に主張している人はいないことを明確にすると思います。

Setdefaultencoding( 'utf-16')の例は、それを変更することに反対する人々によって常に提起されているようです;-)


M = {'a':1、 'é':2}およびファイル 'out.py'の場合:

# coding: utf-8
print u'é' 

次に:

+---------------+-----------------------+-----------------+
| DEF.ENC       | OPERATION             | RESULT (printed)|            
+---------------+-----------------------+-----------------+
| ANY           | u'abc' == 'abc'       | True            |     
| (i.e.Ascii    | str(u'abc')           | 'abc'           |
|  or UTF-8)    | '%s %s' % ('a', u'a') | u'a a'          | 
|               | python out.py         | é               |
|               | u'a' in m             | True            |
|               | len(u'a'), len(a)     | (1, 1)          |
|               | len(u'é'), len('é')   | (1, 2) [*]      |
|               | u'é' in m             | False  (!)      |
+---------------+-----------------------+-----------------+
| UTF-8         | u'abé' == 'abé'       | True   [*]      |
|               | str(u'é')             | 'é'             |
|               | '%s %s' % ('é', u'é') | u'é é'          | 
|               | python out.py | more  | 'é'             |
+---------------+-----------------------+-----------------+
| Ascii         | u'abé' == 'abé'       | False, Warning  |
|               | str(u'é')             | Encoding Crash  |
|               | '%s %s' % ('é', u'é') | Decoding Crash  |
|               | python out.py | more  | Encoding Crash  |
+---------------+-----------------------+-----------------+

[*]:結果はsameéを想定しています。以下を参照してください。

これらの操作を見ながら、プログラムのデフォルトのエンコーディングを変更しても見た目は悪くないかもしれません。結果は、Asciiのみのデータに「近い」結果になります。

ハッシュ(in)とlen()の振る舞いについては、Asciiで同じ結果が得られます(結果については以下を参照)。これらの操作は、ユニコードとバイト文字列の間に大きな違いがあることも示しています。これは、無視された場合に論理エラーを引き起こす可能性があります。

すでに述べたように:プロセスワイドオプションなので、選択するショットは1つだけです-これが理由です library開発者は実際にこれを行うべきではありませんが、Pythonの暗黙的な変換に依存する必要がないように、内部を取得する必要があります。また、期待するものを明確に文書化し、libを作成しなかった入力を返し、拒否する必要があります(normalize関数など、以下を参照)。

=>その設定をオンにしてプログラムを作成すると、少なくとも入力をフィルタリングせずに、他の人が自分のコードでプログラムのモジュールを使用するリスクが高くなります。

注:一部の対戦相手は、def.encを主張しています。 (sitecustomize.pyを介した)システム全体のオプションでもありますが、ソフトウェアコンテナ化(docker)の最新の状況では、オーバーヘッドのない完璧な環境ですべてのプロセスを開始できます。


ハッシュとlen()の振る舞いについて:

Def.encを変更した場合でも、それがわかります。プログラムで処理する文字列のタイプについてはまだ知らないことはできません。 u ''と ''は、メモリ内の異なるバイトシーケンスです-常にではありませんが、一般的には。

したがって、テスト時には、プログラムが非ASCIIデータでも正しく動作することを確認してください。

データ値が変わるとハッシュが等しくなくなる可能性があると言う人もいます-暗黙の変換のために「==」演算は等しいままですが-def.encの変更に対する議論です。

ハッシュの振る舞いは変更しない場合と同じままなので、私は個人的には共有しません。私が「所有」しているプロセスの設定に起因する望ましくない動作の説得力のある例をまだ見ていません。

全体として、setdefaultencoding( "utf-8")に関して:愚かであるかどうかについての答えは、よりバランスが取れている必要があります。

場合によります。クラッシュは避けますが、ログ文のstr()操作で-間違った型は、特定の型に依存する正しい機能を持つコードに長くなるため、価格は後で予期しない結果が生じる可能性が高くなります。

決して、それはあなた自身のコードのためにバイト文字列とユニコード文字列の違いを学ぶことに代わるものであるべきではありません。


最後に、Asciiからデフォルトのエンコーディングを設定しても、len()、スライシング、比較などの一般的なテキスト操作が楽になることはありません。

残念ながら、そうではありません-一般的に。

「==」とlen()の結果は、far考えられるよりも複雑な問題ですが、same両側に入力します。

Def.encなし変更、表に示されているように、非ASCIIの場合、「==」は常に失敗します。それで、それは動作します-時々:

Unicodeは世界の百万個のシンボルを標準化して番号を付けましたが、残念ながら、出力デバイスでユーザーに表示されるグリフとそれらが生成されるシンボルとの間に1:1の全単射はありません。

あなたのやる気を引き出すには これを研究してください :ユーザー入力を含む同じエンコーディングを使用するsameプログラムで書かれた2つのファイルj1、j2があります:

>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2

結果:2.7.9JoséJoséFalse(!)

Py2で関数としてprintを使用すると、理由がわかります。残念ながら、同じ文字をエンコードする2つの方法があり、アクセント記号「e」があります。

>>> print (sys.version.split()[0], u1, u2, u1 == u2)
('2.7.9', 'Jos\xc3\xa9', 'Jose\xcc\x81', False)

なんて馬鹿げたコーデックと言ってもいいが、コーデックのせいではない。そのようなユニコードの問題。

Py3でも:

>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2

結果:3.4.2JoséJoséFalse(!)

=> Py2およびPy3に依存せず、実際に使用するコンピューティング言語に依存しません。高品質のソフトウェアを作成するには、すべてのユーザー入力を「正規化」する必要があります。 Unicode標準は正規化を標準化しました。 Python 2および3では、unicodedata.normalize関数があなたの友人です。

3
Red Pill

リアルワードの例#1

単体テストでは機能しません。

テストランナー(nosepy.test、...)最初にsysを初期化してから、モジュールを検出してインポートします。その時までには、デフォルトのエンコーディングを変更するには遅すぎます。

同じ美徳により、誰かがあなたのコードをモジュールとして実行すると、初期化が最初に行われるため、機能しません。

そして、はい、strunicodeを混合し、暗黙的な変換に依存するだけで、問題はさらに先へと進みます。

2
Dima Tisnek

知っておくべきことの1つは

Python 2はsys.getdefaultencoding()を使用してstrunicodeの間でデコード/エンコードします

conversion between str and unicode

そのため、デフォルトのエンコーディングを変更すると、すべての種類の互換性のない問題が発生します。例えば:

# coding: utf-8
import sys

print "你好" == u"你好"
# False

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

print "你好" == u"你好"
# True

その他の例:

そうは言っても、可能な限りユニコードを使用し、I/Oを扱う場合はビット文字列のみを使用することを提案するブログがあることを覚えています。あなたがこの慣習に従えば、人生はずっと楽になると思います。より多くの解決策を見つけることができます:

1
Jiacai Liu