Короткий ответ
str — это последовательность текстовых символов (Unicode code points), а bytes — последовательность байтов (чисел 0–255), представляющих закодированные данные; UnicodeDecodeError возникает при попытке декодировать bytes в str с кодировкой, которая не соответствует тому, как данные были закодированы на самом деле.
Как это работает подробнее
Текст в памяти Python 3 всегда хранится как str в абстрактном Unicode-представлении. Чтобы отправить текст по сети, записать в файл в бинарном режиме или передать в протокол, str кодируют в bytes через .encode("кодировка"); обратное преобразование — .decode("кодировка"). Если байты были получены в одной кодировке (например, UTF-8 с кириллицей), а decode вызывается с другой (например, ASCII или неправильной cp1251), часть байтовых последовательностей не соответствует ни одному валидному символу — возникает UnicodeDecodeError.
Пример кода
text = "Привет, backend!"
encoded = text.encode("utf-8")
print(encoded) # b'\xd0\x9f\xd1\x80\xd0\xb8...'
decoded = encoded.decode("utf-8")
print(decoded == text) # True
try:
encoded.decode("ascii") # кириллица не кодируется в ASCII
except UnicodeDecodeError as e:
print("Ошибка декодирования:", e)Что использовать на практике
- Практика. По умолчанию большинство Python API (файлы, HTTP-библиотеки) используют UTF-8, и явно указывать кодировку стоит всегда, когда источник данных может быть в другой кодировке (легаси-файлы, экспорт из Excel на Windows в cp1251). Для чтения файла в бинарном режиме используют
open(path, "rb"), в текстовом —open(path, "r", encoding="utf-8").
Подводные камни
- Риски. Смешивают str и bytes в одной операции (
"text" + b"bytes") и получаютTypeError. Также читают файл без указания encoding, из-за чего на разных ОС (разные encoding по умолчанию) один и тот же файл читается по-разному или падает с ошибкой декодирования.