Inilah yang biasanya saya lakukan untuk memastikan bahwa inputnya adalah list
/ tuple
- tapi bukan a str
. Karena berkali-kali saya menemukan bug di mana suatu fungsi melewati suatu str
objek secara tidak sengaja, dan fungsi target tidak for x in lst
mengasumsikan bahwa lst
itu sebenarnya a list
atau tuple
.
assert isinstance(lst, (list, tuple))
Pertanyaan saya adalah: adakah cara yang lebih baik untuk mencapai ini?
Jawaban:
Hanya dalam python 2 (bukan python 3):
Sebenarnya apa yang Anda inginkan, jika tidak Anda akan kehilangan banyak hal yang bertindak seperti daftar, tetapi bukan subclass dari
list
atautuple
.sumber
basestring
hilang, dan Anda hanya memeriksaisinstance(lst, str)
.set
, ekspresi generator, iterator. Ada hal-hal eksotis sepertimmap
, hal-halarray
yang kurang eksotis seperti yang bertindak seperti daftar, dan mungkin banyak lagi yang saya sudah lupa.lst
dapat diubah, sedangkan yang asli lakukan (mis. Int akan melewati pemeriksaan ini)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Ingat bahwa dalam Python kami ingin menggunakan "bebek mengetik". Jadi, apa pun yang bertindak seperti daftar dapat diperlakukan sebagai daftar. Jadi, jangan periksa jenis daftar, lihat saja apakah itu bertindak seperti daftar.
Tetapi string juga berfungsi seperti daftar, dan sering kali bukan itu yang kita inginkan. Ada saat-saat ketika itu bahkan menjadi masalah! Jadi, periksa secara eksplisit untuk string, tetapi kemudian gunakan mengetik bebek.
Berikut adalah fungsi yang saya tulis untuk bersenang-senang. Ini adalah versi khusus
repr()
yang mencetak urutan dalam kurung sudut ('<', '>').Ini bersih dan elegan, secara keseluruhan. Tapi apa yang
isinstance()
dilakukan cek di sana? Itu semacam peretasan. Tetapi ini penting.Fungsi ini menyebut dirinya secara rekursif pada apa pun yang bertindak seperti daftar. Jika kami tidak menangani string secara khusus, maka itu akan diperlakukan seperti daftar, dan membagi satu karakter sekaligus. Tetapi kemudian panggilan rekursif akan mencoba memperlakukan setiap karakter sebagai daftar - dan itu akan berhasil! Bahkan string satu karakter berfungsi sebagai daftar! Fungsi akan terus memanggil dirinya secara rekursif sampai stack overflow.
Fungsi seperti ini, yang bergantung pada setiap panggilan rekursif yang memecah pekerjaan yang harus dilakukan, harus memiliki string kasus khusus - karena Anda tidak dapat memecah string di bawah level string satu karakter, dan bahkan satu string -character bertindak seperti daftar.
Catatan: the
try
/except
adalah cara terbersih untuk mengekspresikan niat kami. Tetapi jika kode ini entah bagaimana kritis terhadap waktu, kita mungkin ingin menggantinya dengan semacam tes untuk melihat apakaharg
ada urutan. Daripada menguji jenisnya, kita mungkin harus menguji perilaku. Jika memiliki.strip()
metode, itu adalah string, jadi jangan menganggapnya sebagai urutan; jika tidak, jika itu dapat diindeks atau diubah, itu adalah urutan:EDIT: Saya awalnya menulis di atas dengan cek untuk
__getslice__()
tetapi saya perhatikan bahwa dalamcollections
dokumentasi modul, metode yang menarik adalah__getitem__()
; ini masuk akal, itulah cara Anda mengindeks objek. Tampaknya lebih mendasar daripada__getslice__()
itu saya mengubah di atas.sumber
srepr
adalah ide yang sangat menarik. Tetapi saya memiliki pendapat yang berbeda dari Anda tentang perlunya kasus khususstr
. Ya,str
sejauh ini merupakan hal yang paling jelas dan umum yang akan menyebabkan rekursi tak terbatassrepr
. Tapi saya bisa dengan mudah membayangkan iterables yang ditentukan pengguna yang berperilaku dengan cara yang sama (dengan atau tanpa alasan yang baik). Daripada kasus khususstr
, kita harus mengakui bahwa pendekatan ini dapat mengalami rekursi tak terbatas, dan menyetujui beberapa cara untuk menghadapinya. Saya akan mengirim saran saya dalam jawaban.srepr()
tidak apa-apa. Kita membutuhkan fungsi rekursif untuk menangani hal-hal seperti daftar yang bersarang di dalam daftar lain; tetapi untuk string kami lebih suka mencetaknya"foo"
daripada sebagai<'f', 'o', 'o'>
. Jadi pemeriksaan eksplisit untuk string masuk akal di sini. Juga, sebenarnya tidak ada contoh tipe data lain di mana iterasi selalu mengembalikan iterable dan rekursi akan selalu menyebabkan stack overflow, jadi kita tidak perlu properti khusus untuk menguji hal ini ("Praktisitas mengalahkan kemurnian").__iter__()
metode dalam Python 3, tetapi tidak dalam Python 2. Anda tidak memiliki tanda kurung diis_sequence()
dalamnya, seharusnya berbunyi:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
sumber
if isinstance( H, (list, tuple) ): ...
lebih pendek dan lebih jelas.if type(H) in [list, tuple]:
Untuk Python 3:
Untuk Python 2:
sumber
collections.Sequence
tetapi saya mengujinya dan saya melihat mereka melakukannya. Begitu jugaxrange
. Bahkan lebih baik, tes ini tidak termasukdict
, yang memiliki keduanya__getitem__
dan__iter__
.inspect.getmro(list)
tidak termasukSequence
? Bagaimana kita mencari tahu apa yang bisa kita lakukanisinstance
ketikagetmro
tidak menunjukkan semuanya?Sequence
adalah kelas abstrak.Python dengan rasa PHP:
sumber
__getitem__
. Juga namanya menyesatkan, karena ada juga modul array. Dan var juga bisa menjadi numpy.ndarray atau jenis lainnya, yang memiliki__getitem__
. Lihat stackoverflow.com/a/1835259/470560 untuk jawaban yang benar.str
juga__getitem__
karena itu cek Anda tidak mengecualikanstr
__getitem__
adalah saran yang buruk di sini.Secara umum, fakta bahwa suatu fungsi yang berulang pada suatu objek bekerja pada string dan juga tupel dan daftar adalah lebih banyak fitur daripada bug. Anda tentu bisa menggunakan
isinstance
atau mengetik bebek untuk memeriksa argumen, tetapi mengapa Anda harus?Itu terdengar seperti pertanyaan retoris, tetapi ternyata tidak. Jawaban untuk "mengapa saya harus memeriksa tipe argumen?" mungkin akan menyarankan solusi untuk masalah nyata, bukan masalah yang dirasakan. Mengapa bug ketika string dilewatkan ke fungsi? Juga: jika itu bug ketika string dilewatkan ke fungsi ini, apakah itu juga bug jika beberapa non-list / tuple iterable diteruskan ke sana? Mengapa atau mengapa tidak?
Saya pikir jawaban yang paling umum untuk pertanyaan tersebut adalah pengembang yang menulis
f("abc")
mengharapkan fungsi berperilaku seolah-olah mereka menulisf(["abc"])
. Mungkin ada keadaan di mana lebih masuk akal untuk melindungi pengembang dari diri mereka sendiri daripada mendukung kasus penggunaan pengulangan di seluruh karakter dalam sebuah string. Tapi saya harus berpikir panjang dan keras dulu.sumber
Coba ini untuk keterbacaan dan praktik terbaik:
Python2
Python3
Semoga ini bisa membantu.
sumber
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
, danisinstance("asd", List)
=False
The
str
objek tidak memiliki__iter__
atributjadi kamu bisa melakukan cek
dan ini juga akan menghasilkan objek yang bagus
AssertionError
untuk objek yang tidak dapat diubah.Sunting: Seperti yang disebutkan Tim dalam komentar, ini hanya akan bekerja di python 2.x, bukan 3.x
sumber
hasattr('','__iter__')
kembaliTrue
. Dan tentu saja itu masuk akal karena Anda dapat beralih menggunakan string.Ini tidak dimaksudkan untuk langsung menjawab OP, tetapi saya ingin berbagi beberapa ide terkait.
Saya sangat tertarik dengan jawaban @steveha di atas, yang sepertinya memberikan contoh di mana ketikan bebek tampaknya pecah. Namun, setelah dipikir-pikir, teladannya menunjukkan bahwa mengetik bebek sulit dilakukan, tetapi tidak menyarankan bahwa
str
layak untuk penanganan khusus.Setelah semua, non-
str
tipe (misalnya, tipe yang ditentukan pengguna yang mempertahankan beberapa struktur rekursif yang rumit) dapat menyebabkansrepr
fungsi @steveha menyebabkan rekursi tak terbatas. Meskipun ini memang agak tidak mungkin, kami tidak bisa mengabaikan kemungkinan ini. Oleh karena itu, daripada khusus casingstr
disrepr
, kita harus menjelaskan apa yang ingin kitasrepr
lakukan ketika hasil rekursi tak terbatas.Tampaknya satu pendekatan yang masuk akal adalah dengan mematahkan rekursi pada
srepr
saat itulist(arg) == [arg]
. Ini, pada kenyataannya, akan sepenuhnya menyelesaikan masalah denganstr
, tanpa adaisinstance
.Namun, struktur rekursif yang sangat rumit dapat menyebabkan loop tak terbatas di mana
list(arg) == [arg]
tidak pernah terjadi. Karena itu, walaupun pemeriksaan di atas bermanfaat, itu tidak cukup. Kami membutuhkan sesuatu seperti batas keras pada kedalaman rekursi.Maksud saya adalah bahwa jika Anda berencana untuk menangani jenis argumen sewenang-wenang, penanganan
str
melalui pengetikan bebek jauh, jauh lebih mudah daripada menangani jenis yang lebih umum yang mungkin (secara teoritis) Anda temui. Jadi, jika Anda merasa perlu untuk mengecualikanstr
instance, Anda harus menuntut bahwa argumen adalah instance dari salah satu dari beberapa jenis yang Anda tentukan secara eksplisit.sumber
str
,, yang ditangani oleh kode kasus khusus. Tetapi mungkin harus ada properti standar baru yang dapat diperiksa kode,.__atomic__
katakanlah, yang menandakan bahwa sesuatu tidak dapat dipecah lebih jauh. Mungkin sudah terlambat untuk menambahkan fungsi builtin lainatomic()
ke Python, tapi mungkin kita bisa menambahkanfrom collections import atomic
atau sesuatu.Saya menemukan fungsi bernama is_afterence dalam tensorflow .
Dan saya telah memverifikasi bahwa itu memenuhi kebutuhan Anda.
sumber
Saya melakukan ini di testcases saya.
Tidak diuji pada generator, saya pikir Anda dibiarkan pada 'hasil' berikutnya jika dilewatkan dalam generator, yang dapat mengacaukan segalanya di hilir. Tapi sekali lagi, ini adalah 'yang tak terhitung'
sumber
Dengan cara "mengetik bebek", bagaimana
atau
masing-masing. Ini menghindari hal-hal
isinstance
/hasattr
introspeksi.Anda juga dapat memeriksa sebaliknya:
Semua varian sebenarnya tidak mengubah konten variabel, tetapi menyiratkan penugasan kembali. Saya tidak yakin apakah ini mungkin tidak diinginkan dalam beberapa keadaan.
Menariknya, dengan penugasan "di tempat"
+=
tidak ada yangTypeError
akan dinaikkan dalam hal apa pun jikalst
merupakan daftar (bukan tupel ). Itu sebabnya tugas dilakukan dengan cara ini. Mungkin seseorang bisa menjelaskan mengapa itu terjadi.sumber
cara paling sederhana ... menggunakan
any
danisinstance
sumber
Versi lain dari mengetik bebek untuk membantu membedakan objek seperti string dari objek seperti urutan lainnya.
Representasi string dari objek mirip string adalah string itu sendiri, sehingga Anda dapat memeriksa apakah Anda mendapatkan objek yang sama kembali dari
str
konstruktor:Ini harus bekerja untuk semua objek yang kompatibel dengan
str
dan untuk semua jenis objek yang dapat diubah.sumber
Python 3 memiliki ini:
Jadi untuk memeriksa Daftar dan Tuple, itu akan menjadi:
sumber
sumber
Lakukan saja ini
sumber
dalam python> 3.6
sumber
str
, bukan string. Cobaisinstance('my_string', collections.abc.Container)
dan Anda akan melihat bahwa itu akan kembaliTrue
. Ini karenaabc.Container
persediaan__contains__
metode, dan string memilikinya, tentu saja.Saya cenderung melakukan ini (jika saya benar-benar harus):
sumber
some_var
adalah turunan dari kelas yang merupakan subkelas darilist()
? Kode Anda tidak akan tahu apa yang harus dilakukan dengan kode itu, meskipun itu akan bekerja dengan sempurna di bawah kode "melakukan sesuatu dengan daftar". Dan Anda jarang perlu peduli tentang perbedaan antara daftar dan tupel. Maaf, -1.type(tuple())
-tuple
akan dilakukan. Sama untuk daftar. Juga, keduanyastr
danunicode
extendedbasestring
, yang merupakan tipe string asli, jadi Anda ingin memeriksanya.type(i) is list
. Juga,type(list())
itulist
sendiri ... Akhirnya, ini tidak bekerja dengan anggun dengan subclass. Jikai
sebenarnya dan OrderedDict, atau semacam namesuple, kode ini akan memperlakukan sebagai string.