Юникод для чайников

    logo
    Сам я не очень люблю заголовки вроде «Покемоны в собственном соку для чайников\кастрюль\сковородок», но это кажется именно тот случай — говорить будем о базовых вещах, работа с которыми довольно часто приводить к купе набитых шишек и уйме потерянного времени вокруг вопроса — «Почему же оно не работает?». Если вы до сих пор боитесь и\или не понимаете Юникода — прошу под кат.


    Зачем?


    Главный вопрос новичка, который встречается с впечатляющим количеством кодировок и на первый взгляд запутанными механизмами работы с ними (например, в Python 2.x). Краткий ответ — потому что так сложилось :)

    Кодировкой, кто не знает, называют способ представления в памяти компьютера (читай — в нулях-единицах\числах) цифр, буков и всех остальных знаков. Например, пробел представляется как 0b100000 (в двоичной), 32 (в десятичной) или 0x20 (в шестнадцатеричной системе счисления).

    Так вот, когда-то памяти было совсем немного и всем компьютерам было достаточно 7 бит для представления всех нужных символов (цифры, строчный\прописной латинский алфавит, куча знаков и так называемые управляемые символы — все возможные 127 номеров были кому-то отданы). Кодировка в это время была одна — ASCII. Шло время, все были счастливы, а кто не был счастлив (читай — кому не хватало знака "©" или родной буквы «щ») — использовали оставшиеся 128 знаков на свое усмотрение, то есть создавали новые кодировки. Так появились и ISO-8859-1, и наши (то есть кириличные) cp1251 и KOI8. Вместе с ними появилась и проблема интерпретации байтов типа 0b1******* (то есть символов\чисел от 128 и до 255) — например, 0b11011111 в кодировке cp1251 это наша родная «Я», в тоже время в кодировке ISO-8859-1 это греческая немецкая Eszett (подсказывает Moonrise) "ß". Ожидаемо, сетевая коммуникация и просто обмен файлами между разными компьютерами превратились в чёрт-знает-что, несмотря на то, что заголовки типа 'Content-Encoding' в HTTP протоколе, email-письмах и HTML-страницах немного спасали ситуацию.

    В этот момент собрались светлые умы и предложили новый стандарт — Unicode. Это именно стандарт, а не кодировка — сам по себе Юникод не определяет, как символы будут сохранятся на жестком диске или передаваться по сети. Он лишь определяет связь между символом и некоторым числом, а формат, согласно с которым эти числа будут превращаться в байты, определяется Юникод-кодировками (например, UTF-8 или UTF-16). На данный момент в Юникод-стандарте есть немного более 100 тысяч символов, тогда как UTF-16 позволяет поддерживать более одного миллиона (UTF-8 — и того больше).

    Полней и веселей по теме советую почитать у великолепного Джоеля Спольски The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets.

    Ближе к делу!


    Естественно, есть поддержка Юникода и в Пайтоне. Но, к сожалению, только в Python 3 все строки стали юникодом, и новичкам приходиться убиваться об ошибки типа:

    >>> with open('1.txt') as fh:
    	s = fh.read()
    
    >>> print s
    кощей
    >>> parser_result = u'баба-яга'  # присвоение для наглядности, представим себе, что это результат работы какого-то парсера
    >>> parser_result + s
    
    Traceback (most recent call last):
      File "<pyshell#43>", line 1, in <module>
        parser_result + s
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xea in position 0: ordinal not in range(128)
    

    или так:
    >>> str(parser_result)
    
    Traceback (most recent call last):
      File "<pyshell#52>", line 1, in <module>
        str(parser_result)
    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
    

    Давайте разберемся, но по порядку.

    Зачем кто-то использует Юникод?

    Почему мой любимый html-парсер возвращает Юникод? Пусть возвращает обычную строку, а я там уже с ней разберусь! Верно? Не совсем. Хотя каждый из существующих в Юникоде символов и можно (наверное) представить в некоторой однобайтовой кодировке (ISO-8859-1, cp1251 и другие называют однобайтовыми, поскольку любой символ они кодируют ровно в один байт), но что делать если в строке должны быть символы с разных кодировок? Присваивать отдельную кодировку каждому символу? Нет, конечно, надо использовать Юникод.

    Зачем нам новый тип «unicode»?

    Вот мы и добрались до самого интересного. Что такое строка в Python 2.x? Это просто байты. Просто бинарные данные, которые могут быть чем-угодно. На самом деле, когда мы пишем что-нибудь вроде:
    >>> x = 'abcd'
    >>> x
    'abcd'
    
    интерпретатор не создает переменную, которая содержит первые четыре буквы латинского алфавита, но только последовательность
    ('a', 'b', 'c', 'd')
    с четырёх байт, и латинские буквы здесь используются исключительно для обозначения именно этого значения байта. То есть 'a' здесь просто синоним для написания '\x61', и ни чуточку больше. Например:

    >>> '\x61' 
    'a'
    >>> struct.unpack('>4b', x)  # 'x' - это просто четыре signed/unsigned char-а
    (97, 98, 99, 100)
    >>> struct.unpack('>2h', x)  # или два short-а
    (24930, 25444)
    >>> struct.unpack('>l', x)  # или один long
    (1633837924,)
    >>> struct.unpack('>f', x)  # или float
    (2.6100787562286154e+20,)
    >>> struct.unpack('>d', x * 2)   # ну или половинка double-а
    (1.2926117739473244e+161,)
    

    И всё!

    И ответ на вопрос — зачем нам «unicode» уже более очевиден — нужен тип, который будет представятся символами, а не байтами.

    Хорошо, я понял чем есть строка. Тогда что такое Юникод в Пайтоне?

    «type unicode» — это прежде всего абстракция, которая реализует идею Юникода (набор символов и связанных с ними чисел). Объект типа «unicode» — это уже не последовательность байт, но последовательность собственно символов без какого либо представления о том, как эти символы эффективно сохранить в памяти компьютера. Если хотите — это более высокой уровень абстракции, чем байтовый строки (именно так в Python 3 называют обычные строки, которые используются в Python 2.6).

    Как пользоваться Юникодом?

    Юникод-строку в Python 2.6 можно создать тремя (как минимум, естественно) способами:
    • u"" литерал:
      >>> u'abc'
      u'abc'
      

    • Метод «decode» для байтовой строки:
      >>> 'abc'.decode('ascii')
      u'abc'
      

    • Функция «unicode»:
      >>> unicode('abc', 'ascii')
      u'abc'
      

    ascii в последних двух примерах указывается в качестве кодировки, что будет использоваться для превращения байтов в символы. Этапы этого превращения выглядят примерно так:

    '\x61' -> кодировка ascii -> строчная латинская "a" -> u'\u0061' (unicode-point для этой буквы)
    
    или
    
    '\xe0' -> кодировка c1251 -> строчная кириличная "a" -> u'\u0430'
    


    Как из юникод-строки получить обычную? Закодировать её:

    >>> u'abc'.encode('ascii')
    'abc'
    


    Алгоритм кодирования естественно обратный приведенному выше.

    Запоминаем и не путаем — юникод == символы, строка == байты, и байты -> что-то значащее (символы) — это де-кодирование (decode), а символы -> байты — кодирование (encode).

    Не кодируется :(

    Разберем примеры с начала статьи. Как работает конкатенация строки и юникод-строки? Простая строка должна быть превращена в юникод-строку, и поскольку интерпретатор не знает кодировки, от использует кодировку по умолчанию — ascii. Если этой кодировке не удастся декодировать строку, получим некрасивую ошибку. В таком случае нам нужно самим привести строку к юникод-строке, используя правильную кодировку:

    >>> print type(parser_result), parser_result
    <type 'unicode'> баба-яга
    >>> s = 'кощей'
    >>> parser_result + s
    
    Traceback (most recent call last):
      File "<pyshell#67>", line 1, in <module>
        parser_result + s
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xea in position 0: ordinal not in range(128)
    
    >>> parser_result + s.decode('cp1251')
    u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0\u043a\u043e\u0449\u0435\u0439'
    >>> print parser_result + s.decode('cp1251')
    баба-ягакощей
    >>> print '&'.join((parser_result, s.decode('cp1251')))
    баба-яга&кощей   # Так лучше :)
    


    «UnicodeDecodeError» обычно есть свидетельством того, что нужно декодировать строку в юникод, используя правильную кодировку.

    Теперь использование «str» и юникод-строк. Не используйте «str» и юникод строки :) В «str» нет возможности указать кодировку, соответственно кодировка по умолчанию будет использоваться всегда и любые символы > 128 будут приводить к ошибке. Используйте метод «encode»:

    >>> print type(s), s
    <type 'unicode'> кощей
    >>> str(s)
    
    Traceback (most recent call last):
      File "<pyshell#90>", line 1, in <module>
        str(s)
    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
    >>> s = s.encode('cp1251')
    >>> print type(s), s
    <type 'str'> кощей


    «UnicodeEncodeError» — знак того, что нам нужно указать правильную кодировку во время превращения юникод-строки в обычную (или использовать второй параметр 'ignore'\'replace'\'xmlcharrefreplace' в методе «encode»).

    Хочу ещё!

    Хорошо, используем бабу-ягу из примера выше ещё раз:

    >>> parser_result = u'баба-яга'   #1
    >>> parser_result
    u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0'   #2
    >>> print parser_result
    áàáà-ÿãà   #3
    >>> print parser_result.encode('latin1')  #4
    баба-яга
    >>> print parser_result.encode('latin1').decode('cp1251')  #5
    баба-яга
    >>> print unicode('баба-яга', 'cp1251')   #6
    баба-яга
    

    Пример не совсем простой, но тут есть всё (ну или почти всё). Что здесь происходит:
    1. Что имеем на входе? Байты, которые IDLE передает интерпретатору. Что нужно на выходе? Юникод, то есть символы. Осталось байты превратить в символы — но ведь надо кодировку, правда? Какая кодировка будет использована? Смотрим дальше.
    2. Здесь важной момент:
      >>> 'баба-яга'
      '\xe1\xe0\xe1\xe0-\xff\xe3\xe0'
      >>> u'\u00e1\u00e0\u00e1\u00e0-\u00ff\u00e3\u00e0' == u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0'
      True
      
      как видим, Пайтон не заморачивается с выбором кодировки — байты просто превращаются в юникод-поинты:
      >>> ord('а')
      224
      >>> ord(u'а')
      224
      
    3. Только вот проблема — 224-ый символ в cp1251 (кодировка, которая используется интерпретатором) совсем не тот, что 224 в Юникоде. Именно из-за этого получаем кракозябры при попытке напечатать нашу юникод-строку.
    4. Как помочь бабе? Оказывается, что первые 256 символов Юникода те же, что и в кодировке ISO-8859-1\latin1, соответственно, если используем её для кодировки юникод-строки, получим те байты, которые вводили сами (кому интересно — Objects/unicodeobject.c, ищем определение функции «unicode_encode_ucs1»):
      >>> parser_result.encode('latin1')
      '\xe1\xe0\xe1\xe0-\xff\xe3\xe0'
      
    5. Как же получить бабу в юникоде? Надо указать, какую кодировку использовать:
      >>> parser_result.encode('latin1').decode('cp1251')
      u'\u0431\u0430\u0431\u0430-\u044f\u0433\u0430'
      
    6. Способ с пункта #5 конечно не ахти, намного удобней использовать использовать built-in unicode.
    На самом деле не всё так плохо с «u''» литералами, поскольку проблема возникает только в консоле. Ведь в случае использования non-ascii символов в исходном файле Пайтон будет настаивать на использовании заголовка типа "# -*- coding: -*-" (PEP 0263), и юникод-строки будут использовать правильную кодировку.

    Есть ещё способ использования «u''» для представления, например, кириллицы, и при этом не указывать кодировку или нечитабельные юникод-поинты (то есть «u'\u1234'»). Способ не совсем удобный, но интересный — использовать unicode entity codes:

    >>> s = u'\N{CYRILLIC SMALL LETTER KA}\N{CYRILLIC SMALL LETTER O}\N{CYRILLIC SMALL LETTER SHCHA}\N{CYRILLIC SMALL LETTER IE}\N{CYRILLIC SMALL LETTER SHORT I}'
    >>> print s
    кощей
    


    Ну и вроде всё. Основные советы — не путать «encode»\«decode» и понимать различия между байтами и символами.

    Python 3

    Здесь без кода, ибо опыта нет. Свидетели утверждают, что там всё значительно проще и веселее. Кто возьмется на кошках продемонстрировать различия между здесь (Python 2.x) и там (Python 3.x) — респект и уважуха.

    Полезно


    Раз уж мы о кодировках, порекомендую ресурс, который время-от-времени помогает побороть кракозябры — http://2cyr.com/decode/?lang=ru.

    Ещё раз линк на статью Спольски — The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets.

    Unicode HOWTO — официальный документ о том где, как и зачем Юникод в Python 2.x.

    Спасибо за внимание. Буду благодарен за замечания в приват.

    P.S. Подкинули линк на перевод Спольски — Абсолютный Минимум, который Каждый Разработчик Программного Обеспечения Обязательно Должен Знать о Unicode и Наборах Символов.
    Метки:
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 53
    • +7
      Годная, полезная статья.
      Я бы еще раскрыл тему как читать/писать юникодные файлы: codecs.open вместо file() и про PEP 0263.
      • –1
        по поводу файлов

        #! /usr/bin/env python
        # -*- coding: utf-8 -*-

        операции с файлами становятся уникодными=)

        ну еще со строками магия проходит вроде
        регулярки работают
        m=re.match('^(第)([0-9]+)(届|期)',s)

        Как практика показывает, входные данные лучше отгонять в UTF-8
        тогда проблемы вроде того что одни и теже данные python, c++ и так далее не сильно актуальные…
        Отдельно надо выделить если происходят операции за Базой данных тут кодировки тоже важно…

        З.Ы каждый программист должен начинать новый язык с UTF-8 / Unicode :) решать вопрос чтобы устойчивая программа была к любым символам
        • +2
          Не операции становятся уникодными, а исходный текст скрипта :).
          Файлы [данных] все же придется читать и декодировать руками.

          > входные данные лучше отгонять в UTF-8

          Ни в коем случае! UTF-8 — это внешнее представление. Входные данные надо отгонять в юникод, со всеми строками работать только в юникоде, а сохранять снова в UTF-8. Кстати, в C++ c UTF-8 вообще можно повеситься (ну или забыть работу со строками).
          • 0
            Насчет входных файлов соглашусь…

            У меня просто все данные в UTF-8, входные из файлов поэтому работает 2.6/2.7
            Внешение представление в UTF-8 спасает…

            UTF-8 для символов a-z 1byte если идет кирилица А_Я и так далее 2 byte.

            С точки зрения C/C++ когда strlen(ansi) = 10 с точки зрения strlen(utf-8) 10 длина не понятна(может символы 1 +2 +2 +2 +1+1 ).

            Если код не ориентирован на чёткий поиск, а склейка строк то strcpy/strcat можно пользовать…

            з.ы. пора тему Unicode / UTF-8/ UTF-16 как курс или пособие, обобщение для разных языков…
      • –44
        Чё за бред? Хабр — УГ.
        • +2
          Толсто.
          • +3
            Терпите. Скоро вас запикает НЛО.
            • 0
              Что полезного сделали вы для хабрсообщества, чтоб его критиковать?
              • +3
                Хабрасэппуку?
                • 0
                  Это вы зря. Мне недавно пришла идея автоматического пополнения словаря с использованием нейронной сети. Идея не нова, просто самообучался. Столкнулся с проблемой кодировки, перерыл полинтернета. Нашел ответы на вопросы, но все очень разрозненно, плюс умные люди помогли, теория все равно осталась мне не понятна. А это фактически единственная статья в рунете, которая все раскладывает по полочкам.
              • 0
                Непонятно, чем «символы» отличаются от байтов. В конечном счете они все равно хранятся как любая другая информация в памяти — как байты. Поясните разницу.
                • +5
                  Символ — это, например, «CYRILLIC SMALL LETTER A», а байт — это нолики и еденицы, например '\xe0'. Для того, что б байт стал символом, необходима кодировка. И в зависимоти от кодировки, этот байт (или байты) может представлять разные символы.
                  • 0
                    Определение одного символа не обязательно умещается в один байт.
                    • 0
                      Я и сказал «чем символы отличаются от байтов?» а не «чем символ отличается от байта?».
                      Я понимаю, что символ может быть больше байта, я не понимаю вот это «не обязательно».
                      То есть в юникоде разные символы имеют разный размер? Вообще что можно почитать по этому поводу. поменьше и попроще, чем стандарт?
                      • +3
                        В Unicode определяются code points. Но code point — это число + название, он не задаёт байты. Для того, чтобы преобразовать последовательность code points в байты, вам нужно выбрать некоторый Unicode Transfotmation Format (UTF) и закодировать в соответствии с его правилами.
                        • 0
                          Да, но число — разве оно и не есть эти самые байты? А названия — разве не однозначно соответствуют числам?
                          • +1
                            Нет, число — это просто целое число. Оно может быть закодировано в виде байт (причём правила различны в зависимости от UTF, и ещё и не всеми битами этих байтов).
                  • 0
                    Один символ может занимать несколько байт.
                    • 0
                      Похоже что косяки машинного перевода немного накладывает отпечаток…
                      ...«Хорошо, я понял чем есть строка.»…

                      А так по логике вещей показалось странным, что:
                      байты в Unicode — это кодирование
                      а Unicode в батый — это де-кодирование

                      интуитивно хочется наоборот… хотя смысл, понятен, и зависит от того что-во-что кодировать и следовательно де-кодировать
                      • +1
                        А мне именно такая логика кажется естественной.
                        У символа есть «код», мы получаем код из символа декодированием оного.
                        • +1
                          Нет, это просто стиль такой выбран. А вобще автор совсем не русско-говорящий :)

                          >> интуитивно хочется наоборот

                          дык на самом деле и есть наоборот — байты в Unicode — это декодирование.
                          • +1
                            "string".decode('ascii') # декодируем в  юникод
                            u"string".encode('ascii') # переводим юникод в кодировку ascii


                            Все как раз наоборот
                          • +1
                            >>
                            На данный момент в Юникод-стандарте есть немного более 100 тысяч символов, тогда как UTF-16 позволяет поддерживать более одного миллиона (UTF-8 — и того больше).


                            На сколько я понимаю, UTF-16 может кодировать максимум 65 535 символов. Возможно, вы ошиблись?
                            • +3
                              Тогда UTF-8 кодирует 255 символов?

                              Нет. «16» в UTF-16 означает размер используемого слова: каждый code point в UTF-16 кодируется одним или более 16-битными словами.
                              • 0
                                Да, вы правы. Принцип работы UTF-16 аналогичен UTF-8 с разницей лишь в размере слова.

                                До этого момента я свято верил в то, что wchar_t в Windows — это UTF-16. Оказывается, это UCS-2 — строго 16-битная кодировка без возможности расширения до 32 бит.
                              • 0
                                16 в названии совсем не значит, что используются 16 бит\2 байта\65 535 доступных комбинаций. Деталей кодировки не знаю, число взял сами знаете откуда. Посмотреть детали думаю можно там же (пока не закрыли в знак протеста против SOPA:) )
                                • +2
                                  Согласно Вики и в UTF-8 и в UTF-16 равное число возможных символов — 1,112,064
                                  • +3
                                    Совершенно верно. Но можно расширить по аналогии правила построения UTF-8 на более длинные последовательности и получить так называемые overlong sequences. Хотя в стандарте прямо написано, что они ошибочны, к сожалению некоторые «хакеры» считают, что они выше стандарта и говорят, что в UTF-8 больше кодов, чем написано в стандарте, да ещё и реализуют их поддержку в своих программах (например, в eglibc bugs.debian.org/cgi-bin/bugreport.cgi?bug=555922 ).

                                    И да, не называйте юникодные code points символами. Не каждый code point можно нарисовать в виде символа и даже есть такие code points, которые называются noncharacters. См. также en.wikipedia.org/wiki/Mapping_of_Unicode_characters
                                    • +1
                                      ммм… Code point. Пасиб, проще с такой терминологией работать.
                                      Какой-то русский аналог у этого названия есть?
                                      • 0
                                        Не слышал, но возможно «кодовая позиция Unicode»?
                                      • +1
                                        прочитал ссылку на баг в eglibc. мдааа…
                                        Ещё один кейс на проверку в программах (я тестировщик). Правда не очень понятно кого винить потом проблеме — своих разработчиков, или разработчиков билиотеки которую они использовали.
                                        Особенно «порадовал» комментарий — «Nobody has ever shown any evidence why this is a bad idea.»
                                        Стандарты видимо люди тоже просто так придумывали…
                                  • +7
                                    > в тоже время в кодировке ISO-8859-1 это греческая "ß".
                                    В ISO-8859-1 нет греческих букв. Это немецкая Eszett. (на правах занудства)
                                    • 0
                                      Спасибо за занудство:)
                                      • 0
                                        Хорошая статья, советую еще раскрыть тему локалей, без этого материал неполный.

                                        Жаль что не смотря на теорию проблема кодировок это сверхбольное место питона, которое каждый раз съедает у меня массу сил и времени. Заткнулся какой ни будь встроенный модуль для работы с gzip на русскоязычных именах файлах и рабочий день коту под хвост.

                                        В python 3000 демонстрировать особо нечего, для самого питона проблема решена на корню как в java, все строки — юникод, на практике ад с портированием библиотек и, имхо, эта ветка сдохнет и на ее место доэволюционирует 2.x, х/з как при этом решится проблема со строками.
                                        • +1
                                          Помер как раз 2.х
                                          3.3 выкатят к осени.
                                          Трудности с портированием есть, но «адом» я их не назову.
                                        • +2
                                          Мне очень понравилась тема Unicode в Dive into Python 3
                                          diveintopython3.ep.io/strings.html
                                          • –1
                                            Честно говоря, до этой статьи я понимал что такое кодировки и как с ними работать.
                                            Очень тяжелая статья для новичков, которая лишь внесет кашу в головы.
                                            • 0
                                              Увидел сначала сколько букв — испугался.
                                              Когда осилил, понял, что не так уж и много. Читается нормально.
                                              Полезная статья!
                                              • +1
                                                В вводной забыли про EBCDIC, они с ASCII практически ровесники.
                                                • 0
                                                  Нужная статья, основная полезность направление decode / encode.
                                                  Некоторая мнемоника:
                                                  1. Для себя сделал ассоциации типа decode более тяжелое по звучанию соответственно создает тяжелый utf,
                                                  encode легкое создает легковесный байт.
                                                  2. Байты это непонятный код, который чтобы увидеть надо декодировать в символы (utf), а чтобы символы превратить в код (т.е. байты), их соответственно нужно закодировать (encode).
                                                  • 0
                                                    На виндах у петона вечные проблемы с юникодом, что у третьего, что у второго. На линуксе с этим проще.
                                                    • 0
                                                      Да и не только с юникодом.
                                                      Да, я по это причине все пытаюсь переползти на макось но пока не складывается.
                                                      • +2
                                                        А чем проблемность юникода в Windows вызвана?
                                                        • –2
                                                          видимо тем, что родная для винды CP1251
                                                        • 0
                                                          не волнуйтесь, в линуксе тоже есть вечные проблемы с вводом/выводом изнутри библиотек.
                                                          например, stderr направленный в файл — работает, а на консоль — падает, а на appengine — генерит servererror.
                                                        • 0
                                                          Правильно ли я понимаю, что если везде и всюду в константах и на входе программы будет только юникод (вместо байтстрок) — это избавит от проблем вида «UnicodeDecodeError» в крайне неожиданных местах?
                                                          (например, от лютых кабздецов, когда appengine-приложение падает в «server error» из-за того, что логгер не может нарисовать лог.)
                                                          • 0
                                                            А в каком формате сам python внутри хранит строки в 2.х и 3.х?
                                                            • 0
                                                              Строки (str)? Естественно, байты char[]
                                                              Unicode — платформозависимо, обычно UCS-2(4)
                                                              Хотя что понимать под «хранит внутри» — я о памяти запущенной программы.
                                                            • 0
                                                              Забавно, вот что по ссылке на перевод статьи Спольски :)
                                                              • 0
                                                                картинка для предыдущего комментария img.skitch.com/20120320-mb9qxs4tukfin98ria2wb7rjt3.png
                                                                • 0
                                                                  Меня спасает
                                                                  # -*- coding: utf-8 -*-
                                                                  from __future__ import unicode_literals
                                                                  


                                                                  Таким образом весь текст становится unicode, если не укажете что он b'текст'

                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.