Python 2.6でunicode_literalsを使用している落とし穴はありますか?


101

Python 2.6で実行しているコードベースをすでに取得しています。Python 3.0を準備するために、以下を追加しました。

__future__からインポートunicode_literals

私たちの.pyファイルに(私たちがそれらを変更するにつれて)。他の誰かがこれを行っていて、(おそらくデバッグに多くの時間を費やした後で)明らかでない落とし穴に遭遇したのではないかと思います。

回答:


101

ユニコード文字列で作業してきた問題の主な原因は、UTF-8でエンコードされた文字列とユニコード文字列を混在させることです。

たとえば、次のスクリプトについて考えてみます。

two.py

# encoding: utf-8
name = 'helló wörld from two'

one.py

# encoding: utf-8
from __future__ import unicode_literals
import two
name = 'helló wörld from one'
print name + two.name

runningの出力python one.pyは次のとおりです。

Traceback (most recent call last):
  File "one.py", line 5, in <module>
    print name + two.name
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

この例でtwo.nameは、はインポートされなかったため、utf-8でエンコードされた文字列(Unicodeではない)unicode_literalsone.nameあり、Unicode文字列です。両方を混在させると、Pythonはエンコードされた文字列をデコードしようとし(ASCIIであると想定)、それをUnicodeに変換して失敗します。あなたがしたならそれはうまくいくでしょうprint name + two.name.decode('utf-8')た。

文字列をエンコードして後で混合しようとすると、同じことが起こります。たとえば、これは動作します:

# encoding: utf-8
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

出力:

DEBUG: <html><body>helló wörld</body></html>

しかし、import unicode_literalsそれを追加した後は:

# encoding: utf-8
from __future__ import unicode_literals
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

出力:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print 'DEBUG: %s' % html
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 16: ordinal not in range(128)

'DEBUG: %s'はUnicode文字列であり、Pythonがデコードしようとするため失敗しますhtml。印刷を修正する方法はいくつかのどちらかやっているのprint str('DEBUG: %s') % htmlprint 'DEBUG: %s' % html.decode('utf-8')

これが、ユニコード文字列を使用する際の潜在的な問題を理解するのに役立つことを願っています。


11
私は行くことをお勧めしますdecode()代わりの解決策str()encode()解決策:より頻繁にあなたがUnicodeオブジェクトを使用して、何がしたいことは、外部暗黙のエンコードで文字、ないバイトの配列の文字列を操作することであるため、明確なコードは、あります。
エリックOレビゴット

8
用語を修正してください。when you mix utf-8 encoded strings with unicode onesUTF-8とUnicodeは2つの異なるエンコーディングではありません。Unicodeは標準であり、UTF-8はそれが定義するエンコーディングの1つです。
Kos

11
@コス:彼は「utf-8エンコードされた文字列」オブジェクトをユニコード(したがってデコードされた)オブジェクトと混合することを意味すると思います。前者はタイプstr、後者はタイプunicodeです。異なるオブジェクトであるため、それらを合計/連結/補間しようとすると問題が発生する可能性があります
MestreLion

これはpython>=2.6またはに適用されますかpython==2.6
ジョール、2013年

16

また、2.6(python 2.6.5 RC1 +より前)では、Unicodeリテラルがキーワード引数でうまく機能しません(issue4978):

たとえば、次のコードはunicode_literalsがなくても機能しますが、TypeErrorで失敗しますkeywords must be string。unicode_literalsが使用されている場合。

  >>> def foo(a=None): pass
  ...
  >>> foo(**{'a':1})
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
      TypeError: foo() keywords must be strings

17
ちなみに、python 2.6.5 RC1 +はこれを修正しました。
Mahmoud Abdelkader、2010年

13

unicode_literalsディレクティブを追加する場合は、次のようなものも追加する必要があることがわかりました。

 # -*- coding: utf-8

.pyファイルの1行目または2行目に。そうでなければ次のような行:

 foo = "barré"

次のようなエラーが発生します。

SyntaxError:ファイルmumble.pyの198行目の非ASCII文字 '\ xc3'、
 しかし、宣言されたエンコーディングはありません。http://www.python.org/peps/pep-0263.htmlを参照してください
 詳細については

5
@IanMackinnon:Python 3は、デフォルトでファイルがUTF8であると想定している
エンドリス

3
@endolith:しかし、Python 2はそうではありません。コメントで非ASCII文字を使用すると、構文エラーが発生します。したがって、IMHO # -*- coding: utf-8は、使用するunicode_literalsかしないかにかかわらず、実質的に必須のステートメントです
MestreLion

-*-必要とされていません。emacs互換の方法を使用している場合は、必要になると思います-*- encoding: utf-8 -*--*-最後のも参照してください)。必要なのはcoding: utf-8(またはの=代わりに: )だけです。
Chris Morgan、

2
あなたかどうかにかかわらず、このエラーが発生しますfrom __future__ import unicode_literals
Flimm 2013

3
Emacsの互換性に # -*- coding: utf-8 -*-「コーディング」が必要です(「エンコーディング」や「ファイルエンコーディング」などではありません。Pythonは接頭辞に関係なく「コーディング」を探します)。
Alex Dupuy 14

7

また、unicode_literal影響を与えるeval()が影響を与えないことも考慮に入れてくださいrepr()(非対称の動作であり、これはバグです)。つまりeval(repr(b'\xa4'))b'\xa4'(Python 3の場合のように)等しくありません。

理想的には、次のコードは不変でありunicode_literals、Python {2.7、3.x}の使用のすべての組み合わせに対して常に機能します。

from __future__ import unicode_literals

bstr = b'\xa4'
assert eval(repr(bstr)) == bstr # fails in Python 2.7, holds in 3.1+

ustr = '\xa4'
assert eval(repr(ustr)) == ustr # holds in Python 2.7 and 3.1+

Python 2.7でrepr('\xa4')評価されるため、2番目のアサーションはたまたま機能しu'\xa4'ます。


2
ここでの大きな問題はrepr、オブジェクトの再生成に使用していることだと思います。reprドキュメントは明確であると述べていない要件。私の意見では、これreprはデバッグにのみ役立つものに依存しています。
jpmc26 2014

5

もっとあります。

ユニコードを許容しない文字列を期待するライブラリとビルトインがあります。

2つの例:

組み込み:

myenum = type('Enum', (), enum)

(やや難解)unicode_literalsでは機能しません:type()は文字列を期待します。

図書館:

from wx.lib.pubsub import pub
pub.sendMessage("LOG MESSAGE", msg="no go for unicode literals")

動作しません:wx pubsubライブラリは文字列メッセージタイプを想定しています。

前者は難解で、簡単に修正できます

myenum = type(b'Enum', (), enum)

しかし、コードがpub.sendMessage()(私のもの)への呼び出しでいっぱいである場合、後者は壊滅的です。

やった、えっ?!?


3
また、型の要素もメタクラスにリークします。つまり、Djangoでは、宣言する文字列class Meta:は次のようになりますb'field_name'
Hamish Downer

2
うん...私の場合、私はすべてのsendMessage文字列を検索してb 'バージョンで置き換えるのは努力の価値があることを理解しました。恐ろしい「デコード」例外を回避したい場合、プログラムで厳密にユニコードを使用し、必要に応じて入力と出力を変換する(トピックで読んだいくつかの論文で言及されている「ユニコードサンドイッチ」)のようなものはありません。全体として、unicode_literalsは私にとって大きな勝利でした...
GreenAsJade

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.