Найкращий спосіб перетворити рядок у байти в Python 3?


858

Здається, є два різні способи перетворення рядка в байти, як видно з відповідей на TypeError: 'str' не підтримує буферний інтерфейс

Який із цих методів був би кращим чи більш пітонічним? Або це лише питання особистої переваги?

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

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

42
Використовувати кодування / декодування є більш поширеним і, можливо, зрозумілішим.
Леннарт Регебро

11
@LennartRegebro Я відхиляю. Навіть якщо це звичайніше, читаючи "байти ()", я знаю, що це робить, в той час як encode () не дає мені відчути, що це кодування до байтів.
м3нда

2
@ erm3nda Це хороший привід використовувати його, поки він не відчує себе таким чином, тоді ви знаходитесь на крок ближче до Unicode zen.
Леннарт Regebro

3
@LennartRegebro Я відчуваю себе досить добре, щоб просто використовувати bytes(item, "utf8"), так як явний кращий, ніж неявний, тому ... str.encode( )типово типово байти, що робить вас більше Unicode-zen, але менш явний-дзен. Також "загальне" - це не термін, який я люблю дотримуватися. Крім того , bytes(item, "utf8"), більше як str()і b"string"нотацій. Мої вибачення, якщо я настільки нуб, щоб зрозуміти ваші причини. Дякую.
m3nda

4
@ erm3nda, якщо ви прочитаєте прийняту відповідь, можете побачити, що encode()не дзвонить bytes(), це навпаки. Звичайно, це не відразу очевидно, саме тому я задав питання.
Марк Викуп

Відповіді:


570

Якщо ви подивитеся на документи bytes, це вказує на bytearray:

ByteArray ([джерело [, кодування [помилка]]])

Повернути новий масив байтів. Тип байт-масиву - це змінна послідовність цілих чисел у діапазоні 0 <= x <256. Вона має більшість звичайних методів змінних послідовностей, описаних у типах змінних послідовностей, а також більшість методів, якими володіє тип байтів, див. Методи масиву байтів.

Необов’язковий параметр джерела можна використовувати для ініціалізації масиву кількома різними способами:

Якщо це рядок, ви також повинні надати параметри кодування (і необов'язково помилки); ByteArray (), а потім перетворить рядок в байтах, використовуючи str.encode ().

Якщо це ціле число, масив матиме такий розмір і буде ініціалізований нульовими байтами.

Якщо це об'єкт, що відповідає інтерфейсу буфера, для ініціалізації масиву байтів буде використовуватися лише буфер об'єкта для читання.

Якщо він є ітерабельним, він повинен бути ітерабельним цілих чисел у діапазоні 0 <= x <256, які використовуються як початковий вміст масиву.

Без аргументу створюється масив розміром 0.

Так bytesможна зробити набагато більше, ніж просто кодувати рядок. Це Pythonic, що дозволить вам викликати конструктор будь-якого типу вихідного параметра, який має сенс.

Для кодування рядка, я думаю , що some_string.encode(encoding)більше , ніж віщі з допомогою конструктора, так як він є найбільш документовано - «взяти цей рядок і кодувати його з цієї кодуванням» є більш ясним , ніж bytes(some_string, encoding)- немає явного дієслова , коли ви використовуєте конструктор.

Редагувати: я перевірив джерело Python. Якщо передати рядок Юникода з bytesдопомогою CPython, він викликає PyUnicode_AsEncodedString , який є реалізацією encode; тож ви просто пропускаєте рівень непрямості, якщо називаєте encodeсебе.

Також дивіться коментар Сердаліса - unicode_string.encode(encoding)також більш пітонічний, оскільки його зворотна є 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'), utf-8яке не охоплює весь діапазон від 0x00 до 0xFF (0-255), ознайомтеся з документами python для більше інформації.
iggy12345

346

Це простіше, ніж вважається:

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
Він знає, як це зробити, він просто запитує, який спосіб краще. Будь ласка, перечитайте питання.
agf

30
FYI: str.decode (байти) не працював для мене (Python 3.3.3 сказав: "тип об'єкта 'str' не має атрибута" декодування "") Я використовував замість цього bytes.decode ()
Майк

6
@Mike: використовувати obj.method()синтаксис замість cls.method(obj)синтаксису, тобто використовувати bytestring = unicode_text.encode(encoding)і unicode_text = bytestring.decode(encoding).
jfs

2
... тобто ви беззаперечно використовуєте незв'язаний метод, а потім викликаєте його, передаючи selfперший аргумент
Антті Хаапала,

2
@KolobCanyon Питання вже показує правильний спосіб зробити це-виклик в encodeякості пов'язаного методу на струні. Ця відповідь говорить про те, що слід замість цього викликати метод без зв’язку і передавати йому рядок. Це єдина нова інформація у відповідь, і це неправильно.
abarnert

144

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

b = mystring.encode()

Це також буде швидше, оскільки аргумент за замовчуванням призводить не до рядка "utf-8"в коді С, але 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
Тут є лише велика різниця, оскільки (a) рядок є чистою ASCII, тобто внутрішня пам’ять - це вже версія UTF-8, тому пошук кодека - це майже єдина витрата, яка пов'язана з усіма, і (b) рядок крихітна , тож навіть якби вам довелося кодувати, це не мало би великого значення. Спробуйте, скажімо, '\u00012345'*10000. Обидва беруть 28,8US на моєму ноутбуці; додаткові 50 імовірно губляться в помилках округлення. Звичайно, це досить екстремальний приклад, але 'abc'настільки ж екстремальний у зворотному напрямку.
abarnert

@abarnert правда, але навіть тоді, немає ніяких підстав передавати аргумент у вигляді рядка.
Антті Хаапала

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