Самая большая проблема, которая не может быть адекватно решена с помощью изменений на микроуровне и 2to3 - это изменение типа строки по умолчанию с байтов на Unicode.
Если ваш код должен что-то делать с кодировками и байтовым вводом-выводом /О, для правильного преобразования потребуется куча ручных усилий, чтобы вещи, которые должны быть байтами, оставались байтами и соответствующим образом декодировались на нужном этапе.Вы обнаружите, что для некоторых строковых методов (в частности, format()
) и библиотечных вызовов требуются строки Unicode, поэтому вам могут потребоваться дополнительные циклы декодирования / кодирования, чтобы использовать строки как Unicode, даже если они на самом деле просто байты.
Этому не помогает тот факт, что некоторые модули стандартной библиотеки Python были грубо преобразованы с использованием 2to3 без должного внимания к проблемам байтов / юникода / кодирования, и поэтому сами делают ошибки в отношении того, какой тип строки подходит.Некоторая часть этого процесса была переработана, но по крайней мере с Python 3.0 до 3.2 вы столкнетесь с запутанным и потенциально ошибочным поведением пакетов, таких как urllib, email и wsgiref, которые должны знать о байт-кодировках.
Вы можете улучшитьпроблема в том, чтобы быть осторожным каждый раз, когда вы пишете строковый литерал.Используйте u''
строки для всего, что по своей сути основано на символах, b''
строки для всего, что на самом деле является байтами, и ''
для типа «строка по умолчанию», где это не имеет значения, или вам нужно соответствовать строковому использованию библиотечного вызоватребования.
К сожалению, синтаксис b''
был введен только в Python 2.6, поэтому это отключает пользователей более ранних версий.
eta:
какая разница?
О, боже.Ну ...
Байт содержит значение в диапазоне 0–255 и может представлять собой загрузку двоичных данных (например, содержимого изображения) или некоторого текста, и в этом случае должно бытьстандарт, выбранный для того, чтобы отобразить набор символов в эти байты.Большинство этих стандартов «кодирования» отображают обычный набор символов «ASCII» в байты 0–127 таким же образом, поэтому обычно безопасно использовать байтовые строки для обработки текста только в ASCII в Python 2.
Если вы хотите использовать какой-либо символ вне набора ASCII в байтовой строке, у вас возникнут проблемы, потому что каждая кодировка отображает различный набор символов в оставшиеся байтовые значения 128–255, и большинство кодировок не может отображать всевозможный символ в байтах.Это источник всех тех проблем, когда вы загружаете файл из одной локали в приложение Windows в другой локали, и все буквы с ударением или нелатинские буквы заменяются на неправильные, создавая нечитаемый беспорядок.(aka 'mojibake'.)
Существуют также «многобайтовые» кодировки, которые пытаются разместить больше символов в доступном пространстве, используя более одного байта для хранения каждого символа.Они были введены для восточноазиатских языков, так как китайских иероглифов очень много.Но есть и UTF-8, более совершенная современная многобайтовая кодировка, которая может вместить каждый символ.
Если вы работаете со строками байтов в многобайтовой кодировке - и сегодня вы, вероятно, будетепотому что UTF-8 очень широко используется;действительно, никакое другое кодирование не должно использоваться в современном приложении - тогда у вас есть еще больше проблем, чем просто отслеживание того, с какой кодировкой вы играете.len()
будет сообщать вам длину в байтах, а не длину в символах, и если вы начнете индексировать и изменять байты, вы очень вероятно разбьете многобайтовую последовательность на две части, создав недопустимую последовательность и, как правило, все запутаете.
По этой причине Python1.6 и более поздние версии имеют собственные строки Unicode (пишутся u'something'
), где каждая единица в строке является символом, а не байтом. Вы можете len()
их, нарезать на куски, заменить их, пересмотреть их, и они всегда будут вести себя соответствующим образом. Для задач обработки текста они, безусловно, лучше, поэтому Python 3 делает их типом строки по умолчанию (без необходимости ставить u
перед ''
).
Смысл в том, что многие существующие интерфейсы, такие как имена файлов в ОС, отличных от Windows, или HTTP, или SMTP, в основном основаны на байтах, с отдельным способом задания кодировки. Поэтому, когда вы имеете дело с компонентами, которым требуются байты, вы должны позаботиться о том, чтобы правильно кодировать свои строки юникода в байты, а в Python 3 вам придется делать это явно в некоторых местах, где раньше вам это не нужно.
Внутренняя деталь реализации заключается в том, что строки Unicode занимают «два байта» памяти на единицу внутри. Вы никогда не увидите это хранилище; Вы не должны думать об этом с точки зрения байтов. Единицы, над которыми вы работаете, являются концептуально символами, независимо от того, как Python решает представлять их в памяти.
... кроме:
Это не совсем так. В «узких сборках» Python, таких как сборка Windows, каждая единица строки Unicode технически не символ, а «кодовая единица» UTF-16. Для символов в базовой многоязычной плоскости, от 0x0000–0xFFFF, вы не заметите никакой разницы, но если вы используете символы из этого 16-битного диапазона, находящиеся на «астральных планах», вы обнаружите, что они принимают два юнита вместо одного, и, опять же, вы рискуете разделить персонажа, когда будете их разрезать.
Это довольно плохо, и произошло это потому, что Windows (и другие, такие как Java) остановились на UTF-16 как механизме хранения в памяти до того, как Unicode превысил предел в 65 000 символов. Тем не менее, использование этих расширенных символов все еще довольно редко, и любой пользователь Windows будет использовать их во многих приложениях, так что это, вероятно, не критично для вас.
В «широких сборках» строки Unicode состоят из реальных символьных «кодовых точек», поэтому даже расширенные символы вне BMP могут обрабатываться последовательно и легко. Платой за это является эффективность: каждая строковая единица занимает четыре байта в памяти.