__getitem__juga cukup untuk membuat objek dapat diubah
Kos
4
FWIW: iter(myObj)berhasil jika isinstance(myObj, dict), jadi jika Anda melihat sebuah myObjyang bisa menjadi urutan dicts atau tunggal dict, Anda akan berhasil dalam kedua kasus. Kehalusan yang penting jika Anda ingin tahu apa itu urutan dan apa yang tidak. (dalam Python 2)
Ben Mosher
7
__getitem__juga cukup untuk membuat objek dapat diubah ... jika dimulai dengan indeks nol .
Carlos A. Gómez
Jawaban:
28
Saya telah mempelajari masalah ini agak belakangan ini. Berdasarkan kesimpulan saya adalah bahwa saat ini ini adalah pendekatan terbaik:
from collections.abc importIterable# drop `.abc` with Python 2.7 or lowerdef iterable(obj):return isinstance(obj,Iterable)
Hal di atas telah direkomendasikan sebelumnya, tetapi konsensus umum adalah bahwa menggunakan iter()akan lebih baik:
Kami telah menggunakan iter()kode kami juga untuk tujuan ini, tetapi akhir-akhir ini saya mulai semakin terganggu oleh objek yang hanya __getitem__dianggap dapat diubah. Ada alasan valid untuk memiliki objek __getitem__yang tidak dapat diubah dan dengan mereka kode di atas tidak berfungsi dengan baik. Sebagai contoh kehidupan nyata kita bisa menggunakan Faker . Kode di atas melaporkannya iterable tetapi sebenarnya mencoba iterate yang menyebabkannya AttributeError(diuji dengan Faker 4.0.2):
>>>from faker importFaker>>> fake =Faker()>>> iter(fake)# No exception, must be iterable<iterator object at 0x7f1c71db58d0>>>> list(fake)# OoopsTraceback(most recent call last):File"<stdin>", line 1,in<module>File"/home/.../site-packages/faker/proxy.py", line 59,in __getitem__
return self._factory_map[locale.replace('-','_')]AttributeError:'int' object has no attribute 'replace'
Jika kami akan menggunakan insinstance(), kami tidak akan secara tidak sengaja menganggap instance Faker (atau objek lain yang hanya memiliki __getitem__) untuk diubah:
Jawaban sebelumnya berkomentar bahwa menggunakan iter()lebih aman karena cara lama untuk menerapkan iterasi dengan Python didasarkan __getitem__dan isinstance()pendekatan tidak akan mendeteksi itu. Ini mungkin benar dengan versi Python lama, tetapi berdasarkan pengujian yang cukup lengkap saya isinstance()bekerja sangat baik saat ini. Satu-satunya kasus di mana isinstance()tidak bekerja tetapi iter()lakukan adalah dengan UserDictketika menggunakan Python 2. Jika itu relevan, itu mungkin digunakan isinstance(item, (Iterable, UserDict))untuk mendapatkan yang tertutup.
Juga typing.Dictdianggap iterable oleh iter(Dict)tetapi list(Dict)gagal dengan kesalahan TypeError: Parameters to generic types must be types. Got 0.. Seperti yang diharapkan isinstance(Dict, Iterable)mengembalikan false.
Pekka Klärck
1
Saya sampai pada kesimpulan yang sama, tetapi karena berbagai alasan. Penggunaan itermenyebabkan beberapa kode kami yang menggunakan "pre-caching" melambat secara tidak perlu. Jika __iter__kodenya lambat, maka akan memanggil iter... kapan saja Anda hanya ingin melihat apakah ada sesuatu yang dapat diubah.
thorwhalen
842
Memeriksa __iter__karya pada tipe urutan, tetapi akan gagal misalnya string di Python 2 . Saya ingin tahu jawaban yang benar juga, sampai saat itu, di sini ada satu kemungkinan (yang juga bisa digunakan pada string):
from __future__ import print_function
try:
some_object_iterator = iter(some_object)exceptTypeErroras te:print(some_object,'is not iterable')
The iterbuilt-in cek untuk __iter__metode atau dalam kasus string yang __getitem__metode.
Pendekatan pythonic umum lainnya adalah dengan menganggap iterable, kemudian gagal dengan anggun jika tidak bekerja pada objek yang diberikan. Daftar istilah Python:
Gaya pemrograman Pythonic yang menentukan tipe objek dengan memeriksa metode atau atribut signature daripada dengan hubungan eksplisit ke beberapa tipe objek ("Jika terlihat seperti bebek dan dukun seperti bebek , itu pasti bebek .") Dengan menekankan antarmuka daripada tipe tertentu, kode yang dirancang dengan baik meningkatkan fleksibilitasnya dengan memungkinkan substitusi polimorfik. Mengetik bebek menghindari tes menggunakan type () atau isinstance ().Sebaliknya, ia biasanya menggunakan gaya pemrograman EAFP (Lebih Mudah untuk Meminta Maaf daripada Izin).
...
try:
_ =(e for e in my_object)exceptTypeError:print my_object,'is not iterable'
The collectionsmodul menyediakan beberapa kelas dasar abstrak, yang memungkinkan untuk meminta kelas atau contoh jika mereka menyediakan fungsionalitas tertentu, misalnya:
from collections.abc importIterableif isinstance(e,Iterable):# e is iterable
Namun, ini tidak memeriksa untuk kelas yang dapat diulangi __getitem__.
[e for e in my_object]dapat menimbulkan pengecualian karena alasan lain, yaitu my_objecttidak terdefinisi atau kemungkinan bug dalam my_objectimplementasi.
Nick Dandoulakis
37
Suatu string adalah suatu urutan ( isinstance('', Sequence) == True) dan sebagaimana urutan apa pun itu dapat diubah ( isinstance('', Iterable)). Padahal hasattr('', '__iter__') == Falsedan itu mungkin membingungkan.
jfs
82
Jika my_objectsangat besar (misalnya, tak terbatas seperti itertools.count()) pemahaman daftar Anda akan memakan banyak waktu / memori. Lebih baik membuat generator, yang tidak akan pernah mencoba membangun daftar (berpotensi tak terbatas).
Chris Lutz
14
Bagaimana jika some_object melempar TypeError yang disebabkan oleh alasan lain (bug dll) juga? Bagaimana kita bisa tahu dari "NotErable TypeError"?
Shaung
54
Perhatikan bahwa dalam Python 3: hasattr(u"hello", '__iter__')pengembalianTrue
Carlos
572
Mengetik bebek
try:
iterator = iter(theElement)exceptTypeError:# not iterableelse:# iterable# for obj in iterator:# pass
Ketik memeriksa
Gunakan Kelas Basis Abstrak . Mereka membutuhkan setidaknya Python 2.6 dan hanya berfungsi untuk kelas gaya baru.
from collections.abc importIterable# import directly from collections for Python < 3.3if isinstance(theElement,Iterable):# iterableelse:# not iterable
Namun, iter()sedikit lebih dapat diandalkan seperti yang dijelaskan oleh dokumentasi :
Memeriksa isinstance(obj, Iterable)mendeteksi kelas yang terdaftar sebagai Iterable atau yang memiliki __iter__()metode, tetapi tidak mendeteksi kelas yang mengulangi dengan __getitem__()
metode. Satu-satunya cara yang dapat diandalkan untuk menentukan apakah suatu objek dapat diubah adalah dengan menelepon iter(obj).
Dari "Fluent Python" oleh Luciano Ramalho: Pada Python 3.4, cara paling akurat untuk memeriksa apakah suatu objek x dapat diubah adalah dengan memanggil iter (x) dan menangani pengecualian TypeError jika tidak. Ini lebih akurat daripada menggunakan isinstance (x, abc.Iterable), karena iter (x) juga mempertimbangkan metode legacy getitem , sedangkan Iterable ABC tidak.
RdB
Jika Anda berpikir "oh saya hanya akan isinstance(x, (collections.Iterable, collections.Sequence))ganti iter(x)", perhatikan bahwa ini masih tidak akan mendeteksi objek iterable yang mengimplementasikan hanya __getitem__tetapi tidak __len__. Gunakan iter(x)dan tangkap pengecualian.
Dale
Jawaban kedua Anda tidak berhasil. Di PyUNO jika saya lakukan iter(slide1), semuanya berjalan dengan baik, namun isinstance(slide1, Iterable)lemparannya TypeError: issubclass() arg 1 must be a class.
Hi-Angel
@ Hai-Angel terdengar seperti bug di PyUNOPerhatikan bahwa pesan kesalahan Anda mengatakan issubclass()bukan isinstance().
Georg Schölly
2
Memanggil iter () di atas objek bisa menjadi operasi yang mahal (lihat DataLoader di Pytorch, yang mem-fork / memunculkan beberapa proses pada iter ()).
szali
126
Saya ingin menumpahkan sedikit yang sedikit lebih terang pada interaksi iter, __iter__dan __getitem__dan apa yang terjadi di balik tirai. Berbekal pengetahuan itu, Anda akan dapat memahami mengapa yang terbaik yang bisa Anda lakukan adalah
try:
iter(maybe_iterable)print('iteration will probably work')exceptTypeError:print('not iterable')
Saya akan mendaftar fakta terlebih dahulu dan kemudian menindaklanjuti dengan pengingat cepat tentang apa yang terjadi ketika Anda mempekerjakan seorang for loop dalam python, diikuti dengan diskusi untuk menggambarkan fakta-fakta.
Fakta
Anda bisa mendapatkan iterator dari objek apa pun odengan memanggil iter(o)jika setidaknya salah satu dari kondisi berikut ini berlaku:
a) omemiliki __iter__metode yang mengembalikan objek iterator. Iterator adalah objek apa pun dengan metode __iter__dan __next__(Python 2 next:).
b) opunya __getitem__metode.
Memeriksa instance Iterableatau Sequence, atau memeriksa atribut __iter__tidak cukup.
Jika suatu objek ohanya mengimplementasikan __getitem__, tetapi tidak __iter__, iter(o)akan membangun iterator yang mencoba mengambil item dari odengan indeks integer, mulai dari indeks 0. Iterator akan menangkap setiap IndexError(tetapi tidak ada kesalahan lain) yang dinaikkan dan kemudian memunculkan StopIterationdirinya sendiri.
Dalam arti yang paling umum, tidak ada cara untuk memeriksa apakah iterator yang dikembalikan oleh iterwaras selain untuk mencobanya.
Jika suatu objek omengimplementasikan __iter__, iterfungsi akan memastikan bahwa objek yang dikembalikan oleh __iter__iterator. Tidak ada pemeriksaan kewarasan jika suatu objek hanya mengimplementasikan __getitem__.
__iter__menang. Jika suatu objek omengimplementasikan keduanya __iter__dan __getitem__, iter(o)akan memanggil __iter__.
Jika Anda ingin membuat objek Anda sendiri dapat diubah, selalu terapkan __iter__metode ini.
for loop
Untuk mengikuti, Anda perlu pemahaman tentang apa yang terjadi ketika Anda menggunakan forloop dengan Python. Jangan ragu untuk langsung ke bagian selanjutnya jika Anda sudah tahu.
Ketika Anda menggunakan for item in ountuk beberapa objek iterable o, Python memanggil iter(o)dan mengharapkan objek iterator sebagai nilai balik. Iterator adalah objek apa pun yang mengimplementasikan metode __next__(dan nextdengan Python 2) dan __iter__metode.
Dengan konvensi, __iter__metode iterator harus mengembalikan objek itu sendiri (yaitu return self). Python kemudian memanggil nextiterator hingga StopIterationdinaikkan. Semua ini terjadi secara implisit, tetapi demonstrasi berikut membuatnya terlihat:
import random
classDemoIterable(object):def __iter__(self):print('__iter__ called')returnDemoIterator()classDemoIterator(object):def __iter__(self):return self
def __next__(self):print('__next__ called')
r = random.randint(1,10)if r ==5:print('raising StopIteration')raiseStopIterationreturn r
Iterasi atas DemoIterable:
>>> di =DemoIterable()>>>for x in di:...print(x)...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
Diskusi dan ilustrasi
Pada poin 1 dan 2: mendapatkan iterator dan cek tidak dapat diandalkan
Inilah sebabnya mengapa Fluent Python oleh Luciano Ramalho merekomendasikan pemanggilan iterdan penanganan potensi TypeErrorsebagai cara paling akurat untuk memeriksa apakah suatu objek dapat diubah. Mengutip langsung dari buku:
Pada Python 3.4, cara paling akurat untuk memeriksa apakah suatu objek xdapat diubah adalah dengan memanggil iter(x)dan menangani TypeErrorpengecualian jika tidak. Ini lebih akurat daripada menggunakan isinstance(x, abc.Iterable), karena iter(x)juga mempertimbangkan __getitem__metode warisan , sedangkan IterableABC tidak.
Pada poin 3: Iterasi objek yang hanya menyediakan __getitem__, tetapi tidak__iter__
Iterasi atas instance BasicIterablekarya seperti yang diharapkan: Python membangun iterator yang mencoba mengambil item menurut indeks, mulai dari nol, hingga sebuah IndexErrordinaikkan. Metode objek demo __getitem__hanya mengembalikan itemyang diberikan sebagai argumen __getitem__(self, item)oleh iterator yang dikembalikan oleh iter.
>>> b =BasicIterable()>>> it = iter(b)>>> next(it)0>>> next(it)1>>> next(it)2>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
Perhatikan bahwa iterator meningkat StopIterationketika tidak dapat mengembalikan item berikutnya dan item IndexErroryang dinaikkan item == 3ditangani secara internal. Inilah sebabnya mengapa pengulangan BasicIterabledengan sebuah forpengulangan bekerja seperti yang diharapkan:
>>>for x in b:...print(x)...012
Berikut adalah contoh lain untuk mengembalikan konsep bagaimana iterator dikembalikan dengan itermencoba mengakses item berdasarkan indeks. WrappedDicttidak mewarisi dari dict, yang berarti instance tidak akan memiliki __iter__metode.
classWrappedDict(object):# note: no inheritance from dict!def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):try:return self._dict[item]# delegate to dict.__getitem__exceptKeyError:raiseIndexError
Perhatikan bahwa panggilan untuk __getitem__didelegasikan ke dict.__getitem__mana notasi braket persegi hanyalah sebuah singkatan.
>>> w =WrappedDict({-1:'not printed',...0:'hi',1:'StackOverflow',2:'!',...4:'not printed',...'x':'not printed'})>>>for x in w:...print(x)...
hi
StackOverflow!
Pada poin 4 dan 5: itermemeriksa iterator ketika memanggil__iter__ :
Ketika iter(o)dipanggil untuk suatu objek o, iterakan memastikan bahwa nilai balik dari __iter__, jika metode ini ada, adalah iterator. Ini berarti bahwa objek yang dikembalikan harus mengimplementasikan __next__(atau nextdengan Python 2) dan __iter__. itertidak dapat melakukan pemeriksaan kewarasan untuk objek yang hanya menyediakan __getitem__, karena tidak memiliki cara untuk memeriksa apakah item objek dapat diakses oleh indeks integer.
classFailIterIterable(object):def __iter__(self):return object()# not an iteratorclassFailGetitemIterable(object):def __getitem__(self, item):raiseException
Perhatikan bahwa membangun iterator dari FailIterIterableinstance gagal dengan segera, saat membangun iterator dari FailGetItemIterableberhasil, tetapi akan membuang Pengecualian pada panggilan pertama ke __next__.
>>> fii =FailIterIterable()>>> iter(fii)Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError: iter() returned non-iterator of type 'object'>>>>>> fgi =FailGetitemIterable()>>> it = iter(fgi)>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>File"/path/iterdemo.py", line 42,in __getitem__
raiseExceptionException
Pada poin 6: __iter__menang
Yang ini mudah. Jika suatu objek mengimplementasikan __iter__dan __getitem__, iterakan memanggil __iter__. Pertimbangkan kelas berikut
>>> iwd =IterWinsDemo()>>>for x in iwd:...print(x)...
__iter__
wins
Pada poin 7: kelas iterable Anda harus diimplementasikan __iter__
Anda mungkin bertanya pada diri sendiri mengapa sebagian besar urutan builtin seperti listmengimplementasikan suatu __iter__metode padahal __getitem__sudah cukup.
classWrappedList(object):# note: no inheritance from list!def __init__(self, lst):
self._list = lst
def __getitem__(self, item):return self._list[item]
Bagaimanapun, iterasi atas instance dari kelas di atas, yang mendelegasikan panggilan __getitem__ke list.__getitem__(menggunakan notasi braket persegi), akan berfungsi dengan baik:
>>> wl =WrappedList(['A','B','C'])>>>for x in wl:...print(x)...
A
B
C
Alasan iterables kustom Anda harus menerapkan __iter__adalah sebagai berikut:
Jika Anda menerapkan __iter__, instance akan dianggap iterables, dan isinstance(o, collections.abc.Iterable)akan kembali True.
Jika objek yang dikembalikan oleh __iter__bukan iterator, iterakan gagal segera dan menaikkan a TypeError.
Penanganan khusus __getitem__ada untuk alasan kompatibilitas ke belakang. Mengutip lagi dari Fluent Python:
Itulah sebabnya setiap urutan Python dapat diubah: mereka semua mengimplementasikan __getitem__. Bahkan, urutan standar juga diterapkan __iter__, dan Anda harus melakukannya juga, karena penanganan khusus __getitem__ada karena alasan kompatibilitas ke belakang dan mungkin hilang di masa depan (meskipun tidak ditinggalkan saat saya menulis ini).
jadi aman untuk mendefinisikan predikat is_iterabledengan kembali Truedi tryblok dan Falsedi except TypeErrorblok?
alancalvitti
Ini jawaban yang bagus. Saya pikir ini menyoroti sifat protokol getitem yang tidak intuitif dan tidak menguntungkan. Seharusnya tidak pernah ditambahkan.
Neil G
31
Ini tidak cukup: objek yang dikembalikan oleh __iter__harus mengimplementasikan protokol iterasi (yaitu nextmetode). Lihat bagian yang relevan dalam dokumentasi .
Dalam Python, praktik yang baik adalah "mencoba dan melihat" alih-alih "memeriksa".
@willem: atau "tidak meminta izin tetapi untuk pengampunan" ;-)
jldupont
14
@willem Gaya "izin" dan "pengampunan" memenuhi syarat sebagai pengetikan bebek. Jika Anda bertanya apa sebuah objek dapat melakukan daripada apa yang , bahwa mengetik bebek. Jika Anda menggunakan introspeksi, itu "izin"; jika Anda hanya mencoba melakukannya dan melihat apakah itu berhasil atau tidak, itu "pengampunan".
Mark Reed
22
Dalam Python <= 2.5, Anda tidak bisa dan tidak boleh - iterable adalah antarmuka "informal".
Tetapi karena Python 2.6 dan 3.0 Anda dapat memanfaatkan infrastruktur ABC (kelas dasar abstrak) yang baru bersama dengan beberapa ABC bawaan yang tersedia dalam modul koleksi:
from collections importIterableclassMyObject(object):pass
mo =MyObject()print isinstance(mo,Iterable)Iterable.register(MyObject)print isinstance(mo,Iterable)print isinstance("abc",Iterable)
Sekarang, apakah ini diinginkan atau benar-benar berfungsi, hanyalah masalah konvensi. Seperti yang Anda lihat, Anda bisa mendaftarkan objek yang tidak dapat diubah sebagai Iterable - dan itu akan menimbulkan pengecualian saat runtime. Oleh karena itu, isinstance memperoleh makna "baru" - itu hanya memeriksa untuk kompatibilitas jenis "dinyatakan", yang merupakan cara yang baik untuk menggunakan Python.
Di sisi lain, jika objek Anda tidak memenuhi antarmuka yang Anda butuhkan, apa yang akan Anda lakukan? Ambil contoh berikut:
from collections importIterablefrom traceback import print_exc
def check_and_raise(x):ifnot isinstance(x,Iterable):raiseTypeError,"%s is not iterable"% x
else:for i in x:print i
def just_iter(x):for i in x:print i
classNotIterable(object):passif __name__ =="__main__":try:
check_and_raise(5)except:
print_exc()printtry:
just_iter(5)except:
print_exc()printtry:Iterable.register(NotIterable)
ni =NotIterable()
check_and_raise(ni)except:
print_exc()print
Jika objek tidak memenuhi apa yang Anda harapkan, Anda cukup melempar TypeError, tetapi jika ABC yang tepat telah terdaftar, cek Anda tidak berguna. Sebaliknya, jika __iter__metode ini tersedia, Python akan secara otomatis mengenali objek kelas itu sebagai Iterable.
Jadi, jika Anda hanya mengharapkan iterable, beralihlah dan lupakan saja. Di sisi lain, jika Anda perlu melakukan hal-hal yang berbeda tergantung pada tipe input, Anda mungkin menemukan infrastruktur ABC cukup berguna.
jangan gunakan telanjang except:di kode contoh untuk pemula. Ini mempromosikan praktik buruk.
jfs
JFS: Saya tidak mau, tapi saya harus melalui beberapa kode peningkatan pengecualian dan saya tidak ingin menangkap pengecualian spesifik ... Saya pikir tujuan dari kode ini cukup jelas.
Alan Franzoni
21
try:#treat object as iterableexceptTypeError, e:#object is not actually iterable
Jangan menjalankan cek untuk melihat apakah bebek Anda benar-benar bebek untuk melihat apakah iterable atau tidak, perlakukan itu seolah-olah itu dan mengeluh jika tidak.
Secara teknis, selama iterasi perhitungan Anda mungkin melempar TypeErrordan melemparkan Anda ke sini, tetapi pada dasarnya ya.
Chris Lutz
6
@willem: Silakan gunakan timeit untuk melakukan benchmark. Pengecualian Python seringkali lebih cepat daripada pernyataan if. Mereka bisa mengambil jalan yang sedikit lebih pendek melalui penerjemah.
S.Lott
2
@willem: IronPython memiliki pengecualian yang lambat (dibandingkan dengan CPython).
jfs
2
Percobaan yang berhasil: pernyataan sangat cepat. Jadi, jika Anda memiliki beberapa pengecualian, coba-kecuali itu cepat. Jika Anda mengharapkan banyak pengecualian, "jika" bisa lebih cepat.
Arne Babenhauserheide
2
Bukankah seharusnya objek pengecualian ditangkap dengan menambahkan " as e" setelah TypeErroralih-alih dengan menambahkan " , e"?
HelloGoodbye
21
Karena Python 3.5 Anda dapat menggunakan modul pengetikan dari perpustakaan standar untuk mengetik hal-hal terkait:
from typing importIterable...if isinstance(my_item,Iterable):print(True)
yang pada dasarnya memeriksa apakah objek mengimplementasikan inoperator.
Keuntungan (tidak ada solusi lain yang memiliki ketiganya):
itu adalah ekspresi (berfungsi sebagai lambda , berbeda dengan percobaan ... kecuali varian)
itu (harus) diimplementasikan oleh semua iterables, termasuk string (sebagai lawan dari __iter__)
bekerja pada sembarang Python> = 2.5
Catatan:
filosofi Python "meminta pengampunan, bukan izin" tidak berfungsi dengan baik ketika misalnya dalam daftar Anda memiliki iterables dan non-iterables dan Anda perlu memperlakukan setiap elemen secara berbeda sesuai dengan tipenya (memperlakukan iterables saat dicoba dan tidak iterables pada kecuali akan bekerja, tetapi akan terlihat butt-jelek dan menyesatkan)
solusi untuk masalah ini yang berusaha untuk benar-benar beralih pada objek (misalnya [x untuk x dalam obj]) untuk memeriksa apakah itu dapat diubah dapat menyebabkan hukuman kinerja yang signifikan untuk iterables besar (terutama jika Anda hanya memerlukan beberapa elemen pertama dari iterable, untuk contoh) dan harus dihindari
Bagus, tapi mengapa tidak menggunakan modul koleksi seperti yang diusulkan di stackoverflow.com/questions/1952464/… ? Tampak lebih ekspresif bagi saya.
Dave Abrahams
1
Ini lebih pendek (dan tidak memerlukan impor tambahan) tanpa kehilangan kejelasan: memiliki metode "mengandung" terasa seperti cara alami untuk memeriksa apakah ada koleksi benda.
Vlad
46
Hanya karena sesuatu dapat mengandung sesuatu tidak selalu berarti itu dapat diubah. Misalnya, pengguna dapat memeriksa apakah suatu titik ada dalam kubus 3D, tetapi bagaimana Anda akan mengulanginya melalui objek ini?
Casey Kuball
13
Ini salah. Iterable sendiri tidak mendukung berisi , setidaknya dengan Python 3.4.
Peter Shinners
15
Anda bisa mencoba ini:
def iterable(a):try:(x for x in a)returnTrueexceptTypeError:returnFalse
Jika kita bisa membuat generator yang beralih di atasnya (tapi jangan pernah menggunakan generator sehingga tidak memakan tempat), itu bisa diubah. Sepertinya itu semacam "duh". Mengapa Anda perlu menentukan apakah suatu variabel dapat diubah sejak awal?
Bagaimana dengan iterable(itertools.repeat(0))? :)
badp
5
@badp, yang (x for x in a)baru saja membuat generator, itu tidak melakukan iterasi pada a.
catchmeifyoutry
5
Apakah berusaha (x for x in a)sama dengan mencoba iterator = iter(a)? Atau ada beberapa kasus di mana keduanya berbeda?
maks
Bukankah for _ in a: breaklebih mudah? Apakah lebih lambat?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D itu buruk jika objek yang diuji adalah iterator yang iterated setelahnya (itu akan menjadi 1 item pendek karena posisinya tidak dapat diatur ulang), membuat generator sampah tidak beralih pada objek karena mereka tidak diulangi, meskipun saya tidak yakin bahwa itu akan meningkatkan 100% TypeError jika tidak dapat diperbaiki.
semua urutan jenis (seperti list, str, dan tuple) dan beberapa non-urutan jenis seperti dictdan filedan objek dari setiap kelas yang Anda mendefinisikan dengan __iter__()atau __getitem__()metode. Iterables dapat digunakan dalam for for dan di banyak tempat lain di mana diperlukan urutan (zip (), map (), ...). Ketika objek iterable diteruskan sebagai argumen ke fungsi built-in iter (), ia mengembalikan iterator untuk objek.
Tentu saja, mengingat gaya pengkodean umum untuk Python berdasarkan pada fakta bahwa “Lebih mudah untuk meminta pengampunan daripada izin.”, Harapan umum adalah menggunakan
try:for i in object_in_question:
do_something
exceptTypeError:
do_something_for_non_iterable
Tetapi jika Anda perlu memeriksanya secara eksplisit, Anda dapat menguji untuk iterable oleh hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Anda perlu memeriksa keduanya, karena strs tidak memiliki __iter__metode (setidaknya tidak dalam Python 2, dalam Python 3 yang mereka lakukan) dan karena generatorobjek tidak memiliki __getitem__metode.
Setiap kali Anda suka if x: return Trueelse: return False(dengan xmenjadi boolean) Anda dapat menulis ini sebagai return x. Dalam kasus Anda return isinstance(…)tanpa if.
Alfe
Karena Anda mengakui bahwa solusi Alfe lebih baik, mengapa Anda tidak mengedit jawaban Anda hanya dengan mengatakan itu? Sebaliknya, Anda sekarang memiliki KEDUA versi dalam jawaban Anda. Verbositas yang tidak perlu. Mengirimkan edit untuk memperbaikinya.
ToolmakerSteve
2
Anda harus menangkap "TypeError" di baris `kecuali: return False`. Menangkap semuanya adalah pola yang buruk.
Mariusz Jamro
Tahu bahwa. Saya menerjemahkan potongan kode dari perpustakaan NumPy, yang menggunakan pengecualian umum.
fmonegaglia
Hanya karena kode diambil dari NumPy tidak berarti itu bagus ... pola atau tidak, satu-satunya waktu menangkap semuanya harus dilakukan adalah jika Anda secara eksplisit menangani kesalahan di dalam program Anda.
Namun ini hanya melihat apakah ada __iter__dan tidak terlalu peduli tentang urutan dan sejenisnya.
ead
4
Itu selalu menghindari saya mengapa python memiliki callable(obj) -> booltetapi tidak iterable(obj) -> bool...
pasti lebih mudah untuk dilakukan hasattr(obj,'__call__')bahkan jika lebih lambat.
Karena hampir setiap jawaban lain merekomendasikan penggunaan try/ except TypeError, di mana pengujian untuk pengecualian umumnya dianggap praktik yang buruk di antara bahasa apa pun, berikut ini adalah implementasi dari iterable(obj) -> boolsaya telah semakin menyukai dan sering menggunakan:
Demi python 2, saya akan menggunakan lambda hanya untuk meningkatkan kinerja ekstra ...
(dalam python 3 tidak masalah apa yang Anda gunakan untuk mendefinisikan fungsi, defmemiliki kecepatan yang kira-kira sama lambda)
Perhatikan bahwa fungsi ini dieksekusi lebih cepat untuk objek dengan __iter__karena tidak diuji __getitem__.
Sebagian besar objek yang dapat diubah harus bergantung pada __iter__tempat objek kasus khusus jatuh kembali __getitem__, meskipun salah satu diperlukan untuk objek yang dapat diulang.
(dan karena ini standar, itu juga mempengaruhi objek C)
dia tidak memberikan kode yang berfungsi, apalagi berbicara tentang kinerja python ... walaupun jawaban ini benar-benar hanya untuk kenyamanan seperti yang saya lihat dilakukan berulang kali di sini.
Tcll
3
def is_iterable(x):try:0in x
exceptTypeError:returnFalseelse:returnTrue
Ini akan mengatakan ya untuk segala macam objek yang dapat diubah, tetapi akan mengatakan tidak pada string dalam Python 2 . (Itulah yang saya inginkan misalnya ketika fungsi rekursif dapat mengambil string atau wadah string. Dalam situasi itu, meminta maaf dapat menyebabkan obfuscode, dan lebih baik meminta izin terlebih dahulu.)
Catatan: is_iterable () akan mengatakan ya untuk string tipe bytesdan bytearray.
bytesobjek dalam Python 3 dapat diulang True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))Tidak ada jenis dalam Python 2.
bytearray objek dalam Python 2 dan 3 dapat diubah True == is_iterable(bytearray(b"abc"))
OP hasattr(x, '__iter__')pendekatan akan mengatakan ya untuk string di Python 3 dan tidak ada di Python 2 (tidak peduli apakah ''atau b''atau u''). Terima kasih kepada @LuisMasuelli karena memperhatikannya juga akan mengecewakan Anda dengan kereta __iter__.
Cara termudah, dengan menghormati pengetikan bebek Python , adalah dengan menangkap kesalahan (Python tahu benar apa yang diharapkan dari suatu objek untuk menjadi iterator):
class A(object):def __getitem__(self, item):return something
class B(object):def __iter__(self):# Return a compliant iterator. Just an examplereturn iter([])class C(object):def __iter__(self):# Return crapreturn1class D(object):passdef iterable(obj):try:
iter(obj)returnTrueexcept:returnFalseassert iterable(A())assert iterable(B())assert iterable(C())assertnot iterable(D())
Catatan :
Itu tidak relevan pembedaan apakah objek tidak iterable, atau buggy __iter__telah diimplementasikan, jika tipe pengecualiannya sama: toh Anda tidak akan dapat mengulangi objek.
Saya rasa saya mengerti kekhawatiran Anda: Bagaimana bisa callableada sebagai cek jika saya juga bisa mengandalkan pengetikan bebek untuk menaikkan AttributeErrorjika __call__tidak ditentukan untuk objek saya, tapi itu tidak berlaku untuk pengecekan yang dapat diubah?
Saya tidak tahu jawabannya, tetapi Anda bisa mengimplementasikan fungsi yang saya (dan pengguna lain) berikan, atau hanya menangkap pengecualian dalam kode Anda (implementasi Anda di bagian itu akan seperti fungsi yang saya tulis - pastikan Anda mengisolasi pembuatan iterator dari sisa kode sehingga Anda dapat menangkap pengecualian dan membedakannya dari yang lain TypeError.
fruits =("apple","banana","peach")
isiterable(fruits)# returns True
num =345
isiterable(num)# returns False
isiterable(str)# returns False because str type is type class and it's not iterable.
hello ="hello dude !"
isiterable(hello)# returns True because as you know string objects are iterable
begitu banyak jawaban terperinci di atas dengan banyak upvotes dan Anda memberikan jawaban yang tidak dapat dijelaskan ... meh
Nrzonline
Tolong jangan memposting kode telanjang. Juga termasuk penjelasan tentang apa yang dilakukan ini.
Jonathan Mee
1
Alih-alih memeriksa __iter__atribut, Anda bisa memeriksa __len__atribut, yang diimplementasikan oleh setiap python yang dibangun iterable, termasuk string.
Objek yang tidak dapat diubah tidak akan mengimplementasikan ini karena alasan yang jelas. Namun, itu tidak menangkap iterables yang ditentukan pengguna yang tidak mengimplementasikannya, atau ekspresi generator, yang iterdapat menangani. Namun, ini dapat dilakukan dalam satu baris, dan menambahkan orekspresi sederhana memeriksa generator akan memperbaiki masalah ini. (Perhatikan bahwa tulisan type(my_generator_expression) == generatorakan melempar NameError. Rujuk ke jawaban ini sebagai gantinya.)
Anda dapat menggunakan GeneratorType dari jenis:
>>>import types
>>> types.GeneratorType<class'generator'>>>> gen =(i for i in range(10))>>> isinstance(gen, types.GeneratorType)True
--- jawaban yang diterima oleh utdemir
(Ini membuatnya berguna untuk memeriksa apakah Anda dapat memanggil lenobjek itu.)
sayangnya tidak semua objek yang dapat diubah menggunakan __len__... untuk kasus ini, biasanya penggunaan jarak penghitungan yang tidak benar antara 2 objek. di mana obj.dist()bisa dengan mudah diganti.
Tcll
Ya. Sebagian besar pengguna mendefinisikan iterables mengimplementasikan iter dan getitem tetapi tidak len. Namun, tipe bawaan memang ada, dan jika Anda ingin memeriksa apakah Anda dapat memanggil fungsi len di atasnya, memeriksa len lebih aman. Tapi kamu benar.
DarthCadeus
0
Tidak benar-benar "benar" tetapi dapat berfungsi sebagai pemeriksaan cepat dari jenis yang paling umum seperti string, tuple, float, dll ...
Agak terlambat ke pesta tetapi saya bertanya pada diri sendiri pertanyaan ini dan melihat ini kemudian memikirkan jawaban. Saya tidak tahu apakah seseorang sudah memposting ini. Tetapi pada dasarnya, saya perhatikan bahwa semua jenis iterable memiliki __getitem __ () dict mereka. Ini adalah bagaimana Anda akan memeriksa apakah suatu objek itu dapat diubah tanpa mencoba. (Pun intended)
__getitem__
juga cukup untuk membuat objek dapat diubahiter(myObj)
berhasil jikaisinstance(myObj, dict)
, jadi jika Anda melihat sebuahmyObj
yang bisa menjadi urutandict
s atau tunggaldict
, Anda akan berhasil dalam kedua kasus. Kehalusan yang penting jika Anda ingin tahu apa itu urutan dan apa yang tidak. (dalam Python 2)__getitem__
juga cukup untuk membuat objek dapat diubah ... jika dimulai dengan indeks nol .Jawaban:
Saya telah mempelajari masalah ini agak belakangan ini. Berdasarkan kesimpulan saya adalah bahwa saat ini ini adalah pendekatan terbaik:
Hal di atas telah direkomendasikan sebelumnya, tetapi konsensus umum adalah bahwa menggunakan
iter()
akan lebih baik:Kami telah menggunakan
iter()
kode kami juga untuk tujuan ini, tetapi akhir-akhir ini saya mulai semakin terganggu oleh objek yang hanya__getitem__
dianggap dapat diubah. Ada alasan valid untuk memiliki objek__getitem__
yang tidak dapat diubah dan dengan mereka kode di atas tidak berfungsi dengan baik. Sebagai contoh kehidupan nyata kita bisa menggunakan Faker . Kode di atas melaporkannya iterable tetapi sebenarnya mencoba iterate yang menyebabkannyaAttributeError
(diuji dengan Faker 4.0.2):Jika kami akan menggunakan
insinstance()
, kami tidak akan secara tidak sengaja menganggap instance Faker (atau objek lain yang hanya memiliki__getitem__
) untuk diubah:Jawaban sebelumnya berkomentar bahwa menggunakan
iter()
lebih aman karena cara lama untuk menerapkan iterasi dengan Python didasarkan__getitem__
danisinstance()
pendekatan tidak akan mendeteksi itu. Ini mungkin benar dengan versi Python lama, tetapi berdasarkan pengujian yang cukup lengkap sayaisinstance()
bekerja sangat baik saat ini. Satu-satunya kasus di manaisinstance()
tidak bekerja tetapiiter()
lakukan adalah denganUserDict
ketika menggunakan Python 2. Jika itu relevan, itu mungkin digunakanisinstance(item, (Iterable, UserDict))
untuk mendapatkan yang tertutup.sumber
typing.Dict
dianggap iterable olehiter(Dict)
tetapilist(Dict)
gagal dengan kesalahanTypeError: Parameters to generic types must be types. Got 0.
. Seperti yang diharapkanisinstance(Dict, Iterable)
mengembalikan false.iter
menyebabkan beberapa kode kami yang menggunakan "pre-caching" melambat secara tidak perlu. Jika__iter__
kodenya lambat, maka akan memanggiliter
... kapan saja Anda hanya ingin melihat apakah ada sesuatu yang dapat diubah.Memeriksa
__iter__
karya pada tipe urutan, tetapi akan gagal misalnya string di Python 2 . Saya ingin tahu jawaban yang benar juga, sampai saat itu, di sini ada satu kemungkinan (yang juga bisa digunakan pada string):The
iter
built-in cek untuk__iter__
metode atau dalam kasus string yang__getitem__
metode.Pendekatan pythonic umum lainnya adalah dengan menganggap iterable, kemudian gagal dengan anggun jika tidak bekerja pada objek yang diberikan. Daftar istilah Python:
The
collections
modul menyediakan beberapa kelas dasar abstrak, yang memungkinkan untuk meminta kelas atau contoh jika mereka menyediakan fungsionalitas tertentu, misalnya:Namun, ini tidak memeriksa untuk kelas yang dapat diulangi
__getitem__
.sumber
[e for e in my_object]
dapat menimbulkan pengecualian karena alasan lain, yaitumy_object
tidak terdefinisi atau kemungkinan bug dalammy_object
implementasi.isinstance('', Sequence) == True
) dan sebagaimana urutan apa pun itu dapat diubah (isinstance('', Iterable)
). Padahalhasattr('', '__iter__') == False
dan itu mungkin membingungkan.my_object
sangat besar (misalnya, tak terbatas sepertiitertools.count()
) pemahaman daftar Anda akan memakan banyak waktu / memori. Lebih baik membuat generator, yang tidak akan pernah mencoba membangun daftar (berpotensi tak terbatas).hasattr(u"hello", '__iter__')
pengembalianTrue
Mengetik bebek
Ketik memeriksa
Gunakan Kelas Basis Abstrak . Mereka membutuhkan setidaknya Python 2.6 dan hanya berfungsi untuk kelas gaya baru.
Namun,
iter()
sedikit lebih dapat diandalkan seperti yang dijelaskan oleh dokumentasi :sumber
isinstance(x, (collections.Iterable, collections.Sequence))
gantiiter(x)
", perhatikan bahwa ini masih tidak akan mendeteksi objek iterable yang mengimplementasikan hanya__getitem__
tetapi tidak__len__
. Gunakaniter(x)
dan tangkap pengecualian.iter(slide1)
, semuanya berjalan dengan baik, namunisinstance(slide1, Iterable)
lemparannyaTypeError: issubclass() arg 1 must be a class
.PyUNO
Perhatikan bahwa pesan kesalahan Anda mengatakanissubclass()
bukanisinstance()
.Saya ingin menumpahkan sedikit yang sedikit lebih terang pada interaksi
iter
,__iter__
dan__getitem__
dan apa yang terjadi di balik tirai. Berbekal pengetahuan itu, Anda akan dapat memahami mengapa yang terbaik yang bisa Anda lakukan adalahSaya akan mendaftar fakta terlebih dahulu dan kemudian menindaklanjuti dengan pengingat cepat tentang apa yang terjadi ketika Anda mempekerjakan seorang
for
loop dalam python, diikuti dengan diskusi untuk menggambarkan fakta-fakta.Fakta
Anda bisa mendapatkan iterator dari objek apa pun
o
dengan memanggiliter(o)
jika setidaknya salah satu dari kondisi berikut ini berlaku:a)
o
memiliki__iter__
metode yang mengembalikan objek iterator. Iterator adalah objek apa pun dengan metode__iter__
dan__next__
(Python 2next
:).b)
o
punya__getitem__
metode.Memeriksa instance
Iterable
atauSequence
, atau memeriksa atribut__iter__
tidak cukup.Jika suatu objek
o
hanya mengimplementasikan__getitem__
, tetapi tidak__iter__
,iter(o)
akan membangun iterator yang mencoba mengambil item dario
dengan indeks integer, mulai dari indeks 0. Iterator akan menangkap setiapIndexError
(tetapi tidak ada kesalahan lain) yang dinaikkan dan kemudian memunculkanStopIteration
dirinya sendiri.Dalam arti yang paling umum, tidak ada cara untuk memeriksa apakah iterator yang dikembalikan oleh
iter
waras selain untuk mencobanya.Jika suatu objek
o
mengimplementasikan__iter__
,iter
fungsi akan memastikan bahwa objek yang dikembalikan oleh__iter__
iterator. Tidak ada pemeriksaan kewarasan jika suatu objek hanya mengimplementasikan__getitem__
.__iter__
menang. Jika suatu objeko
mengimplementasikan keduanya__iter__
dan__getitem__
,iter(o)
akan memanggil__iter__
.Jika Anda ingin membuat objek Anda sendiri dapat diubah, selalu terapkan
__iter__
metode ini.for
loopUntuk mengikuti, Anda perlu pemahaman tentang apa yang terjadi ketika Anda menggunakan
for
loop dengan Python. Jangan ragu untuk langsung ke bagian selanjutnya jika Anda sudah tahu.Ketika Anda menggunakan
for item in o
untuk beberapa objek iterableo
, Python memanggiliter(o)
dan mengharapkan objek iterator sebagai nilai balik. Iterator adalah objek apa pun yang mengimplementasikan metode__next__
(dannext
dengan Python 2) dan__iter__
metode.Dengan konvensi,
__iter__
metode iterator harus mengembalikan objek itu sendiri (yaitureturn self
). Python kemudian memanggilnext
iterator hinggaStopIteration
dinaikkan. Semua ini terjadi secara implisit, tetapi demonstrasi berikut membuatnya terlihat:Iterasi atas
DemoIterable
:Diskusi dan ilustrasi
Pada poin 1 dan 2: mendapatkan iterator dan cek tidak dapat diandalkan
Pertimbangkan kelas berikut:
Menelepon
iter
dengan instance ofBasicIterable
akan mengembalikan iterator tanpa masalah karenaBasicIterable
implementasinya__getitem__
.Namun, penting untuk dicatat bahwa
b
tidak memiliki__iter__
atribut dan tidak dianggap sebagai contohIterable
atauSequence
:Inilah sebabnya mengapa Fluent Python oleh Luciano Ramalho merekomendasikan pemanggilan
iter
dan penanganan potensiTypeError
sebagai cara paling akurat untuk memeriksa apakah suatu objek dapat diubah. Mengutip langsung dari buku:Pada poin 3: Iterasi objek yang hanya menyediakan
__getitem__
, tetapi tidak__iter__
Iterasi atas instance
BasicIterable
karya seperti yang diharapkan: Python membangun iterator yang mencoba mengambil item menurut indeks, mulai dari nol, hingga sebuahIndexError
dinaikkan. Metode objek demo__getitem__
hanya mengembalikanitem
yang diberikan sebagai argumen__getitem__(self, item)
oleh iterator yang dikembalikan olehiter
.Perhatikan bahwa iterator meningkat
StopIteration
ketika tidak dapat mengembalikan item berikutnya dan itemIndexError
yang dinaikkanitem == 3
ditangani secara internal. Inilah sebabnya mengapa pengulanganBasicIterable
dengan sebuahfor
pengulangan bekerja seperti yang diharapkan:Berikut adalah contoh lain untuk mengembalikan konsep bagaimana iterator dikembalikan dengan
iter
mencoba mengakses item berdasarkan indeks.WrappedDict
tidak mewarisi daridict
, yang berarti instance tidak akan memiliki__iter__
metode.Perhatikan bahwa panggilan untuk
__getitem__
didelegasikan kedict.__getitem__
mana notasi braket persegi hanyalah sebuah singkatan.Pada poin 4 dan 5:
iter
memeriksa iterator ketika memanggil__iter__
:Ketika
iter(o)
dipanggil untuk suatu objeko
,iter
akan memastikan bahwa nilai balik dari__iter__
, jika metode ini ada, adalah iterator. Ini berarti bahwa objek yang dikembalikan harus mengimplementasikan__next__
(ataunext
dengan Python 2) dan__iter__
.iter
tidak dapat melakukan pemeriksaan kewarasan untuk objek yang hanya menyediakan__getitem__
, karena tidak memiliki cara untuk memeriksa apakah item objek dapat diakses oleh indeks integer.Perhatikan bahwa membangun iterator dari
FailIterIterable
instance gagal dengan segera, saat membangun iterator dariFailGetItemIterable
berhasil, tetapi akan membuang Pengecualian pada panggilan pertama ke__next__
.Pada poin 6:
__iter__
menangYang ini mudah. Jika suatu objek mengimplementasikan
__iter__
dan__getitem__
,iter
akan memanggil__iter__
. Pertimbangkan kelas berikutdan output ketika mengulang contoh:
Pada poin 7: kelas iterable Anda harus diimplementasikan
__iter__
Anda mungkin bertanya pada diri sendiri mengapa sebagian besar urutan builtin seperti
list
mengimplementasikan suatu__iter__
metode padahal__getitem__
sudah cukup.Bagaimanapun, iterasi atas instance dari kelas di atas, yang mendelegasikan panggilan
__getitem__
kelist.__getitem__
(menggunakan notasi braket persegi), akan berfungsi dengan baik:Alasan iterables kustom Anda harus menerapkan
__iter__
adalah sebagai berikut:__iter__
, instance akan dianggap iterables, danisinstance(o, collections.abc.Iterable)
akan kembaliTrue
.__iter__
bukan iterator,iter
akan gagal segera dan menaikkan aTypeError
.__getitem__
ada untuk alasan kompatibilitas ke belakang. Mengutip lagi dari Fluent Python:sumber
is_iterable
dengan kembaliTrue
ditry
blok danFalse
diexcept TypeError
blok?Ini tidak cukup: objek yang dikembalikan oleh
__iter__
harus mengimplementasikan protokol iterasi (yaitunext
metode). Lihat bagian yang relevan dalam dokumentasi .Dalam Python, praktik yang baik adalah "mencoba dan melihat" alih-alih "memeriksa".
sumber
Dalam Python <= 2.5, Anda tidak bisa dan tidak boleh - iterable adalah antarmuka "informal".
Tetapi karena Python 2.6 dan 3.0 Anda dapat memanfaatkan infrastruktur ABC (kelas dasar abstrak) yang baru bersama dengan beberapa ABC bawaan yang tersedia dalam modul koleksi:
Sekarang, apakah ini diinginkan atau benar-benar berfungsi, hanyalah masalah konvensi. Seperti yang Anda lihat, Anda bisa mendaftarkan objek yang tidak dapat diubah sebagai Iterable - dan itu akan menimbulkan pengecualian saat runtime. Oleh karena itu, isinstance memperoleh makna "baru" - itu hanya memeriksa untuk kompatibilitas jenis "dinyatakan", yang merupakan cara yang baik untuk menggunakan Python.
Di sisi lain, jika objek Anda tidak memenuhi antarmuka yang Anda butuhkan, apa yang akan Anda lakukan? Ambil contoh berikut:
Jika objek tidak memenuhi apa yang Anda harapkan, Anda cukup melempar TypeError, tetapi jika ABC yang tepat telah terdaftar, cek Anda tidak berguna. Sebaliknya, jika
__iter__
metode ini tersedia, Python akan secara otomatis mengenali objek kelas itu sebagai Iterable.Jadi, jika Anda hanya mengharapkan iterable, beralihlah dan lupakan saja. Di sisi lain, jika Anda perlu melakukan hal-hal yang berbeda tergantung pada tipe input, Anda mungkin menemukan infrastruktur ABC cukup berguna.
sumber
except:
di kode contoh untuk pemula. Ini mempromosikan praktik buruk.Jangan menjalankan cek untuk melihat
apakah bebek Anda benar-benar bebekuntuk melihat apakah iterable atau tidak, perlakukan itu seolah-olah itu dan mengeluh jika tidak.sumber
TypeError
dan melemparkan Anda ke sini, tetapi pada dasarnya ya.as e
" setelahTypeError
alih-alih dengan menambahkan ", e
"?Karena Python 3.5 Anda dapat menggunakan modul pengetikan dari perpustakaan standar untuk mengetik hal-hal terkait:
sumber
Solusi terbaik yang saya temukan sejauh ini:
hasattr(obj, '__contains__')
yang pada dasarnya memeriksa apakah objek mengimplementasikan
in
operator.Keuntungan (tidak ada solusi lain yang memiliki ketiganya):
__iter__
)Catatan:
sumber
Anda bisa mencoba ini:
Jika kita bisa membuat generator yang beralih di atasnya (tapi jangan pernah menggunakan generator sehingga tidak memakan tempat), itu bisa diubah. Sepertinya itu semacam "duh". Mengapa Anda perlu menentukan apakah suatu variabel dapat diubah sejak awal?
sumber
iterable(itertools.repeat(0))
? :)(x for x in a)
baru saja membuat generator, itu tidak melakukan iterasi pada a.(x for x in a)
sama dengan mencobaiterator = iter(a)
? Atau ada beberapa kasus di mana keduanya berbeda?for _ in a: break
lebih mudah? Apakah lebih lambat?Saya menemukan solusi yang bagus di sini :
sumber
Menurut Python 2 Glosarium , iterables adalah
Tentu saja, mengingat gaya pengkodean umum untuk Python berdasarkan pada fakta bahwa “Lebih mudah untuk meminta pengampunan daripada izin.”, Harapan umum adalah menggunakan
Tetapi jika Anda perlu memeriksanya secara eksplisit, Anda dapat menguji untuk iterable oleh
hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Anda perlu memeriksa keduanya, karenastr
s tidak memiliki__iter__
metode (setidaknya tidak dalam Python 2, dalam Python 3 yang mereka lakukan) dan karenagenerator
objek tidak memiliki__getitem__
metode.sumber
Saya sering merasa nyaman, di dalam skrip saya, untuk mendefinisikan suatu
iterable
fungsi. (Sekarang memasukkan penyederhanaan yang disarankan Alfe):sehingga Anda dapat menguji apakah ada objek yang dapat diubah dalam bentuk yang sangat mudah dibaca
seperti yang akan Anda lakukan dengan
callable
fungsiEDIT: jika Anda telah menginstal numpy, Anda dapat melakukan: from
numpy import iterable
, yang merupakan sesuatu sepertiJika Anda tidak memiliki numpy, Anda dapat menerapkan kode ini, atau yang di atas.
sumber
if x: return True
else: return False
(denganx
menjadi boolean) Anda dapat menulis ini sebagaireturn x
. Dalam kasus Andareturn isinstance(…)
tanpaif
.panda memiliki fungsi bawaan seperti itu:
sumber
__iter__
dan tidak terlalu peduli tentang urutan dan sejenisnya.Itu selalu menghindari saya mengapa python memiliki
callable(obj) -> bool
tetapi tidakiterable(obj) -> bool
...pasti lebih mudah untuk dilakukan
hasattr(obj,'__call__')
bahkan jika lebih lambat.Karena hampir setiap jawaban lain merekomendasikan penggunaan
try
/except TypeError
, di mana pengujian untuk pengecualian umumnya dianggap praktik yang buruk di antara bahasa apa pun, berikut ini adalah implementasi dariiterable(obj) -> bool
saya telah semakin menyukai dan sering menggunakan:Demi python 2, saya akan menggunakan lambda hanya untuk meningkatkan kinerja ekstra ...
(dalam python 3 tidak masalah apa yang Anda gunakan untuk mendefinisikan fungsi,
def
memiliki kecepatan yang kira-kira samalambda
)Perhatikan bahwa fungsi ini dieksekusi lebih cepat untuk objek dengan
__iter__
karena tidak diuji__getitem__
.Sebagian besar objek yang dapat diubah harus bergantung pada
__iter__
tempat objek kasus khusus jatuh kembali__getitem__
, meskipun salah satu diperlukan untuk objek yang dapat diulang.(dan karena ini standar, itu juga mempengaruhi objek C)
sumber
Ini akan mengatakan ya untuk segala macam objek yang dapat diubah, tetapi akan mengatakan tidak pada string dalam Python 2 . (Itulah yang saya inginkan misalnya ketika fungsi rekursif dapat mengambil string atau wadah string. Dalam situasi itu, meminta maaf dapat menyebabkan obfuscode, dan lebih baik meminta izin terlebih dahulu.)
Banyak strategi lain di sini akan mengatakan ya untuk string. Gunakan mereka jika itu yang Anda inginkan.
Catatan: is_iterable () akan mengatakan ya untuk string tipe
bytes
danbytearray
.bytes
objek dalam Python 3 dapat diulangTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
Tidak ada jenis dalam Python 2.bytearray
objek dalam Python 2 dan 3 dapat diubahTrue == is_iterable(bytearray(b"abc"))
OP
hasattr(x, '__iter__')
pendekatan akan mengatakan ya untuk string di Python 3 dan tidak ada di Python 2 (tidak peduli apakah''
ataub''
atauu''
). Terima kasih kepada @LuisMasuelli karena memperhatikannya juga akan mengecewakan Anda dengan kereta__iter__
.sumber
Cara termudah, dengan menghormati pengetikan bebek Python , adalah dengan menangkap kesalahan (Python tahu benar apa yang diharapkan dari suatu objek untuk menjadi iterator):
Catatan :
__iter__
telah diimplementasikan, jika tipe pengecualiannya sama: toh Anda tidak akan dapat mengulangi objek.Saya rasa saya mengerti kekhawatiran Anda: Bagaimana bisa
callable
ada sebagai cek jika saya juga bisa mengandalkan pengetikan bebek untuk menaikkanAttributeError
jika__call__
tidak ditentukan untuk objek saya, tapi itu tidak berlaku untuk pengecekan yang dapat diubah?Saya tidak tahu jawabannya, tetapi Anda bisa mengimplementasikan fungsi yang saya (dan pengguna lain) berikan, atau hanya menangkap pengecualian dalam kode Anda (implementasi Anda di bagian itu akan seperti fungsi yang saya tulis - pastikan Anda mengisolasi pembuatan iterator dari sisa kode sehingga Anda dapat menangkap pengecualian dan membedakannya dari yang lain
TypeError
.sumber
The
isiterable
func di pengembalian kode berikutTrue
jika objek adalah iterable. jika bukan pengembalian yang bisa dikembalikanFalse
contoh
sumber
Alih-alih memeriksa
__iter__
atribut, Anda bisa memeriksa__len__
atribut, yang diimplementasikan oleh setiap python yang dibangun iterable, termasuk string.Objek yang tidak dapat diubah tidak akan mengimplementasikan ini karena alasan yang jelas. Namun, itu tidak menangkap iterables yang ditentukan pengguna yang tidak mengimplementasikannya, atau ekspresi generator, yang
iter
dapat menangani. Namun, ini dapat dilakukan dalam satu baris, dan menambahkanor
ekspresi sederhana memeriksa generator akan memperbaiki masalah ini. (Perhatikan bahwa tulisantype(my_generator_expression) == generator
akan melemparNameError
. Rujuk ke jawaban ini sebagai gantinya.)(Ini membuatnya berguna untuk memeriksa apakah Anda dapat memanggil
len
objek itu.)sumber
__len__
... untuk kasus ini, biasanya penggunaan jarak penghitungan yang tidak benar antara 2 objek. di manaobj.dist()
bisa dengan mudah diganti.Tidak benar-benar "benar" tetapi dapat berfungsi sebagai pemeriksaan cepat dari jenis yang paling umum seperti string, tuple, float, dll ...
sumber
Agak terlambat ke pesta tetapi saya bertanya pada diri sendiri pertanyaan ini dan melihat ini kemudian memikirkan jawaban. Saya tidak tahu apakah seseorang sudah memposting ini. Tetapi pada dasarnya, saya perhatikan bahwa semua jenis iterable memiliki __getitem __ () dict mereka. Ini adalah bagaimana Anda akan memeriksa apakah suatu objek itu dapat diubah tanpa mencoba. (Pun intended)
sumber