Базовый строковый тип str как байтовая последовательность

Раздел: Основы Python -> Типы данных в разных версиях Python

Тип str в Python 2: байтовая строка по умолчанию

Основной подход: явное разделение str и unicode

В Python 2 строковый тип str представляет собой последовательность байтов. Для символов, не входящих в ASCII, требуется работа с типом unicode. Наиболее эффективное решение - всегда использовать unicode внутри программы и преобразовывать в байтовую строку только при вводе/выводе.

Пример объявления unicode-строки:

# Литерал Unicode в Python 2
my_string = u"Привет, мир!"
print type(my_string)  # <type 'unicode'>

Str python 2 (тип str в python 2)

Преобразование в байтовую строку (str) через кодировку UTF-8:

byte_str = my_string.encode('utf-8')
print type(byte_str)     # <type 'str'>
print repr(byte_str)     # '\xd0\x9f\xd1\x80...'

Str python 3 (тип str в python 3)

Обратное преобразование:

decoded_back = byte_str.decode('utf-8')
print type(decoded_back) # <type 'unicode'>

Как избежать путаницы между str и unicode в исходном коде?

Решение: использовать from __future__ import unicode_literals. Тогда все строковые литералы без префикса становятся unicode, а не str.

from __future__ import unicode_literals

normal = "строка"  # это unicode, а не str
print type(normal)  # <type 'unicode'>

Проблема:

Если в коде есть конструкции, полагающиеся на str (например, %s форматирование), может возникнуть неявное преобразование. Рекомендуется всегда явно указывать .encode() при выводе.

Как обрабатывать файлы с разными кодировками в Python 2?

Решение: использовать io.open с указанием кодировки, чтобы сразу получать unicode.

import io

with io.open('text.txt', 'w', encoding='utf-8') as f:
    f.write(u'Текст на русском')

with io.open('text.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print type(content)  # <type 'unicode'>

Типичная ошибка:

Использование встроенной open() без кодировки возвращает str (байты). Попытка конкатенации str и unicode вызывает UnicodeDecodeError. Решение: всегда явно преобразовывать типы.

Как правильно склеивать str и unicode?

Решение: преобразовать обе части к одному типу (обычно к unicode).

byte_part = 'hello '
unicode_part = u'мир'
# Неправильно: result = byte_part + unicode_part  # UnicodeDecodeError

# Правильно:
result = byte_part.decode('ascii') + unicode_part
print result  # hello мир

Важно:

Python 2 автоматически пытается преобразовать str к unicode при конкатенации, используя ASCII. Если строка содержит не-ASCII байты, возникает ошибка.

Как обработать UnicodeEncodeError при выводе в консоль?

Решение: настроить локаль или явно кодировать вывод.

my_unicode = u'Привет'
# print my_unicode  # может вызвать UnicodeEncodeError

# Безопасный вывод:
print my_unicode.encode('utf-8')

Проблема:

Консоль часто использует кодировку CP1251 или UTF-8. Явное указание кодировки решает проблему.

Расширенные примеры работы со str в Python 2

Пример 1. Преобразование str в unicode и обратно с разными кодировками

Пример
byte_string = '\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'  # байты 'Привет' в utf-8
print type(byte_string)  # <type 'str'>

unicode_string = byte_string.decode('utf-8')
print unicode_string       # Привет

# Кодирование в другую кодировку (CP1251)
cp1251_bytes = unicode_string.encode('cp1251')
print repr(cp1251_bytes)   # '\xcf\xf0\xe8\xe2\xe5\xf2'
<type 'str'>
Привет
'\xcf\xf0\xe8\xe2\xe5\xf2'

Пример 2. Использование from __future__ import unicode_literals

Пример
from __future__ import unicode_literals

# Все литералы - unicode
s1 = "Текст"
s2 = u"Текст"
print type(s1) == type(s2)  # True (оба unicode)

# Но байтовую строку можно создать через b''
byte_s = b'байтовый'
print type(byte_s)          # <type 'str'>
True
<type 'str'>

Пример 3. Работа с кириллицей в именах файлов

Пример
import os
# Создание файла с кириллическим именем
filename = u'файл.txt'
with open(filename.encode('utf-8'), 'w') as f:
    f.write('test')

# Чтение списка файлов
files = os.listdir(u'.')
for f in files:
    print repr(f)  # вывод в байтах
    # если f - байтовая строка, декодировать
    print f.decode('utf-8') if isinstance(f, str) else f
'\xd1\x84\xd0\xb0\xd0\xb9\xd0\xbb.txt'
файл.txt

Пример 4. Обработка ошибок с помощью errors параметра

Пример
# Строка с символами, не представимыми в Latin-1
unicode_str = u'Привет résumé'
# Попытка кодирования с игнорированием ошибок
latin_ignored = unicode_str.encode('latin-1', errors='ignore')
print repr(latin_ignored)  # '?\xef? (только ASCII символы остались)

# Замена на знак вопроса
latin_replace = unicode_str.encode('latin-1', errors='replace')
print repr(latin_replace)  # '??????????? r?sum?'
'?\xef?'
'??????????? r?sum?'

Пример 5. Сравнение str и unicode

Пример
# Две одинаковые строки в разных типах
byte_str = 'abc'
uni_str = u'abc'
print byte_str == uni_str  # True (Python 2 сравнивает по содержанию)

# Но с не-ASCII byte_str != uni_str, если кодировка не совпадает
byte_utf = '\xc3\xa9'   # é в UTF-8
uni = u'\xe9'            # é в юникоде
print byte_utf == uni     # True (если байты корректно декодируются)
True
True

Тип str в Python 2 - comments

En
Str python 2 (python)