Найкращий спосіб перетворити рядок у байти в 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використовується , коли вам потрібен змінний об'єкт. Він вам не потрібен для простих str↔ bytesперетворень.
— хамстерген

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.