Mam kod, który oczekuje, str
ale zajmie się przypadkiem przekazania bytes
w następujący sposób:
if isinstance(data, bytes):
data = data.decode()
Niestety to nie działa w przypadku bytearray
. Czy istnieje bardziej ogólny sposób sprawdzenia, czy obiekt jest albo bytes
albo bytearray
, czy powinienem po prostu sprawdzić oba? Czy jest hasattr('decode')
tak źle, jak się czuję?
python
python-3.x
A. Wilcox
źródło
źródło
str
. Inny kod powinien konwertować z bajtów do Unicode na wejściu tak szybko, jak to możliwe. (2) „podobne do bajtów” ma specjalne znaczenie w Pythonie (obiekty obsługujące protokół bufora (tylko C))Odpowiedzi:
Jest kilka podejść, których możesz tutaj użyć.
Pisanie kaczki
Ponieważ Python jest pisany kaczką , możesz po prostu wykonać następujące czynności (co wydaje się być sposobem zwykle sugerowanym):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
Możesz jednak użyć
hasattr
tego, co opisujesz i prawdopodobnie będzie dobrze. Jest to oczywiście założenie, że.decode()
metoda dla danego obiektu zwraca ciąg znaków i nie ma żadnych nieprzyjemnych skutków ubocznych.Osobiście polecam albo wyjątek, albo
hasattr
metodę, ale cokolwiek użyjesz, zależy od ciebie.Użyj str ()
To podejście jest rzadkie, ale jest możliwe:
data = str(data, "utf-8")
Inne kodowania są dozwolone, podobnie jak w przypadku protokołu bufora
.decode()
. Możesz również przekazać trzeci parametr, aby określić obsługę błędów.Funkcje ogólne z pojedynczą wysyłką (Python 3.4+)
Python 3.4 i nowsze wersje zawierają fajną funkcję zwaną funkcjami ogólnymi o pojedynczej wysyłce, za pośrednictwem functools.singledispatch . Jest to trochę bardziej szczegółowe, ale jest też bardziej wyraźne:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
Jeśli chcesz, możesz również tworzyć specjalne programy obsługi
bytearray
ibytes
obiekty.Uwaga : funkcje pojedynczego wysyłania działają tylko na pierwszym argumencie! Jest to celowa funkcja, zobacz PEP 433 .
źródło
hasattr
bardziej niż try /, z wyjątkiem tego, aby zapobiec przypadkowemu połknięciu jakiegoś błędu w funkcji dekodowania, ale +1.Możesz użyć:
Ze względu na inną klasę bazową zastosowano tutaj.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Sprawdzić
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
Jednak,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Powyższe kody są testowane w Pythonie 2.7
Niestety w Pythonie 3.4 są takie same ...
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
źródło
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
źródło
type(text) is bytes
będzie True!Ten kod nie jest poprawny, chyba że wiesz coś, czego nie wiemy:
if isinstance(data, bytes): data = data.decode()
Nie znasz (wydajesz się) znać kodowania
data
. Zakładasz, że to UTF-8 , ale to może być bardzo złe. Ponieważ nie znasz kodowania, nie masz tekstu . Masz bajty, które pod słońcem mogą mieć jakiekolwiek znaczenie.Dobrą wiadomością jest to, że większość losowych sekwencji bajtów nie jest prawidłowym kodem UTF-8, więc kiedy to się zepsuje, będzie się łamać głośno (
errors='strict'
jest to ustawienie domyślne) zamiast po cichu robić źle. Jeszcze lepszą wiadomością jest to, że większość tych losowych sekwencji, które okazały się być prawidłowymi kodami UTF-8, to również prawidłowe ASCII, które i tak ( prawie ) wszyscy zgadzają się co do sposobu parsowania.Zła wiadomość jest taka, że nie ma rozsądnego sposobu, aby to naprawić. Istnieje standardowy sposób dostarczania informacji o kodowaniu: użyj
str
zamiastbytes
. Jeśli jakiś kod innej firmy przekazał ci obiektbytes
lubbytearray
bez dalszego kontekstu lub informacji, jedyną poprawną czynnością jest niepowodzenie.Zakładając, że znasz kodowanie, możesz użyć
functools.singledispatch
tutaj:@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, ...)
To nie działa na metodach i
data
musi być pierwszym argumentem. Jeśli te ograniczenia nie działają w Twoim przypadku, użyj jednej z pozostałych odpowiedzi.źródło
To zależy od tego, co chcesz rozwiązać. Jeśli chcesz mieć ten sam kod, który konwertuje obie obserwacje na ciąg, możesz po prostu przekonwertować typ na
bytes
pierwszy, a następnie zdekodować. W ten sposób jest to jednowierszowy:#!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)
W ten sposób odpowiedzią dla Ciebie może być:
W każdym razie proponuję napisać
'utf-8'
wprost do dekodowania, jeśli nie zależy ci na oszczędzeniu kilku bajtów. Powodem jest to, że gdy następnym razem ty lub ktoś inny przeczytacie kod źródłowy, sytuacja będzie bardziej widoczna.źródło
Są tu dwa pytania, na które odpowiedzi są różne.
Pierwsze pytanie, tytuł tego posta, brzmi: Jaki jest właściwy sposób określenia, czy obiekt jest obiektem bajtopodobnym w Pythonie? Obejmuje szereg wbudowanych typów (
bytes
,bytearray
,array.array
,memoryview
, inne?) I ewentualnie także typy zdefiniowane przez użytkownika. Najlepszym sposobem, jaki znam, aby to sprawdzić, jest próba utworzeniamemoryview
z nich:>>> 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'
Jednak w treści oryginalnego posta brzmi to tak, jakby zamiast tego pytanie brzmiało: Jak sprawdzić, czy obiekt obsługuje dekodowanie ()? Powyższa odpowiedź @ elizabeth-myers na to pytanie jest świetna. Zauważ, że nie wszystkie obiekty podobne do bajtów obsługują decode ().
źródło
.release()
lub użyć wersji menedżera kontekstu.memoryview
zostałby natychmiast zwolniony i.release()
zostałby wywołany niejawnie. Ale zgadzam się, że najlepiej na tym nie polegać, ponieważ nie wszystkie implementacje Pythona są liczone jako odwołania.Test
if isinstance(data, bytes)
lubif type(data) == bytes
itp. Nie działa w Pythonie 2, gdzie prosty łańcuch ASCII przechodzi test! Ponieważ używam zarówno Pythona 2, jak i Pythona 3, aby temu zaradzić, wykonuję następujące sprawdzenie:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
Jest trochę brzydki, ale spełnia to, o co chodzi w pytaniu, i zawsze działa, w najprostszy sposób.
źródło
str
obiekty sąbytes
jednak:str is bytes
->True
w python2