La meilleure façon de convertir une chaîne en octets en Python 3?

861

Il semble y avoir deux façons différentes de convertir une chaîne en octets, comme le montrent les réponses à TypeError: 'str' ne prend pas en charge l'interface de tampon

Laquelle de ces méthodes serait meilleure ou plus Pythonique? Ou s'agit-il simplement d'une préférence personnelle?

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

b = mystring.encode('utf-8')
Mark Ransom
la source
42
Utiliser l'encodage / décodage est plus courant et peut-être plus clair.
Lennart Regebro
11
@LennartRegebro je rejette. Même si c'est plus courant, lire "bytes ()" je sais ce que ça fait, tandis que encode () ne me fait pas sentir qu'il est encodé en octets.
m3nda
2
@ erm3nda Ce qui est une bonne raison de l' utiliser jusqu'à ce qu'il ne se sent comme ça, alors vous êtes un peu plus de zen Unicode.
Lennart Regebro le
4
@LennartRegebro Je me sens assez bien pour l'utiliser bytes(item, "utf8"), car explicite vaut mieux qu'implicite, donc ... par str.encode( )défaut en octets, ce qui vous rend plus Unicode-zen mais moins Explicit-Zen. "Commun" n'est pas non plus un terme que j'aime suivre. En outre bytes(item, "utf8"),, ressemble davantage à str(), et aux b"string"notations. Mes excuses si je suis tellement noob pour comprendre vos raisons. Je vous remercie.
m3nda
4
@ erm3nda si vous lisez la réponse acceptée, vous pouvez voir que encode()cela n'appelle pas bytes(), c'est l'inverse. Bien sûr, ce n'est pas immédiatement évident, c'est pourquoi j'ai posé la question.
Mark Ransom

Réponses:

571

Si vous regardez les documents bytes, cela vous indique bytearray:

bytearray ([source [, encodage [, erreurs]]])

Renvoie un nouveau tableau d'octets. Le type bytearray est une séquence mutable d'entiers dans la plage 0 <= x <256. Il possède la plupart des méthodes habituelles de séquences mutables, décrites dans Mutable Sequence Types, ainsi que la plupart des méthodes du type bytes, voir Octets et Méthodes de tableau d'octets.

Le paramètre source facultatif peut être utilisé pour initialiser le tableau de différentes manières:

S'il s'agit d'une chaîne, vous devez également donner les paramètres d'encodage (et éventuellement des erreurs); bytearray () convertit ensuite la chaîne en octets à l'aide de str.encode ().

S'il s'agit d'un entier, le tableau aura cette taille et sera initialisé avec des octets nuls.

S'il s'agit d'un objet conforme à l'interface du tampon, un tampon en lecture seule de l'objet sera utilisé pour initialiser le tableau d'octets.

S'il s'agit d'un itérable, il doit s'agir d'un itérable d'entiers compris dans la plage 0 <= x <256, qui sont utilisés comme contenu initial du tableau.

Sans argument, un tableau de taille 0 est créé.

bytesPeut donc faire bien plus que simplement coder une chaîne. C'est Pythonic qu'il vous permettrait d'appeler le constructeur avec n'importe quel type de paramètre source qui a du sens.

Pour encoder une chaîne, je pense que some_string.encode(encoding)c'est plus Pythonic que d'utiliser le constructeur, parce que c'est la plus auto-documentée - "prenez cette chaîne et encodez-la avec cet encodage" est plus clair quebytes(some_string, encoding) - il n'y a pas de verbe explicite lorsque vous utilisez le constructeur.

Edit: j'ai vérifié la source Python. Si vous passez une chaîne unicode à l' bytesutilisation de CPython, il appelle PyUnicode_AsEncodedString , qui est l'implémentation de encode; donc vous sautez juste un niveau d'indirection si vous vous appelez encode.

Voir aussi le commentaire de Serdalis - unicode_string.encode(encoding)est également plus Pythonic car son inverse est byte_string.decode(encoding)et la symétrie est agréable.

