Tengo un código que espera, str
pero manejará el caso de que se pase bytes
de la siguiente manera:
if isinstance(data, bytes):
data = data.decode()
Desafortunadamente, esto no funciona en el caso de bytearray
. ¿Existe una forma más genérica de probar si un objeto es bytes
o bytearray
, o debería simplemente verificar ambos? ¿Es hasattr('decode')
tan malo como creo que sería?
python
python-3.x
A. Wilcox
fuente
fuente
str
. Algún otro código debería convertir de bytes a Unicode en la entrada tan pronto como sea posible. (2) "tipo bytes" tiene un significado especial en Python (objetos que admiten el protocolo de búfer (solo C))Respuestas:
Hay algunos enfoques que puede utilizar aquí.
Tipeo de pato
Dado que Python se escribe pato , simplemente puede hacer lo siguiente (que parece ser la forma generalmente sugerida):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
hasattr
Sin embargo, podría usarlo como lo describe, y probablemente estaría bien. Esto es, por supuesto, asumiendo que el.decode()
método para el objeto dado devuelve una cadena y no tiene efectos secundarios desagradables.Personalmente recomiendo la excepción o el
hasattr
método, pero lo que sea que use depende de usted.Utilice str ()
Este enfoque es poco común, pero es posible:
data = str(data, "utf-8")
Se permiten otras codificaciones, al igual que con el protocolo de búfer
.decode()
. También puede pasar un tercer parámetro para especificar el manejo de errores.Funciones genéricas de envío único (Python 3.4+)
Python 3.4 y superior incluyen una característica ingeniosa llamada funciones genéricas de envío único, a través de functools.singledispatch . Esto es un poco más detallado, pero también más explícito:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
También puede hacer manipuladores
bytearray
ybytes
objetos especiales si así lo desea.Atención : las funciones de envío único solo funcionan en el primer argumento. Esta es una característica intencionada, consulte PEP 433 .
fuente
hasattr
más que el try / excepto para evitar que te tragues accidentalmente algún error en la función de decodificación, pero +1.Puedes usar:
Debido a la diferente clase base se utiliza aquí.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Verificar
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
Sin embargo,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Los códigos anteriores se prueban en python 2.7
Desafortunadamente, en Python 3.4, son iguales ...
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
fuente
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
fuente
type(text) is bytes
será Verdadero.Este código no es correcto a menos que sepa algo que nosotros no:
if isinstance(data, bytes): data = data.decode()
No (parece) conocer la codificación de
data
. Está asumiendo que es UTF-8 , pero eso podría estar mal. Como no conoce la codificación, no tiene texto . Tienes bytes, que podrían tener cualquier significado bajo el sol.La buena noticia es que la mayoría de las secuencias aleatorias de bytes no son UTF-8 válidas, por lo que cuando esto se rompe, se romperá en voz alta (
errors='strict'
es lo predeterminado) en lugar de hacer lo incorrecto en silencio. La mejor noticia es que la mayoría de esas secuencias aleatorias que resultan ser UTF-8 válidas también son ASCII válidas, que ( casi ) todos están de acuerdo en cómo analizar de todos modos.La mala noticia es que no existe una forma razonable de solucionar este problema. Existe una forma estándar de proporcionar información de codificación: use en
str
lugar debytes
. Si algún código de terceros le entregó un objetobytes
ubytearray
sin más contexto o información, la única acción correcta es fallar.Ahora, suponiendo que conozca la codificación, puede usar
functools.singledispatch
aquí:@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, ...)
Esto no funciona con métodos y
data
debe ser el primer argumento. Si esas restricciones no funcionan para usted, use una de las otras respuestas en su lugar.fuente
Depende de lo que quieras resolver. Si desea tener el mismo código que convierta ambos casos en una cadena, simplemente puede convertir el tipo a
bytes
primero y luego decodificar. De esta manera, es una sola línea:#!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)
De esta manera, la respuesta para ti puede ser:
De todos modos, sugiero escribir
'utf-8'
explícitamente en la decodificación, si no le importa ahorrar algunos bytes. La razón es que la próxima vez que usted u otra persona lea el código fuente, la situación será más evidente.fuente
Aquí hay dos preguntas y las respuestas son diferentes.
La primera pregunta, el título de esta publicación, es ¿Cuál es la forma correcta de determinar si un objeto es un objeto de tipo bytes en Python? Esto incluye una serie de tipos predefinidos (
bytes
,bytearray
,array.array
,memoryview
, otros?) Y posiblemente también los tipos definidos por el usuario. La mejor manera que conozco de verificar estos es intentar crear unmemoryview
fuera de ellos:>>> 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'
Sin embargo, en el cuerpo de la publicación original, parece que la pregunta es ¿Cómo pruebo si un objeto admite decode ()? La respuesta anterior de @ elizabeth-myers a esta pregunta es excelente. Tenga en cuenta que no todos los objetos de tipo bytes admiten decode ().
fuente
.release()
o usar la versión del administrador de contexto.memoryview
liberaría de inmediato y.release()
se llamaría implícitamente. Pero estoy de acuerdo en que es mejor no confiar en eso, ya que no todas las implementaciones de Python cuentan con referencias.La prueba
if isinstance(data, bytes)
oif type(data) == bytes
, etc. no funciona en Python 2, donde una simple cadena ASCII pasa la prueba de! Debido a que utilizo Python 2 y Python 3, para superar esto, hago la siguiente verificación:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
Es un poco feo, pero hace el trabajo que pide la pregunta y siempre funciona, de la manera más simple.
fuente
str
objetos de Python2 sonbytes
:str is bytes
->True
en Python2