Любые gotchas с использованием литералов unicode в Python 2.6?
мы уже получили нашу базу кода, работающую под Python 2.6. Чтобы подготовиться к Python 3.0, мы начали добавлять:
from __future__ import unicode_literals
в нашей .py
файлы (как мы их изменить). Мне интересно, если кто-то еще делал это и столкнулся с какими-то неочевидными gotchas (возможно, потратив много времени на отладку).
5 ответов:
основной источник проблем, с которыми я работал со строками 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
является кодированной строкой utf-8 (не unicode), так как она не импортировалаunicode_literals
иone.name
- это строка в юникоде. Когда вы смешайте оба, 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'
является строкой Юникода и поэтому python пытается декодироватьhtml
. Несколько способов исправить печать либо делаютprint str('DEBUG: %s') % html
илиprint 'DEBUG: %s' % html.decode('utf-8')
.я надеюсь, что это поможет вам понять потенциальные gotchas при использовании строк unicode.
также в 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
также примите во внимание, что
unicode_literal
влияетeval()
а неrepr()
(асимметричное поведение, которое imho является ошибкой), т. е.eval(repr(b'\xa4'))
не равноb'\xa4'
(как это было бы с Python 3).в идеале, следующий код будет инвариантом, который должен всегда работать, для всех комбинаций
unicode_literals
и Python {2.7, 3.х} использование: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+
второе утверждение работает, так как
repr('\xa4')
оценивает вu'\xa4'
в Python 2.7.
больше.
есть библиотеки и встроенные файлы, которые ожидают строки, которые не допускают unicode.
два примера:
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)
но последнее разрушительно, если ваш код полон звонков в паб.sendMessage () (который мой).
черт, а?!?