Ich habe Code, der erwartet, str
aber den Fall der Übergabe bytes
folgendermaßen behandelt:
if isinstance(data, bytes):
data = data.decode()
Leider funktioniert dies bei nicht bytearray
. Gibt es eine allgemeinere Möglichkeit zu testen, ob ein Objekt entweder bytes
oder ist bytearray
, oder sollte ich nur nach beiden suchen? Ist hasattr('decode')
so schlimm, wie ich es mir vorstelle?
python
python-3.x
A. Wilcox
quelle
quelle
str
. Ein anderer Code sollte bei der Eingabe so schnell wie möglich von Bytes in Unicode konvertiert werden. (2) "bytes-like" hat eine besondere Bedeutung in Python (Objekte, die das Pufferprotokoll unterstützen (nur C))Antworten:
Es gibt einige Ansätze, die Sie hier verwenden können.
Ente tippen
Da Python vom Typ Ente ist , können Sie einfach Folgendes tun (was normalerweise der vorgeschlagene Weg ist):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
Sie könnten es jedoch so verwenden,
hasattr
wie Sie es beschreiben, und es wäre wahrscheinlich in Ordnung. Dies setzt natürlich voraus, dass die.decode()
Methode für das angegebene Objekt eine Zeichenfolge zurückgibt und keine bösen Nebenwirkungen hat.Ich persönlich empfehle entweder die Ausnahme oder die
hasattr
Methode, aber was auch immer Sie verwenden, liegt bei Ihnen.Verwenden Sie str ()
Dieser Ansatz ist ungewöhnlich, aber möglich:
data = str(data, "utf-8")
Andere Codierungen sind zulässig, genau wie bei den Pufferprotokollen
.decode()
. Sie können auch einen dritten Parameter übergeben, um die Fehlerbehandlung festzulegen.Generische Single-Dispatch-Funktionen (Python 3.4+)
Python 3.4 und höher enthält über functools.singledispatch eine raffinierte Funktion, die als generische Single-Dispatch-Funktionen bezeichnet wird . Dies ist etwas ausführlicher, aber auch expliziter:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
Sie können auch spezielle Handler für
bytearray
undbytes
Objekte erstellen , wenn Sie dies wünschen.Achtung : Single-Dispatch-Funktionen funktionieren nur beim ersten Argument! Dies ist eine beabsichtigte Funktion, siehe PEP 433 .
quelle
hasattr
mehr als den Versuch / außer zu verhindern, dass Sie versehentlich einen Fehler in der Dekodierungsfunktion verschlucken, aber +1.Sie können verwenden:
isinstance(data, (bytes, bytearray))
Aufgrund der unterschiedlichen Basisklasse wird hier verwendet.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Überprüfen
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
Jedoch,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Die obigen Codes werden unter Python 2.7 getestet
Leider sind sie unter Python 3.4 gleich ....
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
quelle
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
quelle
type(text) is bytes
wird wahr sein!Dieser Code ist nicht korrekt, es sei denn, Sie wissen etwas, was wir nicht wissen:
if isinstance(data, bytes): data = data.decode()
Sie kennen (anscheinend) die Kodierung von nicht
data
. Sie nehmen an, dass es UTF-8 ist , aber das könnte sehr gut falsch sein. Da Sie die Codierung nicht kennen, haben Sie keinen Text . Sie haben Bytes, die unter der Sonne jede Bedeutung haben könnten.Die gute Nachricht ist, dass die meisten zufälligen Folgen von Bytes nicht für UTF-8 gültig sind. Wenn dies unterbrochen wird, wird es laut unterbrochen (
errors='strict'
ist die Standardeinstellung), anstatt stillschweigend das Falsche zu tun. Die noch bessere Nachricht ist, dass die meisten dieser zufälligen Sequenzen, die zufällig UTF-8 sind, auch gültige ASCII-Sequenzen sind, denen ( fast ) alle zustimmen, wie sie sowieso analysiert werden sollen.Die schlechte Nachricht ist, dass es keinen vernünftigen Weg gibt, dies zu beheben. Es gibt eine Standardmethode zum Bereitstellen von Codierungsinformationen: Verwenden Sie
str
anstelle vonbytes
. Wenn Ihnen ein Code eines Drittanbieters einbytes
oder einbytearray
Objekt ohne weiteren Kontext oder Informationen übergeben hat, besteht die einzig richtige Aktion darin, fehlzuschlagen.Angenommen, Sie kennen die Codierung, können Sie sie
functools.singledispatch
hier verwenden:@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, ...)
Dies funktioniert bei Methoden nicht und
data
muss das erste Argument sein. Wenn diese Einschränkungen bei Ihnen nicht funktionieren, verwenden Sie stattdessen eine der anderen Antworten.quelle
Es kommt darauf an, was Sie lösen möchten. Wenn Sie denselben Code haben möchten, der beide Fälle in eine Zeichenfolge konvertiert, können Sie den Typ einfach zuerst konvertieren
bytes
und dann dekodieren. Auf diese Weise ist es ein Einzeiler:#!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)
Auf diese Weise könnte die Antwort für Sie sein:
data = bytes(data).decode()
Wie auch immer, ich schlage vor,
'utf-8'
explizit in die Dekodierung zu schreiben , wenn Sie nicht wenige Bytes sparen möchten. Der Grund ist, dass das nächste Mal, wenn Sie oder jemand anderes den Quellcode lesen, die Situation offensichtlicher wird.quelle
Hier gibt es zwei Fragen, und die Antworten darauf sind unterschiedlich.
Die erste Frage, der Titel dieses Beitrags, lautet: Wie lässt sich feststellen, ob ein Objekt in Python ein byteähnliches Objekt ist? Dazu gehört eine Reihe von eingebauten Typen (
bytes
,bytearray
,array.array
,memoryview
, andere?) Und eventuell auch Arten benutzerdefiniert. Der beste Weg, nach diesen zu suchen, besteht darin, zu versuchen,memoryview
aus ihnen ein zu erstellen :>>> 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'
Im Hauptteil des ursprünglichen Beitrags klingt die Frage jedoch wie folgt: Wie teste ich, ob ein Objekt decode () unterstützt? @ elizabeth-myers 'obige Antwort auf diese Frage ist großartig. Beachten Sie, dass nicht alle byteähnlichen Objekte decode () unterstützen.
quelle
.release()
Fall die Kontextmanager-Version aufrufen oder verwenden müssen .memoryview
sofort freigegeben und.release()
implizit aufgerufen. Aber ich stimme zu, dass es am besten ist, sich nicht darauf zu verlassen, da nicht alle Python-Implementierungen mit Referenzen gezählt werden.Der Test
if isinstance(data, bytes)
oderif type(data) == bytes
usw. funktioniert nicht in Python 2, wo eine einfache ASCII-Zeichenfolge den Test von! Besteht. Da ich sowohl Python 2 als auch Python 3 verwende, überprüfe ich Folgendes, um dies zu überwinden:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
Es ist ein bisschen hässlich, aber es macht den Job, den die Frage stellt, und es funktioniert immer auf einfachste Weise.
quelle
str
Objekte sindbytes
jedoch:str is bytes
->True
in Python2