Будь-які проблеми, що використовують unicode_literals в Python 2.6?


101

Ми вже отримали нашу базу коду, що працює під Python 2.6. Щоб підготуватися до Python 3.0, ми почали додавати:

з __future__ імпортуйте unicode_literals

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

Відповіді:


101

Основне джерело проблем, з якими я працював з рядками 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є рядком 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') % htmlабо print 'DEBUG: %s' % html.decode('utf-8').

Я сподіваюсь, що це допоможе вам зрозуміти потенційні проблеми при використанні рядків Unicode.


11
Я хотів би запропонувати , щоб піти з decode()рішеннями замість тих str()чи encode()рішень: чим частіше ви використовуєте об'єкти Unicode, тим ясніше код, так що ви хочете, щоб маніпулювати рядки символів, а НЕ масиви байт з зовні неявної кодуванням.
Ерік О Лебігот

8
Виправте свою термінологію. when you mix utf-8 encoded strings with unicode onesUTF-8 і Unicode не мають 2 різних кодувань; Unicode - це стандарт, а UTF-8 - одна з кодувань, які він визначає.
Кос

11
@Kos: Я думаю , що він означає змішувати «UTF-8 закодованих рядків» об'єкти з юнікодом (звідси береться стверджувати) об'єктами . Перший - тип str, другий - тип unicode. Будучи різними об'єктами, проблема може виникнути, якщо ви спробуєте їх
збити

Це стосується python>=2.6чи python==2.6?
жарт

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
Просто виправлено це FYI, python 2.6.5 RC1 +.
Махмуд Абделькадер

13

Я виявив, що якщо ви додасте unicode_literalsдирективу, ви також повинні додати щось на кшталт:

 # -*- coding: utf-8

до першого чи другого рядка .py-файлу. В іншому випадку такі лінії:

 foo = "barré"

призведе до помилки, наприклад:

SyntaxError: Символ, який не належить до ASCII '\ xc3' у файлі mumble.py у рядку 198,
 але не оголошено кодування; див. http://www.python.org/peps/pep-0263.html
 для деталей

5
@IanMackinnon: Python 3 передбачає, що файли за замовчуванням UTF8
endolith

3
@endolith: Але Python 2 цього не робить, і він видасть синтаксичну помилку, якщо ви використовуєте символи, що не мають функції ascii, навіть у коментарях ! Тож IMHO # -*- coding: utf-8- це фактично обов'язкове твердження, незалежно від того, використовуєте ви його unicode_literalsчи ні
MestreLion

-*-Не потрібно; якби ви їхали по сумісному з emacs способом, я думаю, вам це знадобиться -*- encoding: utf-8 -*-(див. -*-також наприкінці). Все, що вам потрібно, це coding: utf-8(або навіть =замість цього : ).
Кріс Морган

2
Ви отримуєте цю помилку, чи не ви from __future__ import unicode_literals.
Flimm

3
Сумісність Emacs вимагає # -*- coding: utf-8 -*- "кодування" (не "кодування" чи "кодування файлів" чи іншого) - Python просто шукає "кодування" незалежно від будь-якого префікса).
Алекс Дюпюй

7

Також врахуйте, що unicode_literalвплине, eval()але не repr()(асиметрична поведінка, яка imho є помилкою), тобто 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+

Друге твердження буває дієвим, оскільки repr('\xa4')оцінюється u'\xa4'в Python 2.7.


2
Я відчуваю, що більша проблема тут полягає в тому, що ви використовуєте reprдля відновлення об'єкта. У reprдокументації чітко зазначено, що це не вимога. На мою думку, це відноситься reprдо чогось корисного лише для налагодження.
jpmc26

5

Є більше.

Є бібліотеки та вбудовані файли, які очікують рядків, які не переносять unicode.

Два приклади:

вбудований:

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 ", про який йдеться у певній статті, яку я читав у темі). Загалом, unicode_literals стала для мене великою перемогою ...
GreenAsJade

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.