Любые gotchas с использованием литералов unicode в Python 2.6?


мы уже получили нашу базу кода, работающую под Python 2.6. Чтобы подготовиться к Python 3.0, мы начали добавлять:

from __future__ import unicode_literals

в нашей .py файлы (как мы их изменить). Мне интересно, если кто-то еще делал это и столкнулся с какими-то неочевидными gotchas (возможно, потратив много времени на отладку).

5 97

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 () (который мой).

черт, а?!?