J'ai du code qui attend str
mais qui traitera le cas d'être passé bytes
de la manière suivante:
if isinstance(data, bytes):
data = data.decode()
Malheureusement, cela ne fonctionne pas dans le cas de bytearray
. Existe-t-il un moyen plus générique de tester si un objet est l'un bytes
ou l' autre bytearray
ou devrais-je simplement vérifier les deux? Est-ce hasattr('decode')
aussi mauvais que je pense que ce serait?
python
python-3.x
A. Wilcox
la source
la source
str
. Un autre code doit convertir des octets en Unicode dès que possible en entrée. (2) "bytes-like" a une signification particulière en Python (objets qui prennent en charge le protocole de tampon (C uniquement))Réponses:
Vous pouvez utiliser ici quelques approches.
Typage de canard
Puisque Python est typé canard , vous pouvez simplement faire ce qui suit (ce qui semble être la manière généralement suggérée):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
Vous pouvez cependant utiliser
hasattr
comme vous le décrivez, et ce serait probablement bien. Ceci, bien sûr, en supposant que la.decode()
méthode de l'objet donné renvoie une chaîne et n'a aucun effet secondaire désagréable.Je recommande personnellement l'exception ou la
hasattr
méthode, mais tout ce que vous utilisez dépend de vous.Utilisez str ()
Cette approche est rare, mais est possible:
data = str(data, "utf-8")
D'autres encodages sont autorisés, tout comme avec le protocole tampon
.decode()
. Vous pouvez également passer un troisième paramètre pour spécifier la gestion des erreurs.Fonctions génériques à répartition unique (Python 3.4+)
Python 3.4 et les versions ultérieures incluent une fonctionnalité intéressante appelée fonctions génériques à distribution unique, via functools.singledispatch . C'est un peu plus détaillé, mais c'est aussi plus explicite:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
Vous pouvez également créer des gestionnaires spéciaux pour les objets
bytearray
etbytes
si vous le souhaitez.Attention : les fonctions d'envoi unique ne fonctionnent que sur le premier argument! C'est une caractéristique intentionnelle, voir PEP 433 .
la source
hasattr
plus que le try / sauf pour vous empêcher d'avaler accidentellement un bogue dans la fonction de décodage, mais +1.Vous pouvez utiliser:
En raison de la classe de base différente est utilisée ici.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Vérifier
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
cependant,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Les codes ci-dessus sont testés sous python 2.7
Malheureusement, sous python 3.4, ce sont les mêmes ...
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
la source
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
la source
type(text) is bytes
sera vrai!Ce code n'est pas correct sauf si vous savez quelque chose que nous ignorons:
if isinstance(data, bytes): data = data.decode()
Vous ne connaissez pas (semblez-vous) le codage de
data
. Vous supposez que c'est UTF-8 , mais cela pourrait très bien être faux. Puisque vous ne connaissez pas l'encodage, vous n'avez pas de texte . Vous avez des octets, qui pourraient avoir n'importe quelle signification sous le soleil.La bonne nouvelle est que la plupart des séquences aléatoires d'octets ne sont pas UTF-8 valides, donc quand cela se brise, il se cassera bruyamment (
errors='strict'
c'est la valeur par défaut) au lieu de faire la mauvaise chose en silence. La meilleure nouvelle est que la plupart de ces séquences aléatoires qui se trouvent être du UTF-8 valide sont également de l'ASCII valide, que ( presque ) tout le monde s'accorde de toute façon sur la façon d'analyser.La mauvaise nouvelle est qu'il n'existe aucun moyen raisonnable de résoudre ce problème. Il existe une manière standard de fournir des informations d'encodage: utilisez
str
plutôt quebytes
. Si un code tiers vous a remis un objetbytes
oubytearray
sans autre contexte ou information, la seule action correcte est d'échouer.Maintenant, en supposant que vous connaissez l'encodage, vous pouvez utiliser
functools.singledispatch
ici:@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, ...)
Cela ne fonctionne pas sur les méthodes et
data
doit être le premier argument. Si ces restrictions ne fonctionnent pas pour vous, utilisez plutôt l'une des autres réponses.la source
Cela dépend de ce que vous voulez résoudre. Si vous souhaitez avoir le même code qui convertit les deux cas en une chaîne, vous pouvez simplement convertir le type en
bytes
premier, puis le décoder. De cette façon, c'est un one-liner:#!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 cette façon, la réponse pour vous peut être:
Quoi qu'il en soit, je suggère d'écrire
'utf-8'
explicitement dans le décodage, si vous ne voulez pas épargner quelques octets. La raison en est que la prochaine fois que vous ou quelqu'un d'autre lirez le code source, la situation sera plus apparente.la source
Il y a deux questions ici, et les réponses sont différentes.
La première question, le titre de cet article, est: Quelle est la bonne façon de déterminer si un objet est un objet de type octets en Python? Cela comprend un certain nombre de types intégrés (
bytes
,bytearray
,array.array
,memoryview
, autres?) Et peut - être aussi types définis par l' utilisateur. Le meilleur moyen que je connaisse pour les vérifier est d'essayer d'en créer unmemoryview
:>>> 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'
Dans le corps du message original, cependant, il semble que la question soit plutôt: Comment puis-je tester si un objet prend en charge decode ()? La réponse ci-dessus de @ elizabeth-myers à cette question est excellente. Notez que tous les objets de type octets ne prennent pas en charge decode ().
la source
.release()
ou utiliser la version du gestionnaire de contexte.memoryview
serait immédiatement libéré et.release()
serait appelé implicitement. Mais je conviens qu'il vaut mieux ne pas se fier à cela, car toutes les implémentations Python ne sont pas comptées par référence.Le test
if isinstance(data, bytes)
orif type(data) == bytes
, etc. ne fonctionne pas dans Python 2, où une simple chaîne ASCII réussit le test de! Parce que j'utilise à la fois Python 2 et Python 3, afin de surmonter cela, je fais la vérification suivante:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
C'est un peu moche, mais ça fait le travail que la question pose et ça marche toujours, de la manière la plus simple.
la source
str
objets Python2 sontbytes
cependant:str is bytes
->True
en Python2