Wydaje się, że istnieją dwa różne sposoby konwersji ciągu znaków na bajty, jak widać w odpowiedziach na https://stackoverflow.com/questions/5471158/typeerror-str-does-not-support-the-buffer-interface.
Która z tych metod byłaby lepsza lub bardziej Pythonic? A może jest to tylko kwestia osobistych preferencji?
b = bytes(mystring, 'utf-8')
b = mystring.encode('utf-8')
Jeśli spojrzysz na docs dla bytes
, wskazuje on na bytearray
:
bytearray([source[, encoding[, errors]]])
Zwraca nową tablicę bajtów. Typ bytearray jest zmienną sekwencją liczb całkowitych w zakresie 0 <= x <256. Posiada większość typowych metod dla sekwencji mutowalnych, opisanych w Mutable Sequence Types, jak również większość metod, które posiada typ bytes, zobacz Bytes and Byte Array Methods.
Opcjonalny parametr source może być użyty do inicjalizacji tablicy na kilka różnych sposobów:
Jeśli jest to łańcuch znaków, musisz również podać parametry kodowania (i opcjonalnie błędy); bytearray() następnie konwertuje łańcuch na bajty używając str.encode()..
Jeśli jest to liczba całkowita, tablica będzie miała ten rozmiar i zostanie zainicjalizowana bajtami zerowymi.
Jeśli jest to obiekt zgodny z interfejsem bufora, to do inicjalizacji tablicy bajtów użyty zostanie bufor tylko do odczytu obiektu.
Jeśli jest iterowalną, musi być iterowalną liczb całkowitych z zakresu 0 <= x <256, które są używane jako początkowa zawartość tablicy.
Bez argumentu, tworzona jest tablica o rozmiarze 0.
Tak więc bytes
może zrobić znacznie więcej niż tylko zakodować ciąg znaków. To jest Pythonic, że pozwoliłoby ci to wywołać konstruktor z dowolnym typem parametru źródłowego, który ma sens.
Dla kodowania łańcucha, myślę, że some_string.encode(encoding)
jest bardziej Pythonic niż użycie konstruktora, ponieważ jest najbardziej samodokumentujące -- "weź ten łańcuch i zakoduj go tym kodowaniem" jest jaśniejsze niż bytes(some_string, encoding)
-- nie ma wyraźnego czasownika, gdy używasz konstruktora.
Edit: Sprawdziłem źródło Pythona. Jeśli przekażesz ciąg znaków unicode do bytes
używając CPython, wywołuje on PyUnicode_AsEncodedString, który jest implementacją encode
; tak więc jesteś'e po prostu pomijasz poziom pośredni, jeśli sam wywołasz encode
.
Zobacz także komentarz Serdalisa' unicode_string.encode(encoding)
jest także bardziej Pythonic, ponieważ jego odwrotnością jest byte_string.decode(encoding)
, a symetria jest miła.
Jest to łatwiejsze niż się wydaje:
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
Absolutnie najlepszym sposobem nie jest żaden z tych dwóch, ale trzeci. Pierwszy parametr do encode
defaults to* 'utf-8'
od czasu Pythona 3.0. Tak więc najlepszym sposobem jest
b = mystring.encode()
Będzie to również szybsze, ponieważ domyślny argument nie skutkuje ciągiem "utf-8"
w kodzie C, ale NULL
, który jest dużo szybszy do sprawdzenia!
Oto kilka czasów:
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
Pomimo ostrzeżenia czasy były bardzo stabilne po wielokrotnym uruchomieniu - odchylenie wynosiło zaledwie ~2 procent.
Użycie encode()
bez argumentu nie jest zgodne z Pythonem 2, ponieważ w Pythonie 2 domyślnym kodowaniem znaków jest 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)