Er blijken twee verschillende manieren te zijn om een string naar bytes te converteren, zoals te zien is in de antwoorden op https://stackoverflow.com/questions/5471158/typeerror-str-does-not-support-the-buffer-interface
Welke van deze methodes zou beter of meer Pythonisch zijn? Of is het gewoon een kwestie van persoonlijke voorkeur?
b = bytes(mystring, 'utf-8')
b = mystring.encode('utf-8')
Als je kijkt naar de docs voor bytes
, dan wijst het je naar bytearray
:
bytearray([bron[, encoding[, fouten]])
Geeft een nieuwe array van bytes. Het bytearray-type is een veranderlijke reeks gehele getallen in het bereik 0 <= x < 256. Het heeft de meeste van de gebruikelijke methoden van muteerbare reeksen, beschreven in Muteerbare reeks-typen, alsook de meeste methoden die het bytes-type heeft, zie Bytes en Byte Array methoden.
De optionele bronparameter kan worden gebruikt om de array op een paar verschillende manieren te initialiseren:
Als het een string is, moet je ook de encoding (en optioneel, errors) parameters meegeven; bytearray() converteert dan de string naar bytes met behulp van str.encode().
Als het een integer is, zal de array die grootte hebben en worden geïnitialiseerd met null bytes.
Als het een object is dat voldoet aan de buffer-interface, zal een alleen-lezen buffer van het object worden gebruikt om de bytes-array te initialiseren.
Als het een iterable is, moet het een iterable zijn van gehele getallen in het bereik 0 <= x < 256, die worden gebruikt als de initiële inhoud van de array.
Zonder een argument wordt een array van grootte 0 aangemaakt.
bytes
kan dus veel meer dan alleen een string coderen. Het's Pythonisch dat je de constructor zou kunnen aanroepen met elk type bronparameter dat zinvol is.
Voor het coderen van een string, denk ik dat some_string.encode(encoding)
meer Pythonisch is dan het gebruik van de constructor, omdat het het meest zelf-documenterend is -- "neem deze string en codeer het met deze encoding" is duidelijker dan bytes(some_string, encoding)
-- er is geen expliciet werkwoord als je de constructor gebruikt.
Edit: Ik heb de Python broncode gecontroleerd. Als je een unicode string doorgeeft aan bytes
met CPython, roept het PyUnicode_AsEncodedString aan, dat is de implementatie van encode
; dus je'slaat gewoon een niveau van indirectie over als je encode
zelf aanroept.
Zie ook Serdalis' commentaar -- unicode_string.encode(encoding)
is ook meer Pythonisch omdat zijn inverse byte_string.decode(encoding)
is en symmetrie is leuk.
Het's makkelijker dan men denkt:
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
De absoluut beste manier is geen van de 2, maar de 3e. De eerste parameter van encode
defaults to 'utf-8'
sinds Python 3.0. Dus de beste manier is
b = mystring.encode()
Dit zal ook sneller zijn, omdat het standaard argument niet resulteert in de string "utf-8"
in de C code, maar NULL
, wat veel sneller te controleren is!
Hier zijn wat timings:
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
Ondanks de waarschuwing waren de tijden zeer stabiel na herhaalde runs - de afwijking bedroeg slechts ~2 procent.
Het gebruik van encode()
zonder een argument is niet compatibel met Python 2, omdat in Python 2 de standaard tekencodering ASCII is.
>>> 'äöä'.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)