Python 2.6でunicode_literalsを使用する落とし穴はありますか?
-
03-07-2019 - |
質問
すでにPython 2.6で実行するコードベースを取得しています。 Python 3.0の準備をするために、以下を追加し始めました:
from __future__ import unicode_literals
.py
ファイルに(変更時に)。他の誰かがこれをやっていて、明らかでない落とし穴にぶつかったのではないかと思っています(おそらくデバッグに多くの時間を費やした後)。
解決
Unicode文字列で作業していた問題の主な原因は、utf-8でエンコードされた文字列とUnicode文字列を混在させることです。
たとえば、次のスクリプトを検討してください。
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
実行中の 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
は unicode_literals
と one.name
はUnicode文字列です。両方を混在させると、Pythonはエンコードされた文字列(asciiであると想定)のデコードを試み、ユニコードに変換して失敗します。 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')%html
または print 'DEBUG:%s'%html.decode( 'utf-8 ')
。
これが、ユニコード文字列を使用する際の潜在的な落とし穴を理解するのに役立つことを願っています。
他のヒント
また、2.6(Python 2.6.5 RC1 +より前)のユニコードリテラルは、キーワード引数( issue4978 ):
たとえば、次のコードはunicode_literalsがなくても機能しますが、TypeErrorで失敗します。unicode_literalsを使用する場合は、 keywords must be string
。
>>> 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
unicode_literals
ディレクティブを追加する場合、次のようなものも追加する必要があることがわかりました。
# -*- coding: utf-8
.pyファイルの1行目または2行目。それ以外の場合:
などの行 foo = "barré"
次のようなエラーが発生します
SyntaxError: Non-ASCII character '\xc3' in file mumble.py on line 198, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
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+
2番目のアサーションは、Python 2.7では repr( '\ xa4')
が u '\ xa4'
に評価されるため、機能します。
他にもあります。
ユニコードを許容しない文字列を期待するライブラリとビルトインがあります。
2つの例:
builtin:
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()(これは私のものです)の呼び出しでいっぱいの場合、後者は壊滅的です。
やった、えっ?!?
クリックすると、至る所でUnicode例外が発生します click.echo
を使用して、 from __future__ import unicode_literals
を持つモジュールがインポートされた場合。それは悪夢です&#8230;