Лучший способ преобразовать строку в байты в Python 3?


862

Похоже, есть два разных способа преобразования строки в байты, как видно из ответов на TypeError: 'str' не поддерживает интерфейс буфера

Какой из этих методов будет лучше или больше Pythonic? Или это просто вопрос личных предпочтений?

b = bytes(mystring, 'utf-8')

b = mystring.encode('utf-8')

42
Использование кодирования / декодирования является более распространенным и, возможно, более понятным.
Леннарт Регебро

11
@LennartRegebro Я увольняюсь. Даже если это более распространено, читая «bytes ()», я знаю, что он делает, в то время как encode () не дает мне почувствовать, что он кодирует в байты.
17

2
@ erm3nda Это хорошая причина, чтобы использовать его, пока он не почувствует себя так, тогда вы на шаг ближе к дзен Unicode.
Леннарт Регебро

4
@LennartRegebro Я чувствую себя достаточно хорошо, чтобы просто использовать bytes(item, "utf8"), поскольку явное лучше, чем неявное, так что ... по str.encode( )умолчанию устанавливается в байтах, что делает вас более Unicode-Zen, но менее Explicit-Zen. Также «общий» - это не термин, которому я хотел бы следовать. Кроме того, bytes(item, "utf8")больше похоже на str(), и b"string"обозначения. Мои извинения, если я так нуб, чтобы понять ваши причины. Спасибо.
m3nda

4
@ erm3nda если вы прочитаете принятый ответ, вы увидите, что encode()он не звонит bytes(), а наоборот. Конечно, это не сразу очевидно, поэтому я и задал вопрос.
Марк Рэнсом

Ответы:


571

Если вы посмотрите на документы для bytes, он указывает на bytearray:

bytearray ([источник [, кодировка [, ошибки]]])

Вернуть новый массив байтов. Тип bytearray - это изменяемая последовательность целых чисел в диапазоне 0 <= x <256. Он имеет большинство обычных методов изменяемых последовательностей, описанных в Mutable Sequence Types, а также большинство методов, которые имеет тип bytes, см. Байты и Методы массива байтов.

Необязательный параметр source можно использовать для инициализации массива несколькими различными способами:

Если это строка, вы также должны указать параметры кодирования (и, возможно, ошибки); Затем bytearray () преобразует строку в байты с помощью str.encode ().

Если это целое число, массив будет иметь такой размер и будет инициализирован нулевыми байтами.

Если это объект, соответствующий интерфейсу буфера, для инициализации массива байтов будет использоваться доступный только для чтения буфер объекта.

Если это итерация, это должна быть итерация целых чисел в диапазоне 0 <= x <256, которые используются в качестве начального содержимого массива.

Без аргумента создается массив размером 0.

Так bytes можно сделать гораздо больше, чем просто кодировать строку. Это Pythonic, что позволит вам вызывать конструктор с любым типом исходного параметра, который имеет смысл.

Для кодирования строки, я думаю, что some_string.encode(encoding) это более Pythonic, чем использование конструктора, потому что это наиболее самодокументируемый - «взять эту строку и кодировать ее с помощью этой кодировки» яснее, чем bytes(some_string, encoding)- нет явного глагола, когда вы используете конструктор.

Изменить: я проверил источник Python. Если вы передаете строку юникода в bytesCPython, он вызывает PyUnicode_AsEncodedString , которая является реализацией encode; так что вы просто пропускаете уровень косвенности, если вы звонитеencode себя.

Кроме того, см. Комментарий Serdalis - unicode_string.encode(encoding)также более Pythonic, потому что его инверсия есть byte_string.decode(encoding)и симметрия хороша.


73
+1 за хороший аргумент и цитаты из документации по питону. Также unicode_string.encode(encoding)хорошо сочетается с тем, bytearray.decode(encoding)когда вы хотите вернуть вашу строку.
Serdalis

6
bytearrayиспользуется, когда вам нужен изменяемый объект. Вам не нужно это для простых strbytesпреобразований.
Хомякен

