Eu tenho um código que espera, str
mas vai lidar com o caso de ser passado bytes
da seguinte maneira:
if isinstance(data, bytes):
data = data.decode()
Infelizmente, isso não funciona no caso de bytearray
. Existe uma maneira mais genérica de testar se um objeto é bytes
ou bytearray
, ou devo apenas verificar os dois? É hasattr('decode')
tão ruim quanto eu sinto que seria?
python
python-3.x
A. Wilcox
fonte
fonte
str
. Algum outro código deve ser convertido de bytes para Unicode na entrada o mais rápido possível. (2) "semelhante a bytes" tem um significado especial em Python (objetos que suportam o protocolo de buffer (apenas C))Respostas:
Existem algumas abordagens que você pode usar aqui.
Digitação de pato
Como o Python é digitado em pato , você poderia simplesmente fazer o seguinte (que parece ser a forma geralmente sugerida):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
Você poderia usar
hasattr
como descreve, no entanto, e provavelmente seria bom. Isso, é claro, assumindo que o.decode()
método para o objeto fornecido retorna uma string e não tem efeitos colaterais desagradáveis.Eu pessoalmente recomendo a exceção ou o
hasattr
método, mas o que quer que você use é com você.Use str ()
Essa abordagem é incomum, mas é possível:
data = str(data, "utf-8")
Outras codificações são permitidas, assim como com o protocolo de buffer
.decode()
. Você também pode passar um terceiro parâmetro para especificar o tratamento de erros.Funções genéricas de despacho único (Python 3.4+)
Python 3.4 e superior incluem um recurso bacana chamado funções genéricas de despacho único, via functools.singledispatch . Isso é um pouco mais detalhado, mas também é mais explícito:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
Você também pode fazer manipuladores especiais para objetos
bytearray
ebytes
, se assim desejar.Cuidado : as funções de despacho único funcionam apenas no primeiro argumento! Este é um recurso intencional, consulte PEP 433 .
fonte
hasattr
mais do que tentar / exceto para evitar que você engula acidentalmente algum bug na função de decodificação, mas +1.Você pode usar:
isinstance(data, (bytes, bytearray))
Devido à classe base diferente é usada aqui.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Checar
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
Contudo,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Os códigos acima são testados em python 2.7
Infelizmente, no python 3.4, eles são os mesmos ....
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
fonte
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
fonte
type(text) is bytes
será True!Este código não está correto a menos que você saiba algo que não sabemos:
if isinstance(data, bytes): data = data.decode()
Você não (parece) saber a codificação de
data
. Você está assumindo que é UTF-8 , mas isso pode muito bem estar errado. Como você não sabe a codificação, não tem texto . Você tem bytes, que podem ter algum significado sob o sol.A boa notícia é que a maioria das sequências aleatórias de bytes não são UTF-8 válidas, portanto, quando isso for interrompido, será interrompido ruidosamente (
errors='strict'
é o padrão) em vez de fazer silenciosamente a coisa errada. A notícia ainda melhor é que a maioria dessas sequências aleatórias que por acaso são UTF-8 válidas também são ASCII válidas, que ( quase ) todos concordam em como analisar de qualquer maneira.A má notícia é que não existe uma maneira razoável de corrigir isso. Existe uma maneira padrão de fornecer informações de codificação: use em
str
vez debytes
. Se algum código de terceiros entregou a você um objetobytes
oubytearray
sem qualquer contexto ou informação adicional, a única ação correta é falhar.Agora, supondo que você conheça a codificação, você pode usar
functools.singledispatch
aqui:@functools.singledispatch def foo(data, other_arguments, ...): raise TypeError('Unknown type: '+repr(type(data))) @foo.register(str) def _(data, other_arguments, ...): # data is a str @foo.register(bytes) @foo.register(bytearray) def _(data, other_arguments, ...): data = data.decode('encoding') # explicit is better than implicit; don't leave the encoding out for UTF-8 return foo(data, other_arguments, ...)
Isso não funciona em métodos e
data
deve ser o primeiro argumento. Se essas restrições não funcionarem para você, use uma das outras respostas.fonte
Depende do que você deseja resolver. Se você deseja que o mesmo código que converta ambos os casos em uma string, você pode simplesmente converter o tipo em
bytes
primeiro e depois decodificar. Dessa forma, é uma linha:#!python3 b1 = b'123456' b2 = bytearray(b'123456') print(type(b1)) print(type(b2)) s1 = bytes(b1).decode('utf-8') s2 = bytes(b2).decode('utf-8') print(s1) print(s2)
Dessa forma, a resposta para você pode ser:
data = bytes(data).decode()
De qualquer forma, sugiro escrever
'utf-8'
explicitamente no decodificador, se você não se importar em poupar alguns bytes. O motivo é que da próxima vez que você ou outra pessoa ler o código-fonte, a situação ficará mais aparente.fonte
Existem duas perguntas aqui, e as respostas a elas são diferentes.
A primeira pergunta, o título desta postagem, é: Qual é a maneira adequada de determinar se um objeto é um objeto semelhante a bytes em Python? Isto inclui uma série de built-in tipos (
bytes
,bytearray
,array.array
,memoryview
, outros?) E possivelmente também tipos definidos pelo usuário. A melhor maneira que conheço de verificar isso é tentar criar um amemoryview
partir deles:>>> memoryview(b"foo") <memory at 0x7f7c43a70888> >>> memoryview(u"foo") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: memoryview: a bytes-like object is required, not 'str'
No corpo da postagem original, porém, parece que a pergunta é: Como faço para testar se um objeto oferece suporte a decode ()? A resposta acima de @elizabeth-myers a esta pergunta é excelente. Observe que nem todos os objetos semelhantes a bytes suportam decode ().
fonte
.release()
ou usar a versão do gerenciador de contexto.memoryview
seria liberado imediatamente e.release()
seria chamado implicitamente. Mas concordo que é melhor não confiar nisso, já que nem todas as implementações Python são contadas por referência.O teste
if isinstance(data, bytes)
ouif type(data) == bytes
etc. não funciona no Python 2, onde uma string ASCII simples passa no teste de! Como uso Python 2 e Python 3, para resolver isso, faço a seguinte verificação:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
É um pouco feio, mas cumpre o que a pergunta pede e sempre funciona, da maneira mais simples.
fonte
str
objetos Python2 sãobytes
:str is bytes
->True
em Python2