Melhor maneira de converter string em bytes no Python 3?

861

Parece haver duas maneiras diferentes de converter uma seqüência de caracteres em bytes, como visto nas respostas para TypeError: 'str' não suporta a interface do buffer

Qual desses métodos seria melhor ou mais pitônico? Ou é apenas uma questão de preferência pessoal?

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

b = mystring.encode('utf-8')
Mark Ransom
fonte
42
O uso de codificação / decodificação é mais comum e talvez mais claro.
Lennart Regebro
11
@LennartRegebro eu demitir. Mesmo que seja mais comum, lendo "bytes ()" eu sei o que está fazendo, enquanto encode () não me faz sentir que está codificando para bytes.
M3nda
2
@ erm3nda que é uma boa razão para usá-lo até que ele se sente assim, então você está um passo mais perto de Unicode zen.
Lennart Regebro
4
@LennartRegebro Eu me sinto bem o suficiente para usar bytes(item, "utf8"), já que o explícito é melhor do que implícito, então ... o str.encode( )padrão é o de bytes silenciosos, tornando-o mais Unicode-zen, mas menos Zen-Explícito. Também "comum" não é um termo que eu gosto de seguir. Além disso, bytes(item, "utf8")é mais parecido com os str()e b"string"notações. Peço desculpas se eu sou tão noob para entender suas razões. Obrigado.
M3nda
4
@ erm3nda Se você ler a resposta aceita, poderá ver que encode()não chama bytes(), é o contrário. Claro que isso não é imediatamente óbvio, e foi por isso que fiz a pergunta.
Mark Ransom

Respostas:

571

Se você olhar para os documentos bytes, ele indica bytearray:

bytearray ([origem [, codificação [, erros]]])

Retorne uma nova matriz de bytes. O tipo bytearray é uma sequência mutável de números inteiros no intervalo 0 <= x <256. Possui a maioria dos métodos usuais de sequências mutáveis, descritos em Tipos de sequências mutáveis, bem como a maioria dos métodos que o tipo de bytes possui, consulte Bytes e Métodos de matriz de bytes.

O parâmetro de origem opcional pode ser usado para inicializar a matriz de algumas maneiras diferentes:

Se for uma sequência, você também deve fornecer os parâmetros de codificação (e, opcionalmente, erros); bytearray () converte a string em bytes usando str.encode ().

Se for um número inteiro, a matriz terá esse tamanho e será inicializada com bytes nulos.

Se for um objeto em conformidade com a interface do buffer, um buffer somente leitura do objeto será usado para inicializar a matriz de bytes.

Se for iterável, deve ser iterável de números inteiros no intervalo 0 <= x <256, que são usados ​​como o conteúdo inicial da matriz.

Sem um argumento, uma matriz de tamanho 0 é criada.

Portanto, bytespode fazer muito mais do que apenas codificar uma string. É Pythonic que permite chamar o construtor com qualquer tipo de parâmetro de origem que faça sentido.

Para codificar uma string, acho que some_string.encode(encoding)é mais pitonico do que usar o construtor, porque é o mais documentado - "pegue essa string e codifique-a com essa codificação" é mais claro que bytes(some_string, encoding)- não há verbo explícito quando você usa o construtor.

Edit: Eu verifiquei a fonte Python. Se você passar uma string unicode para bytesusar o CPython, ela chamará PyUnicode_AsEncodedString , que é a implementação de encode; então você está pulando um nível de indireção se você se chamar encode.

Veja também o comentário de Serdalis - unicode_string.encode(encoding)também é mais pitônico, porque é inverso byte_string.decode(encoding)e simétrico.

agf
fonte
73
+1 por ter um bom argumento e citações dos documentos python. Também unicode_string.encode(encoding)combina muito bem com bytearray.decode(encoding)quando você deseja que sua string volte.
Serdalis 28/09
6
bytearrayé usado quando você precisa de um objeto mutável. Você não precisa dele para simples strbytesconversões.
28511 hamstergene
8
@EugeneHomyakov Isso não tem nada a ver, bytearrayexceto que os documentos bytesnão dão detalhes, eles apenas dizem "esta é uma versão imutável de bytearray", então eu tenho que citar a partir daí.
AGF
1
Apenas uma nota de advertência do Python em poucas palavras sobre bytes: Evite usar o tipo de bytes como uma função com um argumento inteiro. Na v2, isso retorna o número inteiro convertido em uma string (byte) porque bytes é um alias para str, enquanto na v3 ele retorna uma cadeia de bytes contendo o número especificado de caracteres nulos. Portanto, por exemplo, em vez da expressão v3 bytes (6), use o equivalente b '\ x00' * 6, que funciona perfeitamente da mesma maneira em cada versão.
precisa saber é o seguinte
2
Apenas uma nota, que, se você está tentando converter dados binários para uma cadeia, você provavelmente vai precisar usar algo como byte_string.decode('latin-1')como utf-8não cobre toda a faixa de 0x00 a 0xFF (0-255), confira os python docs para mais informações.
iggy12345
349

É mais fácil do que se pensa:

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
fonte
37
Ele sabe como fazê-lo, ele está apenas perguntando qual caminho é melhor. Por favor, leia novamente a pergunta.
AGF
30
FYI: str.decode (bytes) não funcionou para mim (Python 3.3.3 disse "tipo de objeto 'str' tem nenhum atributo 'decodificação'") I bytes.decode utilizado () em vez
Mike
6
@ Mike: use obj.method()sintaxe em vez de cls.method(obj)sintaxe, ou seja, use bytestring = unicode_text.encode(encoding)e unicode_text = bytestring.decode(encoding).
JFS
2
... ou seja, você está fazendo desnecessariamente um método não ligado, em seguida, chamando-o de passar o selfcomo o primeiro argumento
Antti Haapala
2
@KolobCanyon A pergunta já mostra o caminho certo para fazê-lo - chame encodecomo um método vinculado na string. Esta resposta sugere que você deve chamar o método não acoplado e passar a string. Essa é a única informação nova na resposta e está errada.
abarnert
144

A melhor maneira absolutamente não é dos 2, mas do 3º. O primeiro parâmetro para o padrão desde o Python 3.0. Assim, a melhor maneira éencode 'utf-8'

b = mystring.encode()

Isso também será mais rápido, porque o argumento padrão não resulta na sequência "utf-8"do código C, mas NULLé muito mais rápida de verificar!

Aqui estão alguns horários:

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

Apesar do aviso, os tempos eram muito estáveis ​​após repetidas corridas - o desvio era de apenas 2%.


O uso encode()sem argumento não é compatível com Python 2, pois no Python 2 a codificação de caracteres padrão é 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
fonte
2
Há apenas uma diferença considerável aqui porque (a) a string é pura ASCII, o que significa que o armazenamento interno já é a versão UTF-8, portanto, procurar o codec é quase o único custo envolvido e (b) a string é pequena , portanto, mesmo se você tivesse que codificar, não faria muita diferença. Experimente com, digamos '\u00012345'*10000,. Ambos levam 28.8us no meu laptop; presumivelmente, os 50ns extras são perdidos no erro de arredondamento. Claro que este é um exemplo bastante extremo - mas 'abc'é igualmente extremo na direção oposta.
abarnert
@abarnert true, mas mesmo assim, não há razão para passar o argumento como uma string.
Antti Haapala
De acordo com isso, os argumentos padrão são sempre "absolutamente a melhor maneira" de fazer as coisas, certo? Esse tipo de análise de velocidade pareceria um provável exagero se tratasse da discussão do código C. Em uma linguagem interpretada, isso me deixa sem palavras.
hmijail lamenta os demitidos