Saya memiliki kode yang mengharapkan str
tetapi akan menangani kasus yang diteruskan bytes
dengan cara berikut:
if isinstance(data, bytes):
data = data.decode()
Sayangnya, ini tidak berhasil dalam kasus bytearray
. Apakah ada cara yang lebih umum untuk menguji apakah suatu objek adalah salah satu bytes
atau bytearray
, atau haruskah saya memeriksa keduanya? Apakah hasattr('decode')
seburuk yang saya rasakan?
python
python-3.x
A. Wilcox
sumber
sumber
str
. Beberapa kode lain harus dikonversi dari byte ke Unicode pada input secepat mungkin. (2) "seperti byte" memiliki arti khusus dalam Python (objek yang mendukung protokol buffer (hanya C))Jawaban:
Ada beberapa pendekatan yang dapat Anda gunakan di sini.
Mengetik bebek
Karena Python adalah tipe bebek , Anda dapat melakukan hal berikut (yang sepertinya biasanya disarankan):
try: data = data.decode() except (UnicodeDecodeError, AttributeError): pass
Anda bisa menggunakan
hasattr
apa yang Anda gambarkan, bagaimanapun, dan itu mungkin akan baik-baik saja. Ini, tentu saja, dengan asumsi.decode()
metode untuk objek yang diberikan mengembalikan string, dan tidak memiliki efek samping yang buruk.Saya pribadi merekomendasikan pengecualian atau
hasattr
metode, tetapi apa pun yang Anda gunakan terserah Anda.Gunakan str ()
Pendekatan ini tidak umum, tetapi mungkin:
data = str(data, "utf-8")
Pengkodean lain diizinkan, seperti halnya dengan protokol buffer
.decode()
. Anda juga dapat memberikan parameter ketiga untuk menentukan penanganan kesalahan.Fungsi umum pengiriman tunggal (Python 3.4+)
Python 3.4 dan yang lebih baru menyertakan fitur bagus yang disebut fungsi generik pengiriman tunggal, melalui functools.singledispatch . Ini sedikit lebih bertele-tele, tetapi juga lebih eksplisit:
def func(data): # This is the generic implementation data = data.decode() ... @func.register(str) def _(data): # data will already be a string ...
Anda juga dapat membuat penangan khusus untuk
bytearray
danbytes
objek jika Anda mau.Hati-hati : fungsi pengiriman tunggal hanya berfungsi pada argumen pertama! Ini adalah fitur yang disengaja, lihat PEP 433 .
sumber
hasattr
lebih suka daripada mencoba / kecuali untuk mencegah Anda secara tidak sengaja menelan beberapa bug dalam fungsi decode, tetapi +1.Kamu bisa memakai:
Karena kelas dasar yang berbeda digunakan di sini.
>>> bytes.__base__ <type 'basestring'> >>> bytearray.__base__ <type 'object'>
Untuk memeriksa
bytes
>>> by = bytes() >>> isinstance(by, basestring) True
Namun,
>>> buf = bytearray() >>> isinstance(buf, basestring) False
Kode di atas diuji di bawah python 2.7
Sayangnya, di bawah python 3.4, keduanya sama ....
>>> bytes.__base__ <class 'object'> >>> bytearray.__base__ <class 'object'>
sumber
>>> content = b"hello" >>> text = "hello" >>> type(content) <class 'bytes'> >>> type(text) <class 'str'> >>> type(text) is str True >>> type(content) is bytes True
sumber
type(text) is bytes
akan Benar!Kode ini tidak benar kecuali Anda mengetahui sesuatu yang tidak kami ketahui:
if isinstance(data, bytes): data = data.decode()
Anda tidak (tampaknya) mengetahui pengkodean
data
. Anda berasumsi bahwa itu UTF-8 , tetapi itu bisa jadi salah. Karena Anda tidak tahu pengkodeannya, Anda tidak memiliki teks . Anda memiliki byte, yang bisa memiliki arti apa pun di bawah matahari.Kabar baiknya adalah bahwa sebagian besar urutan byte yang acak bukanlah UTF-8 yang valid, jadi ketika ini rusak, itu akan rusak dengan keras (
errors='strict'
adalah default) daripada diam-diam melakukan hal yang salah. Berita yang lebih baik lagi adalah bahwa sebagian besar urutan acak yang kebetulan merupakan UTF-8 yang valid juga merupakan ASCII yang valid, yang ( hampir ) semua orang setuju tentang cara mengurai.Kabar buruknya adalah tidak ada cara yang masuk akal untuk memperbaikinya. Ada cara standar untuk memberikan informasi pengkodean: gunakan
str
sebagai gantibytes
. Jika beberapa kode pihak ketiga memberi Anda objekbytes
ataubytearray
tanpa konteks atau informasi lebih lanjut, satu-satunya tindakan yang benar adalah gagal.Sekarang, dengan asumsi Anda mengetahui pengkodeannya, Anda dapat menggunakan di
functools.singledispatch
sini:@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, ...)
Ini tidak berfungsi pada metode, dan
data
harus menjadi argumen pertama. Jika batasan tersebut tidak berhasil untuk Anda, gunakan salah satu jawaban lainnya.sumber
Itu tergantung apa yang ingin Anda selesaikan. Jika Anda ingin memiliki kode yang sama yang mengonversi kedua kasus menjadi string, Anda cukup mengonversi tipe menjadi
bytes
pertama, lalu mendekodekan. Dengan cara ini, ini adalah satu baris:#!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)
Dengan cara ini, jawaban untuk Anda mungkin:
Bagaimanapun, saya menyarankan untuk menulis
'utf-8'
secara eksplisit ke decode, jika Anda tidak peduli untuk menyisihkan beberapa byte. Alasannya adalah saat Anda atau orang lain membaca kode sumbernya, situasinya akan lebih jelas.sumber
Ada dua pertanyaan di sini, dan jawabannya berbeda.
Pertanyaan pertama, judul posting ini, adalah Apa cara yang tepat untuk menentukan apakah suatu objek adalah objek seperti byte dengan Python? Ini mencakup sejumlah built-in jenis (
bytes
,bytearray
,array.array
,memoryview
, orang lain?) Dan mungkin juga jenis yang ditetapkan pengguna. Cara terbaik yang saya ketahui untuk memeriksanya adalah dengan mencoba membuatnyamemoryview
:>>> 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'
Namun, di badan postingan asli, sepertinya pertanyaannya adalah Bagaimana cara menguji apakah suatu objek mendukung decode ()? Jawaban @ elizabeth-myers di atas untuk pertanyaan ini sangat bagus. Perhatikan bahwa tidak semua objek mirip byte mendukung decode ().
sumber
.release()
, atau menggunakan versi pengelola konteks.memoryview
itu akan segera dibebaskan dan.release()
akan dipanggil secara implisit. Tetapi saya setuju bahwa yang terbaik adalah tidak mengandalkan itu, karena tidak semua implementasi Python dihitung berdasarkan referensi.Tes
if isinstance(data, bytes)
atauif type(data) == bytes
, dll. Tidak berfungsi di Python 2, di mana string ASCII sederhana lolos tes! Karena saya menggunakan Python 2 dan Python 3, untuk mengatasinya, saya melakukan pengecekan berikut:if str(type(data)).find("bytes") != -1: print("It's <bytes>")
Ini sedikit jelek, tapi itu melakukan pekerjaan yang diminta dan selalu berhasil, dengan cara yang paling sederhana.
sumber
str
Objek Python2 adalahbytes
:str is bytes
->True
dengan Python2