8
@EugeneHomyakov Это не имеет ничего общего, bytearrayза исключением того, что документы для bytesне дают деталей, они просто говорят, что «это неизменная версия bytearray», поэтому я должен процитировать оттуда.
AGF

1
Просто предупреждение от Python в двух словах о bytes: Избегайте использование типа байт в виде функции с целочисленным аргументом. В v2 это возвращает целое число, преобразованное в (байтовую) строку, потому что байты являются псевдонимом для str, в то время как в v3 это возвращает строку байтов, содержащую данное число нулевых символов. Так, например, вместо байтов выражения v3 (6) используйте эквивалентный b '\ x00' * 6, который одинаково работает одинаково в каждой версии.
holdenweb

2
Просто обратите внимание, что если вы пытаетесь преобразовать двоичные данные в строку, вам, скорее всего, потребуется использовать что-то вроде byte_string.decode('latin-1')as utf-8, не охватывающее весь диапазон от 0x00 до 0xFF (0-255), ознакомьтесь с документацией по Python для: больше информации.
iggy12345

349

Это проще, чем кажется

my_str = "hello world"
my_str_as_bytes = str.encode(my_str)
type(my_str_as_bytes) # ensure it is byte representation
my_decoded_str = my_str_as_bytes.decode()
type(my_decoded_str) # ensure it is string representation

37
Он знает, как это сделать, он просто спрашивает, какой путь лучше. Пожалуйста, перечитайте вопрос.
августа

30
К вашему сведению: str.decode (bytes) у меня не работал (Python 3.3.3 сказал, что «тип объекта str» не имеет атрибута «decode»)) Вместо этого я использовал bytes.decode ()
Майк

6
@Mike: использовать obj.method()синтаксис вместо cls.method(obj)синтаксиса, т.е. использовать bytestring = unicode_text.encode(encoding)и unicode_text = bytestring.decode(encoding).
Jfs

2
... т.е. вы без необходимости создаете несвязанный метод, а затем вызываете его, передавая в selfкачестве первого аргумента
Antti Haapala

2
@KolobCanyon Вопрос уже показывает правильный способ сделать это - вызвать encodeсвязанный метод в строке. Этот ответ предполагает, что вы должны вместо этого вызвать несвязанный метод и передать ему строку. Это единственная новая информация в ответе, и это неправильно.
Абарнерт

144

Абсолютно лучший способ не является ни в 2, но третий. Первый параметр по умолчанию со времен Python 3.0. Таким образом, лучший способencode 'utf-8'

b = mystring.encode()

Это также будет быстрее, потому что аргумент по умолчанию приводит не к строке "utf-8"в коде C, а к тому NULL, что проверять намного быстрее!

Вот некоторые моменты времени:

In [1]: %timeit -r 10 'abc'.encode('utf-8')
The slowest run took 38.07 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 183 ns per loop

In [2]: %timeit -r 10 'abc'.encode()
The slowest run took 27.34 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 137 ns per loop

Несмотря на предупреждение, времена были очень стабильными после повторных прогонов - отклонение составляло всего ~ 2%.


Использование encode()без аргумента несовместимо с Python 2, так как в Python 2 кодировка символов по умолчанию - ASCII .

>>> 'äöä'.encode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

2
Здесь есть только существенная разница, потому что (а) строка является чистой ASCII, что означает, что внутреннее хранилище уже является версией UTF-8, поэтому поиск кодека - это почти единственная стоимость, и (б) строка крошечная , так что даже если бы вам пришлось кодировать, это не имело бы большого значения. Попробуйте, скажем, с '\u00012345'*10000. Оба берут 28.8us на моем ноутбуке; дополнительные 50 нс, вероятно, теряются при ошибке округления. Конечно, это довольно экстремальный пример, но 'abc'такой же экстремальный в противоположном направлении.
abarnert

@abarnert true, но даже тогда нет причин передавать аргумент в виде строки.
Антти Хаапала

В соответствии с этим аргументы по умолчанию всегда "абсолютно лучший способ", верно? Такой анализ скорости был бы вероятным преувеличением, если бы речь шла об обсуждении кода на языке Си. На интерпретированном языке это лишает меня дара речи.
hmijail скорбит по отставке
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.