agf
la source
73
+1 pour avoir un bon argument et des citations des documents python. Convient également unicode_string.encode(encoding)parfaitement bytearray.decode(encoding)lorsque vous souhaitez récupérer votre chaîne.
Serdalis
6
bytearrayest utilisé lorsque vous avez besoin d'un objet modifiable. Vous n'avez pas besoin pour de simples strbytesconversions.
hamstergene
8
@EugeneHomyakov Cela n'a rien à voir avec le fait bytearrayque les documents pour bytesne donnent pas de détails, ils disent simplement "ceci est une version immuable de bytearray" donc je dois citer à partir de là.
agf
1
Juste une note d'avertissement de Python dans un bref mot sur bytes: Évitez d'utiliser le type d'octets en tant que fonction avec un argument entier. Dans la v2, cela renvoie l'entier converti en une chaîne (octet) car les octets sont un alias pour str, tandis que dans la v3, il renvoie une chaîne d'octets contenant le nombre donné de caractères nuls. Ainsi, par exemple, au lieu des octets d'expression v3 (6), utilisez l'équivalent b '\ x00' * 6, qui fonctionne de manière transparente de la même manière dans chaque version.
holdenweb
2
Juste une note, que si vous essayez de convertir des données binaires en une chaîne, vous aurez très probablement besoin d'utiliser quelque chose comme byte_string.decode('latin-1')as utf-8ne couvre pas toute la plage 0x00 à 0xFF (0-255), consultez les documents python pour Plus d'informations.
iggy12345
349

C'est plus facile qu'on ne le pense:

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
hasanatkazmi
la source
37
Il sait comment faire, il demande simplement quelle est la meilleure voie. Veuillez relire la question.
agf
30
FYI: str.decode (octets) n'a pas fonctionné pour moi (Python 3.3.3 a dit que "type objet 'str' n'a pas d'attribut 'décode'")) J'ai plutôt utilisé bytes.decode ()
Mike
6
@Mike: utilisez la obj.method()syntaxe au lieu de la cls.method(obj)syntaxe c'est-à-dire, utilisez bytestring = unicode_text.encode(encoding)et unicode_text = bytestring.decode(encoding).
jfs
2
... c'est-à-dire que vous créez inutilement une méthode non liée, puis que vous l'appelez en passant le selfcomme premier argument
Antti Haapala
2
@KolobCanyon La question montre déjà la bonne façon de le faire: appelez encodecomme méthode liée sur la chaîne. Cette réponse suggère que vous devez à la place appeler la méthode non liée et lui passer la chaîne. C'est la seule nouvelle information dans la réponse, et c'est faux.
abarnert
144

La absolument meilleur moyen est ni de 2, mais le 3ème. Le premier paramètre par défaut depuis Python 3.0. Ainsi, la meilleure façon estencode 'utf-8'

b = mystring.encode()

Ce sera également plus rapide, car l'argument par défaut n'entraîne pas la chaîne "utf-8"dans le code C, mais NULLce qui est beaucoup plus rapide à vérifier!

Voici quelques horaires:

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

Malgré l'avertissement, les temps étaient très stables après des cycles répétés - l'écart n'était que de ~ 2%.


L'utilisation encode()sans argument n'est pas compatible avec Python 2, car dans Python 2 le codage de caractères par défaut est 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)
Antti Haapala
la source
2
Il n'y a qu'une différence importante ici car (a) la chaîne est en pur ASCII, ce qui signifie que le stockage interne est déjà la version UTF-8, donc la recherche du codec est presque le seul coût impliqué, et (b) la chaîne est minuscule , donc même si vous deviez encoder, cela ne ferait pas beaucoup de différence. Essayez avec, par exemple, '\u00012345'*10000. Les deux prennent 28.8us sur mon ordinateur portable; les 50 ns supplémentaires sont probablement perdues dans l'erreur d'arrondi. Bien sûr, c'est un exemple assez extrême, mais il 'abc'est tout aussi extrême dans la direction opposée.
abarnert
@abarnert true, mais même dans ce cas, il n'y a aucune raison de passer l'argument sous forme de chaîne.
Antti Haapala
Selon cela, les arguments par défaut sont toujours "absolument la meilleure façon" de faire les choses, non? Ce type d'analyse de vitesse serait une exagération probable s'il s'agissait de discuter du code C. Dans une langue interprétée, cela me laisse sans voix.
hmijail pleure les démissionnaires le