unicode() がエンコードを指定せずにオブジェクトに対してのみ str() を使用するのはなぜですか?
質問
まず、いくつかの文字列変数を作成します 非アスキー UTF-8 その上のエンコードされたデータ:
>>> text = 'á'
>>> text
'\xc3\xa1'
>>> text.decode('utf-8')
u'\xe1'
使用する unicode()
エラーが発生します...
>>> unicode(text)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0:
ordinal not in range(128)
...しかし、エンコーディングがわかっていれば、それを 2 番目のパラメータとして使用できます。
>>> unicode(text, 'utf-8')
u'\xe1'
>>> unicode(text, 'utf-8') == text.decode('utf-8')
True
このテキストを返すクラスがあるとします。 __str__()
方法:
>>> class ReturnsEncoded(object):
... def __str__(self):
... return text
...
>>> r = ReturnsEncoded()
>>> str(r)
'\xc3\xa1'
unicode(r)
使っているようです str()
と同じエラーが発生するため、 unicode(text)
その上:
>>> unicode(r)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0:
ordinal not in range(128)
ここまではすべて計画通りです!
しかし、誰も予想していなかったように、 unicode(r, 'utf-8')
試してもみません:
>>> unicode(r, 'utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: coercing to Unicode: need string or buffer, ReturnsEncoded found
なぜ?なぜこのような一貫性のない動作が起こるのでしょうか?バグですか?それは意図されたものですか?とても気まずい。
解決
この動作は混乱を招くように見えますが、意図的なものです。ここに、Unicode ドキュメント全体を転載します。 Python 組み込み関数のドキュメント (これを書いている時点ではバージョン 2.5.2 の場合):
unicode([オブジェクト[, エンコード[, エラー]]])
次のいずれかのモードを使用して、オブジェクトの Unicode 文字列バージョンを返します。
エンコードおよび/またはエラーが指定されている場合、Unicode()は、エンコードのためにコーデックを使用して、8ビット文字列または文字バッファーのいずれかであるオブジェクトをデコードします。エンコーディングパラメーターは、エンコードの名前を与える文字列です。エンコーディングが知られていない場合、lookuperrorが上昇します。エラー処理はエラーに従って行われます。これは、入力エンコードで無効な文字の処理を指定します。エラーが「厳格」(デフォルト)の場合、バリューエラーがエラーで上昇し、「無視」の値はエラーを静かに無視し、「置換」の値は公式のユニコード置換文字u+fffdを引き起こします。デコードできない入力文字を交換するために使用されます。も参照してください。 コーデック モジュール。
オプションのパラメーターが与えられていない場合、Unicode()は、8ビット文字列の代わりにUnicode文字列を返すことを除いて、str()の動作を模倣します。より正確には、オブジェクトがUnicode文字列またはサブクラスである場合、追加のデコードを適用することなく、そのUnicode文字列を返します。
__ unicode __()メソッドを提供するオブジェクトの場合、Unicode文字列を作成するために引数なしでこの方法を呼び出します。他のすべてのオブジェクトについては、8ビット文字列バージョンまたは表現が要求され、「Strict」モードでデフォルトのエンコードのためにコーデックを使用してUnicode文字列に変換されます。
バージョン 2.0 の新機能。バージョン 2.2 で変更された点:__unicode__() のサポートが追加されました。
それで、あなたが電話するとき、 unicode(r, 'utf-8')
, 、最初の引数として 8 ビット文字列または文字バッファーが必要なので、 __str__()
メソッドを使用し、それをデコードしようとします。 utf-8
コーデック。なしで utf-8
, 、 unicode()
関数は a を探します __unicode__()
オブジェクトのメソッドが見つからず、 __str__()
あなたが提案したように、デフォルトのコーデックを使用して Unicode に変換しようとしています。
他のヒント
unicode
はテキストのエンコーディングを推測しません。オブジェクト自体を次のように出力できる場合 unicode
, を定義します。 __unicode__()
Unicode 文字列を返すメソッド。
その秘密は、 unicode(r)
実際に電話をかけているわけではありません __str__()
自体。代わりに、 __unicode__()
方法。デフォルトの実装は、 __unicode__()
電話します __str__()
そして、それを使用してデコードを試みます ascii
文字コード。エンコーディングを渡すと、 unicode()
最初のオブジェクトがデコードできるもの、つまり、のインスタンスであることを期待します。 basestring
.
「utf-8」を渡さないとASCIIとしてデコードしようとするため、動作が奇妙です。しかし、「utf-8」を渡すと、別のエラーが発生します...
これは、「utf-8」を指定すると、最初のパラメータがデコードされる文字列のようなオブジェクトとして扱われるためです。これがないと、パラメータは Unicode に強制されるオブジェクトとして扱われます。
混乱がわかりません。そのオブジェクトが text
属性は常に UTF-8 でエンコードされます。定義するだけです __unicode__()
そうすればすべてがうまくいくでしょう。