__getitem__wystarcza również, aby obiekt mógł być iterowalny
Kos
4
FWIW: iter(myObj)jeśli się uda isinstance(myObj, dict), więc jeśli spojrzysz myObjna sekwencję dicts lub pojedynczą dict, odniesiesz sukces w obu przypadkach. Subtelność, która jest ważna, jeśli chcesz wiedzieć, co jest sekwencją, a co nie. (w Python 2)
Ben Mosher
7
__getitem__wystarcza również, aby obiekt był iterowalny ... jeśli zaczyna się od zera indeksu .
Carlos A. Gómez
Odpowiedzi:
26
Ostatnio dość często studiowałem ten problem. Na tej podstawie stwierdzam, że obecnie jest to najlepsze podejście:
from collections.abc importIterable# drop `.abc` with Python 2.7 or lowerdef iterable(obj):return isinstance(obj,Iterable)
Powyższe zalecono już wcześniej, ale ogólny konsensus jest taki, że stosowanie iter()byłoby lepsze:
Użyliśmy również iter()w naszym kodzie do tego celu, ale ostatnio zacząłem coraz bardziej denerwować się obiektami, które tylko __getitem__uważano za iterowalne. Istnieją ważne powody, by mieć obiekt, __getitem__którego nie da się iterować, a powyższy kod nie działa dobrze. Jako przykład z życia możemy użyć Fakera . Powyższy kod zgłasza, że jest iterowalny, ale w rzeczywistości próba iteracji powoduje AttributeError(testowany z 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'
Gdybyśmy tego użyli insinstance(), przypadkowo nie uznalibyśmy instancji Fakera (ani żadnych innych obiektów posiadających tylko __getitem__) za iterowalne:
Wcześniejsze odpowiedzi skomentowały, że używanie iter()jest bezpieczniejsze, ponieważ opierał się na starym sposobie implementacji iteracji w Pythonie __getitem__i isinstance()podejście to nie wykrywa. Mogło to być prawdą w przypadku starych wersji Pythona, ale w oparciu o moje dość wyczerpujące testy isinstance()działają obecnie świetnie. Jedyny przypadek, w którym isinstance()nie działał, ale działał, iter()dotyczył UserDictużywania Pythona 2. Jeśli jest to istotne, można skorzystać isinstance(item, (Iterable, UserDict))z tej opcji.
typing.DictJest również uważany za iterowalny, iter(Dict)ale list(Dict)kończy się błędem TypeError: Parameters to generic types must be types. Got 0.. Zgodnie z oczekiwaniami isinstance(Dict, Iterable)zwraca false.
Pekka Klärck
1
Doszedłem do tego samego wniosku, ale z różnych powodów. Użycie iterspowodowało, że część naszego kodu, który używał „buforowania wstępnego”, niepotrzebnie zwalniała. Jeśli __iter__kod jest wolny, zadzwoni iter... za każdym razem, gdy chcesz tylko sprawdzić, czy coś da się powtórzyć.
thorwhalen
842
Sprawdzanie, czy __iter__działa na typach sekwencji, ale nie powiedzie się np. Na łańcuchach w Pythonie 2 . Chciałbym również znać właściwą odpowiedź, do tego czasu jest jedna możliwość (która również działałaby na ciągach):
from __future__ import print_functiontry:
some_object_iterator = iter(some_object)exceptTypeErroras te:print(some_object,'is not iterable')
Te iterwbudowane kontrole na __iter__metodzie lub w przypadku strun do __getitem__metody.
Innym ogólnym podejściem pythonowym jest założenie, że jest iterowalny, a następnie kończy się niepowodzeniem, jeśli nie działa na danym obiekcie. Glosariusz Pythona:
Pythoniczny styl programowania, który określa typ obiektu poprzez sprawdzenie jego metody lub podpisu atrybutu, a nie poprzez wyraźne powiązanie z jakimś obiektem typu („Jeśli wygląda jak kaczka i kwacze jak kaczka , musi to być kaczka .”) Poprzez podkreślenie interfejsów zamiast określonych typów dobrze zaprojektowany kod poprawia jego elastyczność, umożliwiając podstawienie polimorficzne. Wpisywanie kaczych pozwala uniknąć testów z użyciem type () lub isinstance (). Zamiast tego zazwyczaj wykorzystuje styl programowania EAFP (łatwiej prosić o przebaczenie niż pozwolenie).
...
try:
_ =(e for e in my_object)exceptTypeError:print my_object,'is not iterable'
collectionsModuł dostarcza pewnych abstrakcyjnych klas bazowych, które pozwalają zadać klas lub instancji, jeżeli zapewniają one szczególną funkcję, na przykład:
from collections.abc importIterableif isinstance(e,Iterable):# e is iterable
Nie sprawdza to jednak klas, które można iterować __getitem__.
[e for e in my_object]może zgłosić wyjątek z innych powodów, tj. my_objectjest niezdefiniowany lub możliwe błędy we my_objectwdrażaniu.
Nick Dandoulakis,
37
Ciąg jest sekwencją ( isinstance('', Sequence) == True) i dowolną sekwencją jest iterowalna ( isinstance('', Iterable)). Chociaż hasattr('', '__iter__') == Falsemoże to być mylące.
jfs
82
Jeśli my_objectjest bardzo duży (powiedzmy, nieskończony jak itertools.count()), zrozumienie listy zajmie dużo czasu / pamięci. Lepiej jest stworzyć generator, który nigdy nie będzie próbował zbudować listy (potencjalnie nieskończonej).
Chris Lutz
14
Co jeśli jakiś obiekt rzuca błąd typu TypeError spowodowany również z innego powodu (błędy itp.)? Jak możemy to rozpoznać po „Błędzie typu iterowalnego”?
Shaung
54
Zauważ, że w Pythonie 3: hasattr(u"hello", '__iter__')zwracaTrue
Carlos
572
Pisanie kaczek
try:
iterator = iter(theElement)exceptTypeError:# not iterableelse:# iterable# for obj in iterator:# pass
from collections.abc importIterable# import directly from collections for Python < 3.3if isinstance(theElement,Iterable):# iterableelse:# not iterable
Jednakże, iter()jest nieco bardziej wiarygodne, jak to opisano w dokumentacji :
Sprawdzanie isinstance(obj, Iterable)wykrywa klasy, które są zarejestrowane jako Iterable lub mają __iter__()metodę, ale nie wykrywa klas, które iterują z tą __getitem__()
metodą. Jedynym niezawodnym sposobem ustalenia, czy obiekt jest iterowalny, jest wywołanie iter(obj).
Z „Fluent Python” Luciano Ramalho: Począwszy od Python 3.4, najdokładniejszym sposobem sprawdzenia, czy obiekt x jest iterowalny, jest wywołanie iter (x) i obsłużenie wyjątku TypeError, jeśli nie jest. Jest to dokładniejsze niż użycie isinstance (x, abc.Iterable), ponieważ iter (x) uwzględnia również starszą metodę getitem , podczas gdy Iterable ABC nie.
RdB
Jeśli myślisz „och, po prostu isinstance(x, (collections.Iterable, collections.Sequence))zamiast iter(x)”, pamiętaj, że nadal nie wykryje iterowalnego obiektu, który implementuje tylko, __getitem__ale nie __len__. Użyj iter(x)i złap wyjątek.
Dale
Twoja druga odpowiedź nie działa. Jeśli to zrobię w PyUNO iter(slide1), wszystko pójdzie dobrze, ale isinstance(slide1, Iterable)rzuty TypeError: issubclass() arg 1 must be a class.
Hi-Angel,
@ Hi-Angel brzmi jak błąd w PyUNOZauważ, że issubclass()zamiast tego wyświetlany jest komunikat o błędzie isinstance().
Georg Schölly,
2
Wywołanie iter () nad obiektem może być kosztowną operacją (patrz DataLoader w Pytorch, który uruchamia / odradza wiele procesów na iter ()).
szali
125
Chciałbym rzucić nieco więcej światła na grę iter, __iter__a __getitem__i co dzieje się za kurtyną. Uzbrojony w tę wiedzę będziesz w stanie zrozumieć, dlaczego najlepiej możesz to zrobić
try:
iter(maybe_iterable)print('iteration will probably work')exceptTypeError:print('not iterable')
Najpierw wymienię fakty, a następnie szybko przypomnę, co się stanie, gdy zastosujesz forpętlę w pythonie, a następnie przeprowadzimy dyskusję ilustrującą fakty.
Fakty
Możesz pobrać iterator z dowolnego obiektu o, wywołując, iter(o)jeśli spełniony jest przynajmniej jeden z następujących warunków:
a) oma __iter__metodę, która zwraca obiekt iteratora. Iteratorem jest dowolny obiekt z metodą __iter__i __next__(Python 2 next:).
b) oma __getitem__metodę.
Sprawdzanie instancji Iterablelub Sequencelub kontroli dla atrybutu __iter__nie jest wystarczające.
Jeśli obiekt oimplementuje tylko __getitem__, ale nie __iter__, iter(o)skonstruuje iterator, który spróbuje pobrać elementy z oindeksu liczb całkowitych, zaczynając od indeksu 0. Iterator wyłapie wszystkie IndexError(ale żadnych innych błędów), które zostaną podniesione, a następnie StopIterationsam się podniesie .
W najogólniejszym sensie nie ma sposobu, aby sprawdzić, czy powracający iterator iterjest zdrowy, poza wypróbowaniem go.
Jeśli obiekt zostanie ozaimplementowany __iter__, iterfunkcja upewni się, że zwracany przez niego obiekt __iter__jest iteratorem. Nie ma kontroli poprawności, jeśli obiekt tylko implementuje __getitem__.
__iter__wygrywa Jeśli obiekt oimplementuje oba __iter__i __getitem__, iter(o)wywoła __iter__.
Jeśli chcesz, aby własne obiekty były iterowalne, zawsze implementuj __iter__metodę.
for pętle
Aby kontynuować, musisz zrozumieć, co się dzieje, gdy zastosujesz forpętlę w Pythonie. Jeśli już wiesz, możesz przejść do następnej sekcji.
Kiedy używasz for item in odla jakiegoś iterowalnego obiektu o, Python wywołuje iter(o)i oczekuje obiektu iteratora jako wartości zwracanej. Iterator to dowolny obiekt, który implementuje metodę __next__(lub nextw Pythonie 2) i __iter__metodę.
Zgodnie z konwencją __iter__metoda iteratora powinna zwrócić sam obiekt (tj return self.). Python następnie wywołuje nextiterator, aż StopIterationzostanie podniesiony. Wszystko to dzieje się niejawnie, ale poniższa demonstracja pokazuje:
import randomclassDemoIterable(object):def __iter__(self):print('__iter__ called')returnDemoIterator()classDemoIterator(object):def __iter__(self):return selfdef __next__(self):print('__next__ called')
r = random.randint(1,10)if r ==5:print('raising StopIteration')raiseStopIterationreturn r
Iteracja po DemoIterable:
>>> di =DemoIterable()>>>for x in di:...print(x)...
__iter__ called
__next__ called9
__next__ called8
__next__ called10
__next__ called3
__next__ called10
__next__ called
raising StopIteration
Dyskusja i ilustracje
W punktach 1 i 2: uzyskanie iteratora i nierzetelne kontrole
Właśnie dlatego Fluent Python autorstwa Luciano Ramalho zaleca wywoływanie iteri zarządzanie potencjałem TypeErrorjako najdokładniejszy sposób sprawdzenia, czy obiekt jest iterowalny. Cytując bezpośrednio z książki:
Od wersji Python 3.4 najdokładniejszym sposobem sprawdzenia, czy obiekt xjest iterowalny, jest wywołanie iter(x)i obsłużenie TypeErrorwyjątku, jeśli tak nie jest. Jest to dokładniejsze niż używanie isinstance(x, abc.Iterable), ponieważ iter(x)uwzględnia również starszą __getitem__metodę, podczas gdy IterableABC nie.
W punkcie 3: Iterowanie nad obiektami, które tylko zapewniają __getitem__, ale nie zapewniają__iter__
Iteracja po instancji BasicIterabledziała zgodnie z oczekiwaniami: Python konstruuje iterator, który próbuje pobierać elementy według indeksu, zaczynając od zera, aż do IndexErrorpodniesienia wartości. Metoda obiektu demonstracyjnego __getitem__zwraca po prostu to, itemco podano jako argument __getitem__(self, item)zwracany przez iterator 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
Zauważ, że iterator podnosi się, StopIterationgdy nie może zwrócić następnego elementu, a ten, dla IndexErrorktórego podniesiono, item == 3jest obsługiwany wewnętrznie. Oto dlaczego zapętlenie BasicIterablez forpętlą działa zgodnie z oczekiwaniami:
>>>for x in b:...print(x)...012
Oto kolejny przykład, który pokazuje, w jaki sposób iterator wrócił przez iterpróby uzyskania dostępu do elementów według indeksu. WrappedDictnie dziedziczy po dict, co oznacza, że instancje nie będą miały __iter__metody.
classWrappedDict(object):# note: no inheritance from dict!def __init__(self, dic):
self._dict = dicdef __getitem__(self, item):try:return self._dict[item]# delegate to dict.__getitem__exceptKeyError:raiseIndexError
Zauważ, że wywołania do __getitem__są delegowane, dict.__getitem__dla których notacja w nawiasach kwadratowych jest po prostu skrótem.
>>> w =WrappedDict({-1:'not printed',...0:'hi',1:'StackOverflow',2:'!',...4:'not printed',...'x':'not printed'})>>>for x in w:...print(x)...
hiStackOverflow!
W punktach 4 i 5: itersprawdza iterator, gdy wywołuje__iter__ :
Kiedy iter(o)zostanie wywołany dla obiektu o, iterupewni się, że zwracana wartość __iter__, jeśli metoda jest obecna, jest iteratorem. Oznacza to, że zwracany obiekt musi implementować __next__(lub nextw Pythonie 2) i __iter__. iternie może wykonać żadnych kontroli poczytalności dla obiektów, które tylko zapewniają __getitem__, ponieważ nie ma możliwości sprawdzenia, czy elementy obiektu są dostępne przez indeks liczb całkowitych.
classFailIterIterable(object):def __iter__(self):return object()# not an iteratorclassFailGetitemIterable(object):def __getitem__(self, item):raiseException
Zauważ, że konstruowanie iteratora z FailIterIterableinstancji kończy się niepowodzeniem natychmiast, podczas gdy konstruowanie iteratora z FailGetItemIterablepowodzeniem, ale generuje wyjątek przy pierwszym wywołaniu do __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
W punkcie 6: __iter__wygrywa
Ten jest prosty. Jeśli obiekt zaimplementuje __iter__i __getitem__, iterzadzwoni __iter__. Rozważ następującą klasę
>>> iwd =IterWinsDemo()>>>for x in iwd:...print(x)...
__iter__
wins
W punkcie 7: twoje klasy iterowalne powinny zaimplementować __iter__
Możesz zadać sobie pytanie, dlaczego większość wbudowanych sekwencji, takich jak listimplementacja __iter__metody __getitem__, jest wystarczająca.
classWrappedList(object):# note: no inheritance from list!def __init__(self, lst):
self._list = lst
def __getitem__(self, item):return self._list[item]
Po tym wszystkim, iteracja przez instancje klasy wyżej, który deleguje do połączenia __getitem__się list.__getitem__(za pomocą notacji nawiasu kwadratowego), będzie działać prawidłowo:
>>> wl =WrappedList(['A','B','C'])>>>for x in wl:...print(x)...
A
B
C
Powody, dla których niestandardowe iteratory powinny zostać zaimplementowane, __iter__są następujące:
Jeśli zaimplementujesz __iter__, instancje zostaną uznane za iterowalne i isinstance(o, collections.abc.Iterable)zostaną zwrócone True.
Jeśli obiekt zwracany przez __iter__nie jest iteratorem, iternatychmiast zawiedzie i podniesie wartość TypeError.
Specjalna obsługa __getitem__istnieje ze względu na kompatybilność wsteczną. Cytując ponownie z płynnego Pythona:
Dlatego każda sekwencja Pythona jest iterowalna: wszystkie implementują __getitem__. W rzeczywistości standardowe sekwencje również się implementują __iter__, a twoja też powinna, ponieważ specjalna obsługa __getitem__istnieje ze względu na kompatybilność wsteczną i może zniknąć w przyszłości (chociaż nie jest przestarzała, kiedy to piszę).
@willem: lub „nie proś o pozwolenie, ale o wybaczenie” ;-)
jldupont
14
@willem Zarówno style „pozwolenie”, jak i „wybaczenie” kwalifikują się jako pisanie kaczek. Jeśli zapytasz, co może zrobić obiekt , a nie czym on jest , jest to pisanie kaczki. Jeśli używasz introspekcji, jest to „pozwolenie”; jeśli tylko spróbujesz to zrobić i zobaczysz, czy to działa, czy nie, to jest to „wybaczenie”.
Mark Reed,
22
W Pythonie <= 2.5 nie możesz i nie powinieneś - iterowalny był „nieformalny” interfejs.
Ale od wersji Python 2.6 i 3.0 możesz wykorzystać nową infrastrukturę ABC (abstrakcyjna klasa bazowa) wraz z niektórymi wbudowanymi ABC, które są dostępne w module kolekcji:
from collections importIterableclassMyObject(object):pass
mo =MyObject()print isinstance(mo,Iterable)Iterable.register(MyObject)print isinstance(mo,Iterable)print isinstance("abc",Iterable)
To, czy jest to pożądane, czy faktycznie działa, jest tylko kwestią konwencji. Jak widać, to można zarejestrować bez iterable obiektu jako iterable - i to podniesie wyjątek w czasie wykonywania. Dlatego isinstance nabiera „nowego” znaczenia - po prostu sprawdza zgodność z „zadeklarowanym” typem, co jest dobrym sposobem na przejście do Pythona.
Z drugiej strony, jeśli twój obiekt nie spełnia potrzebnego interfejsu, co zamierzasz zrobić? Weź następujący przykład:
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
Jeśli obiekt nie spełnia oczekiwań, po prostu generujesz błąd typu TypeError, ale jeśli zarejestrowano prawidłowe ABC, czek jest nieużyteczny. Przeciwnie, jeśli __iter__metoda jest dostępna, Python automatycznie rozpozna obiekt tej klasy jako Iterowalny.
Jeśli więc spodziewasz się iteracji, iteruj ją i zapomnij. Z drugiej strony, jeśli musisz robić różne rzeczy w zależności od typu danych wejściowych, infrastruktura ABC może okazać się bardzo przydatna.
nie używaj goły except:w przykładowym kodzie dla początkujących. Promuje złe praktyki.
jfs
JFS: Nie zrobiłbym tego, ale musiałem przejść przez wiele kodów wywołujących wyjątki i nie chciałem wychwycić konkretnego wyjątku ... Myślę, że cel tego kodu jest całkiem jasny.
Alan Franzoni
21
try:#treat object as iterableexceptTypeError, e:#object is not actually iterable
Nie sprawdzaj, czy twoja kaczka naprawdę jest kaczką, czy jest iterowalna, czy nie, traktuj ją tak, jakby była, i narzekaj, jeśli nie była.
Technicznie rzecz biorąc, podczas iteracji twoje obliczenia mogą wyrzucić TypeErrori rzucić cię tutaj, ale w zasadzie tak.
Chris Lutz
6
@willem: Proszę użyć timeit, aby wykonać test porównawczy. Wyjątki w Pythonie są często szybsze niż instrukcje if. Mogą przejść nieco krótszą ścieżkę przez tłumacza.
S.Lott,
2
@willem: IronPython ma powolne (w porównaniu do CPython) wyjątki.
jfs
2
Działająca próba: instrukcja jest naprawdę szybka. Więc jeśli masz kilka wyjątków, try-wyjątek jest szybki. Jeśli oczekujesz wielu wyjątków, „jeśli” może być szybsze.
Arne Babenhauserheide
2
Czy obiekt wyjątku nie powinien zostać przechwycony przez dodanie „ as e” po TypeErrorzamiast dodania „ , e”?
HelloGoodbye,
21
Od wersji Python 3.5 możesz używać modułu do pisania ze standardowej biblioteki do rzeczy związanych z typem:
from typing importIterable...if isinstance(my_item,Iterable):print(True)
Najlepsze rozwiązanie, jakie do tej pory znalazłem:
hasattr(obj, '__contains__')
który w zasadzie sprawdza, czy obiekt implementuje inoperator.
Zalety (żadne z pozostałych rozwiązań nie ma wszystkich trzech):
jest to wyrażenie (działa jak lambda , w przeciwieństwie do try ... oprócz wariantu)
jest (powinien być) zaimplementowany przez wszystkie iteracje, w tym ciągi (w przeciwieństwie do __iter__)
działa na dowolnym Pythonie> = 2.5
Uwagi:
filozofia Pythona „prosić o wybaczenie, a nie pozwolenie” nie działa dobrze, gdy np. na liście masz zarówno iterabilne, jak i nie iteracyjne i musisz traktować każdy element inaczej w zależności od jego typu (traktuj iterowalne przy próbie i nie iterabety z wyjątkiem mogłyby działać, ale wyglądałyby brzydko i wprowadzały w błąd)
rozwiązania tego problemu, które próbują faktycznie iterować obiekt (np. [x dla x w obiekcie]) w celu sprawdzenia, czy jest iterowalny, mogą powodować znaczne kary wydajnościowe dla dużych iteracji (szczególnie jeśli potrzebujesz tylko kilku pierwszych elementów iteracji, dla przykład) i należy tego unikać
Jest krótszy (i nie wymaga dodatkowego importu) bez utraty jasności: posiadanie metody „zawiera” jest naturalnym sposobem na sprawdzenie, czy coś jest kolekcją obiektów.
Vlad
46
To, że coś może zawierać coś, niekoniecznie oznacza, że można je powtarzać. Na przykład użytkownik może sprawdzić, czy punkt znajduje się w sześcianie 3D, ale w jaki sposób iterowałbyś przez ten obiekt?
Casey Kuball
13
To jest niepoprawne. Iterable sama nie obsługuje zawiera , co najmniej z Pythonem 3.4.
Peter Shinners,
15
Możesz spróbować:
def iterable(a):try:(x for x in a)returnTrueexceptTypeError:returnFalse
Jeśli możemy stworzyć generator, który będzie iterował nad nim (ale nigdy nie będzie używać generatora, aby nie zajmował miejsca), będzie można go powtarzać. Wygląda na coś w rodzaju „duh”. Dlaczego musisz przede wszystkim ustalić, czy zmienna jest iterowalna?
@badp, po (x for x in a)prostu tworzy generator, nie wykonuje żadnej iteracji na.
catchmeifyoutry
5
Czy próba jest (x for x in a)dokładnie równoważna z próbą iterator = iter(a)? Czy w niektórych przypadkach oba są różne?
maks.
Czy to nie for _ in a: breakjest prostsze? Czy to wolniej?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D to źle, jeśli testowany obiekt jest iteratorem, który jest następnie iterowany (będzie krótszy o 1 element, ponieważ jego pozycji nie można zresetować), tworzenie generatorów śmieci nie iteruje obiektu, ponieważ nie są iterowane, chociaż nie jestem pewien, czy w 100% podniesie błąd typu, jeśli nie da się go powtórzyć.
wszystkie typy sekwencyjne (takie jak list, stri tuple) oraz niektóre rodzaje non-sekwencji, jak dicti filei obiektów wszelkich typów definiowanych ze związkiem __iter__()lub __getitem__()metody. Iterable mogą być używane w pętli for oraz w wielu innych miejscach, w których potrzebna jest sekwencja (zip (), map (), ...). Gdy obiekt iterowalny jest przekazywany jako argument do wbudowanej funkcji iter (), zwraca iterator dla obiektu.
Oczywiście, biorąc pod uwagę ogólny styl kodowania dla Pythona w oparciu o fakt, że „łatwiej prosić o wybaczenie niż o pozwolenie”, ogólnie oczekuje się, że użyje
try:for i in object_in_question:
do_something
exceptTypeError:
do_something_for_non_iterable
Ale jeśli musisz to wyraźnie sprawdzić, możesz przetestować iterację przez hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Musisz sprawdzić oba, ponieważ strnie mają __iter__metody (przynajmniej nie w Pythonie 2, w Pythonie 3 mają) i ponieważ generatorobiekty nie mają __getitem__metody.
Ilekroć robisz coś takiego if x: return Trueelse: return False( xbo-boolean), możesz napisać to jako return x. W twoim przypadku return isinstance(…)bez żadnych if.
Alfe
Skoro przyznajesz, że rozwiązanie Alfe jest lepsze, dlaczego nie zmodyfikowałeś swojej odpowiedzi, żeby to powiedzieć? Zamiast tego masz teraz obie wersje w swojej odpowiedzi. Niepotrzebna gadatliwość. Przesyłanie zmiany w celu rozwiązania tego problemu.
ToolmakerSteve,
2
Powinieneś złapać "TypeError" w linii `oprócz: zwróć False`. Łapanie wszystkiego to zły wzór.
Mariusz Jamro
Wiedz to Przetłumaczyłem ten fragment kodu z biblioteki NumPy, która wykorzystuje ogólny wyjątek.
fmonegaglia,
Tylko dlatego, że kod jest pobierany z NumPy, nie oznacza, że jest dobry ... wzorzec czy nie, jedyny czas na złapanie wszystkiego, co należy zrobić, to jeśli jawnie obsługuje się błędy w programie.
wygląda to jednak tylko na to, czy __iter__tak naprawdę chodzi o sekwencje i tym podobne.
ead
4
Zawsze wymykało mi się pytanie, dlaczego python ma, callable(obj) -> boolale nie iterable(obj) -> bool... z
pewnością łatwiej to zrobić, hasattr(obj,'__call__')nawet jeśli jest wolniejszy.
Ponieważ prawie co druga odpowiedź zaleca używanie try/ except TypeError, gdzie testowanie wyjątków jest ogólnie uważane za złą praktykę w dowolnym języku, oto implementacja iterable(obj) -> bool, którą polubiłem i używam często:
Dla dobra Pythona 2 użyję lambda tylko dla tego dodatkowego zwiększenia wydajności ...
(w Pythonie 3 nie ma znaczenia, czego użyjesz do zdefiniowania funkcji, defma mniej więcej taką samą prędkość jak lambda)
Zauważ, że ta funkcja działa szybciej dla obiektów, __iter__ponieważ nie sprawdza __getitem__.
Większość iterowalnych obiektów powinna polegać na tym, do __iter__czego wracają obiekty ze specjalnych przypadków __getitem__, chociaż jest to wymagane, aby obiekt był iterowalny.
(a ponieważ jest to standard, wpływa również na obiekty C)
nie zapewnia działającego kodu, nie mówiąc już o wydajności Pythona ... chociaż ta odpowiedź była tak naprawdę dla wygody, jak widziałem tutaj wiele razy.
Tcll
3
def is_iterable(x):try:0in x
exceptTypeError:returnFalseelse:returnTrue
To powie tak dla wszystkich rodzajów iterowalnych obiektów, ale powie nie dla łańcuchów w Pythonie 2 . (Tego właśnie chcę na przykład, gdy funkcja rekurencyjna może wziąć ciąg znaków lub kontener ciągów znaków. W takiej sytuacji prośba o wybaczenie może prowadzić do obfuscode i lepiej najpierw poprosić o pozwolenie).
Uwaga: is_iterable () powie tak na ciągi znaków typu bytesi bytearray.
bytesobiekty w Pythonie 3 są iterowalne True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))W Pythonie 2 nie ma takiego typu.
bytearray obiekty w Pythonie 2 i 3 są iterowalne True == is_iterable(bytearray(b"abc"))
hasattr(x, '__iter__')Podejście OP powie tak dla ciągów w Pythonie 3 i nie w Pythonie 2 (bez względu na to, ''czy b''lub u''). Dzięki @LuisMasuelli za zauważenie, że zawiedzie Cię również na buggy __iter__.
Najłatwiejszym sposobem, uwzględniając typ kaczki Pythona , jest złapanie błędu (Python doskonale wie, czego oczekuje od obiektu, aby stał się iteratorem):
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())
Uwagi :
Nie ma znaczenia rozróżnienie, czy obiekt nie jest iterowalny, czy __iter__zaimplementowano buggy , jeśli typ wyjątku jest taki sam: w każdym razie nie będzie można iterować obiektu.
Wydaje mi się, że rozumiem twoją troskę: W jaki sposób callableistnieje sprawdzanie, czy mogę polegać na pisaniu kaczek, aby podnieść AttributeErrorif, który __call__nie jest zdefiniowany dla mojego obiektu, ale nie dotyczy to sprawdzania iteracyjnego?
Nie znam odpowiedzi, ale możesz albo zaimplementować funkcję, którą dałem (i innym użytkownikom), albo po prostu złapać wyjątek w kodzie (implementacja w tej części będzie podobna do funkcji, którą napisałem - po prostu upewnij się, że izolujesz tworzenie iteratora z pozostałej części kodu, dzięki czemu można przechwycić wyjątek i odróżnić go od innego 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
tak wiele szczegółowych odpowiedzi powyżej z wieloma opiniami i wrzucasz niewyjaśnioną odpowiedź ... meh
Nrzonline
Nie publikuj samego kodu. Objaśnij także, co to robi.
Jonathan Mee,
1
Zamiast sprawdzać __iter__atrybut, można sprawdzić __len__atrybut, który jest implementowany przez każdą iterowalną wbudowaną wersję Pythona, w tym ciągi znaków.
Obiekty, których nie da się powtórzyć, nie wdrożyłyby tego z oczywistych powodów. Jednak nie przechwytuje iterałów zdefiniowanych przez użytkownika, które go nie implementują, ani nie wyraża wyrażeń generatora, z którymi itermożna sobie poradzić. Można to jednak zrobić w linii, a dodanie prostego orsprawdzania wyrażeń dla generatorów rozwiązałoby ten problem. (Pamiętaj, że pisanie type(my_generator_expression) == generatorrzuciłoby a NameError. Zamiast tego zapoznaj się z tą odpowiedzią)
Możesz użyć GeneratorType z typów:
>>>import types
>>> types.GeneratorType<class'generator'>>>> gen =(i for i in range(10))>>> isinstance(gen, types.GeneratorType)True
--- zaakceptowana odpowiedź przez utdemir
(Przydaje się to jednak do sprawdzania, czy można wywołać lenobiekt).
niestety nie wszystkie iterowalne obiekty używają __len__... w tym przypadku zwykle jest to niewłaściwe użycie obliczania odległości między 2 obiektami. gdzie obj.dist()można łatwo zastąpić.
Tcll,
Tak. Większość zdefiniowanych przez użytkownika iteratów implementuje iter i getitem, ale nie len. Jednak wbudowane typy tak, a jeśli chcesz sprawdzić, czy możesz wywołać na nim funkcję len, sprawdzanie len jest bezpieczniejsze. Ale masz rację.
DarthCadeus
0
Niezupełnie „poprawne”, ale może służyć jako szybkie sprawdzenie najbardziej popularnych typów, takich jak łańcuchy, krotki, zmiennoprzecinkowe itp.
Trochę późno na imprezę, ale zadałem sobie to pytanie i zobaczyłem to, a potem pomyślałem o odpowiedzi. Nie wiem, czy ktoś to już opublikował. Zasadniczo zauważyłem jednak, że wszystkie typy iterowalne mają w swoim słowniku __getitem __ () . W ten sposób można sprawdzić, czy obiekt był iterowalny, nawet nie próbując. (Kalambur przeznaczony)
__getitem__
wystarcza również, aby obiekt mógł być iterowalnyiter(myObj)
jeśli się udaisinstance(myObj, dict)
, więc jeśli spojrzyszmyObj
na sekwencjędict
s lub pojedyncządict
, odniesiesz sukces w obu przypadkach. Subtelność, która jest ważna, jeśli chcesz wiedzieć, co jest sekwencją, a co nie. (w Python 2)__getitem__
wystarcza również, aby obiekt był iterowalny ... jeśli zaczyna się od zera indeksu .Odpowiedzi:
Ostatnio dość często studiowałem ten problem. Na tej podstawie stwierdzam, że obecnie jest to najlepsze podejście:
Powyższe zalecono już wcześniej, ale ogólny konsensus jest taki, że stosowanie
iter()
byłoby lepsze:Użyliśmy również
iter()
w naszym kodzie do tego celu, ale ostatnio zacząłem coraz bardziej denerwować się obiektami, które tylko__getitem__
uważano za iterowalne. Istnieją ważne powody, by mieć obiekt,__getitem__
którego nie da się iterować, a powyższy kod nie działa dobrze. Jako przykład z życia możemy użyć Fakera . Powyższy kod zgłasza, że jest iterowalny, ale w rzeczywistości próba iteracji powodujeAttributeError
(testowany z Faker 4.0.2):Gdybyśmy tego użyli
insinstance()
, przypadkowo nie uznalibyśmy instancji Fakera (ani żadnych innych obiektów posiadających tylko__getitem__
) za iterowalne:Wcześniejsze odpowiedzi skomentowały, że używanie
iter()
jest bezpieczniejsze, ponieważ opierał się na starym sposobie implementacji iteracji w Pythonie__getitem__
iisinstance()
podejście to nie wykrywa. Mogło to być prawdą w przypadku starych wersji Pythona, ale w oparciu o moje dość wyczerpujące testyisinstance()
działają obecnie świetnie. Jedyny przypadek, w którymisinstance()
nie działał, ale działał,iter()
dotyczyłUserDict
używania Pythona 2. Jeśli jest to istotne, można skorzystaćisinstance(item, (Iterable, UserDict))
z tej opcji.źródło
typing.Dict
Jest również uważany za iterowalny,iter(Dict)
alelist(Dict)
kończy się błędemTypeError: Parameters to generic types must be types. Got 0.
. Zgodnie z oczekiwaniamiisinstance(Dict, Iterable)
zwraca false.iter
spowodowało, że część naszego kodu, który używał „buforowania wstępnego”, niepotrzebnie zwalniała. Jeśli__iter__
kod jest wolny, zadzwoniiter
... za każdym razem, gdy chcesz tylko sprawdzić, czy coś da się powtórzyć.Sprawdzanie, czy
__iter__
działa na typach sekwencji, ale nie powiedzie się np. Na łańcuchach w Pythonie 2 . Chciałbym również znać właściwą odpowiedź, do tego czasu jest jedna możliwość (która również działałaby na ciągach):Te
iter
wbudowane kontrole na__iter__
metodzie lub w przypadku strun do__getitem__
metody.Innym ogólnym podejściem pythonowym jest założenie, że jest iterowalny, a następnie kończy się niepowodzeniem, jeśli nie działa na danym obiekcie. Glosariusz Pythona:
collections
Moduł dostarcza pewnych abstrakcyjnych klas bazowych, które pozwalają zadać klas lub instancji, jeżeli zapewniają one szczególną funkcję, na przykład:Nie sprawdza to jednak klas, które można iterować
__getitem__
.źródło
[e for e in my_object]
może zgłosić wyjątek z innych powodów, tj.my_object
jest niezdefiniowany lub możliwe błędy wemy_object
wdrażaniu.isinstance('', Sequence) == True
) i dowolną sekwencją jest iterowalna (isinstance('', Iterable)
). Chociażhasattr('', '__iter__') == False
może to być mylące.my_object
jest bardzo duży (powiedzmy, nieskończony jakitertools.count()
), zrozumienie listy zajmie dużo czasu / pamięci. Lepiej jest stworzyć generator, który nigdy nie będzie próbował zbudować listy (potencjalnie nieskończonej).hasattr(u"hello", '__iter__')
zwracaTrue
Pisanie kaczek
Sprawdzanie typu
Użyj abstrakcyjnych klas podstawowych . Potrzebują co najmniej Pythona 2.6 i działają tylko w przypadku klas nowego stylu.
Jednakże,
iter()
jest nieco bardziej wiarygodne, jak to opisano w dokumentacji :źródło
isinstance(x, (collections.Iterable, collections.Sequence))
zamiastiter(x)
”, pamiętaj, że nadal nie wykryje iterowalnego obiektu, który implementuje tylko,__getitem__
ale nie__len__
. Użyjiter(x)
i złap wyjątek.iter(slide1)
, wszystko pójdzie dobrze, aleisinstance(slide1, Iterable)
rzutyTypeError: issubclass() arg 1 must be a class
.PyUNO
Zauważ, żeissubclass()
zamiast tego wyświetlany jest komunikat o błędzieisinstance()
.Chciałbym rzucić nieco więcej światła na grę
iter
,__iter__
a__getitem__
i co dzieje się za kurtyną. Uzbrojony w tę wiedzę będziesz w stanie zrozumieć, dlaczego najlepiej możesz to zrobićNajpierw wymienię fakty, a następnie szybko przypomnę, co się stanie, gdy zastosujesz
for
pętlę w pythonie, a następnie przeprowadzimy dyskusję ilustrującą fakty.Fakty
Możesz pobrać iterator z dowolnego obiektu
o
, wywołując,iter(o)
jeśli spełniony jest przynajmniej jeden z następujących warunków:a)
o
ma__iter__
metodę, która zwraca obiekt iteratora. Iteratorem jest dowolny obiekt z metodą__iter__
i__next__
(Python 2next
:).b)
o
ma__getitem__
metodę.Sprawdzanie instancji
Iterable
lubSequence
lub kontroli dla atrybutu__iter__
nie jest wystarczające.Jeśli obiekt
o
implementuje tylko__getitem__
, ale nie__iter__
,iter(o)
skonstruuje iterator, który spróbuje pobrać elementy zo
indeksu liczb całkowitych, zaczynając od indeksu 0. Iterator wyłapie wszystkieIndexError
(ale żadnych innych błędów), które zostaną podniesione, a następnieStopIteration
sam się podniesie .W najogólniejszym sensie nie ma sposobu, aby sprawdzić, czy powracający iterator
iter
jest zdrowy, poza wypróbowaniem go.Jeśli obiekt zostanie
o
zaimplementowany__iter__
,iter
funkcja upewni się, że zwracany przez niego obiekt__iter__
jest iteratorem. Nie ma kontroli poprawności, jeśli obiekt tylko implementuje__getitem__
.__iter__
wygrywa Jeśli obiekto
implementuje oba__iter__
i__getitem__
,iter(o)
wywoła__iter__
.Jeśli chcesz, aby własne obiekty były iterowalne, zawsze implementuj
__iter__
metodę.for
pętleAby kontynuować, musisz zrozumieć, co się dzieje, gdy zastosujesz
for
pętlę w Pythonie. Jeśli już wiesz, możesz przejść do następnej sekcji.Kiedy używasz
for item in o
dla jakiegoś iterowalnego obiektuo
, Python wywołujeiter(o)
i oczekuje obiektu iteratora jako wartości zwracanej. Iterator to dowolny obiekt, który implementuje metodę__next__
(lubnext
w Pythonie 2) i__iter__
metodę.Zgodnie z konwencją
__iter__
metoda iteratora powinna zwrócić sam obiekt (tjreturn self
.). Python następnie wywołujenext
iterator, ażStopIteration
zostanie podniesiony. Wszystko to dzieje się niejawnie, ale poniższa demonstracja pokazuje:Iteracja po
DemoIterable
:Dyskusja i ilustracje
W punktach 1 i 2: uzyskanie iteratora i nierzetelne kontrole
Rozważ następującą klasę:
Wywołanie
iter
z instancjąBasicIterable
zwróci iterator bez żadnych problemów, ponieważBasicIterable
implementuje__getitem__
.Należy jednak zauważyć, że
b
nie ma tego__iter__
atrybutu i nie jest uważany za instancjęIterable
lubSequence
:Właśnie dlatego Fluent Python autorstwa Luciano Ramalho zaleca wywoływanie
iter
i zarządzanie potencjałemTypeError
jako najdokładniejszy sposób sprawdzenia, czy obiekt jest iterowalny. Cytując bezpośrednio z książki:W punkcie 3: Iterowanie nad obiektami, które tylko zapewniają
__getitem__
, ale nie zapewniają__iter__
Iteracja po instancji
BasicIterable
działa zgodnie z oczekiwaniami: Python konstruuje iterator, który próbuje pobierać elementy według indeksu, zaczynając od zera, aż doIndexError
podniesienia wartości. Metoda obiektu demonstracyjnego__getitem__
zwraca po prostu to,item
co podano jako argument__getitem__(self, item)
zwracany przez iteratoriter
.Zauważ, że iterator podnosi się,
StopIteration
gdy nie może zwrócić następnego elementu, a ten, dlaIndexError
którego podniesiono,item == 3
jest obsługiwany wewnętrznie. Oto dlaczego zapętlenieBasicIterable
zfor
pętlą działa zgodnie z oczekiwaniami:Oto kolejny przykład, który pokazuje, w jaki sposób iterator wrócił przez
iter
próby uzyskania dostępu do elementów według indeksu.WrappedDict
nie dziedziczy podict
, co oznacza, że instancje nie będą miały__iter__
metody.Zauważ, że wywołania do
__getitem__
są delegowane,dict.__getitem__
dla których notacja w nawiasach kwadratowych jest po prostu skrótem.W punktach 4 i 5:
iter
sprawdza iterator, gdy wywołuje__iter__
:Kiedy
iter(o)
zostanie wywołany dla obiektuo
,iter
upewni się, że zwracana wartość__iter__
, jeśli metoda jest obecna, jest iteratorem. Oznacza to, że zwracany obiekt musi implementować__next__
(lubnext
w Pythonie 2) i__iter__
.iter
nie może wykonać żadnych kontroli poczytalności dla obiektów, które tylko zapewniają__getitem__
, ponieważ nie ma możliwości sprawdzenia, czy elementy obiektu są dostępne przez indeks liczb całkowitych.Zauważ, że konstruowanie iteratora z
FailIterIterable
instancji kończy się niepowodzeniem natychmiast, podczas gdy konstruowanie iteratora zFailGetItemIterable
powodzeniem, ale generuje wyjątek przy pierwszym wywołaniu do__next__
.W punkcie 6:
__iter__
wygrywaTen jest prosty. Jeśli obiekt zaimplementuje
__iter__
i__getitem__
,iter
zadzwoni__iter__
. Rozważ następującą klasęoraz dane wyjściowe podczas zapętlania instancji:
W punkcie 7: twoje klasy iterowalne powinny zaimplementować
__iter__
Możesz zadać sobie pytanie, dlaczego większość wbudowanych sekwencji, takich jak
list
implementacja__iter__
metody__getitem__
, jest wystarczająca.Po tym wszystkim, iteracja przez instancje klasy wyżej, który deleguje do połączenia
__getitem__
sięlist.__getitem__
(za pomocą notacji nawiasu kwadratowego), będzie działać prawidłowo:Powody, dla których niestandardowe iteratory powinny zostać zaimplementowane,
__iter__
są następujące:__iter__
, instancje zostaną uznane za iterowalne iisinstance(o, collections.abc.Iterable)
zostaną zwróconeTrue
.__iter__
nie jest iteratorem,iter
natychmiast zawiedzie i podniesie wartośćTypeError
.__getitem__
istnieje ze względu na kompatybilność wsteczną. Cytując ponownie z płynnego Pythona:źródło
is_iterable
powracającTrue
wtry
bloku iFalse
wexcept TypeError
bloku?To nie wystarcza: obiekt zwracany przez
__iter__
musi implementować protokół iteracji (tj.next
Metodę). Zobacz odpowiedni rozdział w dokumentacji .W Pythonie dobrą praktyką jest „spróbuj i zobacz” zamiast „sprawdzania”.
źródło
W Pythonie <= 2.5 nie możesz i nie powinieneś - iterowalny był „nieformalny” interfejs.
Ale od wersji Python 2.6 i 3.0 możesz wykorzystać nową infrastrukturę ABC (abstrakcyjna klasa bazowa) wraz z niektórymi wbudowanymi ABC, które są dostępne w module kolekcji:
To, czy jest to pożądane, czy faktycznie działa, jest tylko kwestią konwencji. Jak widać, to można zarejestrować bez iterable obiektu jako iterable - i to podniesie wyjątek w czasie wykonywania. Dlatego isinstance nabiera „nowego” znaczenia - po prostu sprawdza zgodność z „zadeklarowanym” typem, co jest dobrym sposobem na przejście do Pythona.
Z drugiej strony, jeśli twój obiekt nie spełnia potrzebnego interfejsu, co zamierzasz zrobić? Weź następujący przykład:
Jeśli obiekt nie spełnia oczekiwań, po prostu generujesz błąd typu TypeError, ale jeśli zarejestrowano prawidłowe ABC, czek jest nieużyteczny. Przeciwnie, jeśli
__iter__
metoda jest dostępna, Python automatycznie rozpozna obiekt tej klasy jako Iterowalny.Jeśli więc spodziewasz się iteracji, iteruj ją i zapomnij. Z drugiej strony, jeśli musisz robić różne rzeczy w zależności od typu danych wejściowych, infrastruktura ABC może okazać się bardzo przydatna.
źródło
except:
w przykładowym kodzie dla początkujących. Promuje złe praktyki.Nie sprawdzaj,
czy twoja kaczka naprawdę jest kaczką,czy jest iterowalna, czy nie, traktuj ją tak, jakby była, i narzekaj, jeśli nie była.źródło
TypeError
i rzucić cię tutaj, ale w zasadzie tak.as e
” poTypeError
zamiast dodania „, e
”?Od wersji Python 3.5 możesz używać modułu do pisania ze standardowej biblioteki do rzeczy związanych z typem:
źródło
Najlepsze rozwiązanie, jakie do tej pory znalazłem:
hasattr(obj, '__contains__')
który w zasadzie sprawdza, czy obiekt implementuje
in
operator.Zalety (żadne z pozostałych rozwiązań nie ma wszystkich trzech):
__iter__
)Uwagi:
źródło
Możesz spróbować:
Jeśli możemy stworzyć generator, który będzie iterował nad nim (ale nigdy nie będzie używać generatora, aby nie zajmował miejsca), będzie można go powtarzać. Wygląda na coś w rodzaju „duh”. Dlaczego musisz przede wszystkim ustalić, czy zmienna jest iterowalna?
źródło
iterable(itertools.repeat(0))
? :)(x for x in a)
prostu tworzy generator, nie wykonuje żadnej iteracji na.(x for x in a)
dokładnie równoważna z próbąiterator = iter(a)
? Czy w niektórych przypadkach oba są różne?for _ in a: break
jest prostsze? Czy to wolniej?Znalazłem ładne rozwiązanie tutaj :
źródło
Zgodnie ze Słowniczkiem Python 2 iterowalne są
Oczywiście, biorąc pod uwagę ogólny styl kodowania dla Pythona w oparciu o fakt, że „łatwiej prosić o wybaczenie niż o pozwolenie”, ogólnie oczekuje się, że użyje
Ale jeśli musisz to wyraźnie sprawdzić, możesz przetestować iterację przez
hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Musisz sprawdzić oba, ponieważstr
nie mają__iter__
metody (przynajmniej nie w Pythonie 2, w Pythonie 3 mają) i ponieważgenerator
obiekty nie mają__getitem__
metody.źródło
Często w moich skryptach wygodne jest definiowanie
iterable
funkcji. (Teraz obejmuje sugerowane uproszczenie Alfe):dzięki czemu możesz przetestować, czy dowolny obiekt jest iterowalny w bardzo czytelnej formie
tak jak w przypadku tej
callable
funkcjiEDYCJA: jeśli masz zainstalowany numpy, możesz po prostu zrobić: z
numpy import iterable
, co jest po prostu czymś podobnymJeśli nie masz numpy, możesz po prostu zaimplementować ten kod lub powyższy.
źródło
if x: return True
else: return False
(x
bo-boolean), możesz napisać to jakoreturn x
. W twoim przypadkureturn isinstance(…)
bez żadnychif
.pandy ma taką wbudowaną funkcję:
źródło
__iter__
tak naprawdę chodzi o sekwencje i tym podobne.Zawsze wymykało mi się pytanie, dlaczego python ma,
callable(obj) -> bool
ale nieiterable(obj) -> bool
... zpewnością łatwiej to zrobić,
hasattr(obj,'__call__')
nawet jeśli jest wolniejszy.Ponieważ prawie co druga odpowiedź zaleca używanie
try
/except TypeError
, gdzie testowanie wyjątków jest ogólnie uważane za złą praktykę w dowolnym języku, oto implementacjaiterable(obj) -> bool
, którą polubiłem i używam często:Dla dobra Pythona 2 użyję lambda tylko dla tego dodatkowego zwiększenia wydajności ...
(w Pythonie 3 nie ma znaczenia, czego użyjesz do zdefiniowania funkcji,
def
ma mniej więcej taką samą prędkość jaklambda
)Zauważ, że ta funkcja działa szybciej dla obiektów,
__iter__
ponieważ nie sprawdza__getitem__
.Większość iterowalnych obiektów powinna polegać na tym, do
__iter__
czego wracają obiekty ze specjalnych przypadków__getitem__
, chociaż jest to wymagane, aby obiekt był iterowalny.(a ponieważ jest to standard, wpływa również na obiekty C)
źródło
To powie tak dla wszystkich rodzajów iterowalnych obiektów, ale powie nie dla łańcuchów w Pythonie 2 . (Tego właśnie chcę na przykład, gdy funkcja rekurencyjna może wziąć ciąg znaków lub kontener ciągów znaków. W takiej sytuacji prośba o wybaczenie może prowadzić do obfuscode i lepiej najpierw poprosić o pozwolenie).
Wiele innych strategii mówi tak dla łańcuchów. Użyj ich, jeśli tego chcesz.
Uwaga: is_iterable () powie tak na ciągi znaków typu
bytes
ibytearray
.bytes
obiekty w Pythonie 3 są iterowalneTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
W Pythonie 2 nie ma takiego typu.bytearray
obiekty w Pythonie 2 i 3 są iterowalneTrue == is_iterable(bytearray(b"abc"))
hasattr(x, '__iter__')
Podejście OP powie tak dla ciągów w Pythonie 3 i nie w Pythonie 2 (bez względu na to,''
czyb''
lubu''
). Dzięki @LuisMasuelli za zauważenie, że zawiedzie Cię również na buggy__iter__
.źródło
Najłatwiejszym sposobem, uwzględniając typ kaczki Pythona , jest złapanie błędu (Python doskonale wie, czego oczekuje od obiektu, aby stał się iteratorem):
Uwagi :
__iter__
zaimplementowano buggy , jeśli typ wyjątku jest taki sam: w każdym razie nie będzie można iterować obiektu.Wydaje mi się, że rozumiem twoją troskę: W jaki sposób
callable
istnieje sprawdzanie, czy mogę polegać na pisaniu kaczek, aby podnieśćAttributeError
if, który__call__
nie jest zdefiniowany dla mojego obiektu, ale nie dotyczy to sprawdzania iteracyjnego?Nie znam odpowiedzi, ale możesz albo zaimplementować funkcję, którą dałem (i innym użytkownikom), albo po prostu złapać wyjątek w kodzie (implementacja w tej części będzie podobna do funkcji, którą napisałem - po prostu upewnij się, że izolujesz tworzenie iteratora z pozostałej części kodu, dzięki czemu można przechwycić wyjątek i odróżnić go od innego
TypeError
.źródło
Funkcja
isiterable
w poniższym kodzie zwraca,True
jeśli obiekt jest iterowalny. jeśli nie jest to iterowalne zwrotyFalse
przykład
źródło
Zamiast sprawdzać
__iter__
atrybut, można sprawdzić__len__
atrybut, który jest implementowany przez każdą iterowalną wbudowaną wersję Pythona, w tym ciągi znaków.Obiekty, których nie da się powtórzyć, nie wdrożyłyby tego z oczywistych powodów. Jednak nie przechwytuje iterałów zdefiniowanych przez użytkownika, które go nie implementują, ani nie wyraża wyrażeń generatora, z którymi
iter
można sobie poradzić. Można to jednak zrobić w linii, a dodanie prostegoor
sprawdzania wyrażeń dla generatorów rozwiązałoby ten problem. (Pamiętaj, że pisanietype(my_generator_expression) == generator
rzuciłoby aNameError
. Zamiast tego zapoznaj się z tą odpowiedzią)(Przydaje się to jednak do sprawdzania, czy można wywołać
len
obiekt).źródło
__len__
... w tym przypadku zwykle jest to niewłaściwe użycie obliczania odległości między 2 obiektami. gdzieobj.dist()
można łatwo zastąpić.Niezupełnie „poprawne”, ale może służyć jako szybkie sprawdzenie najbardziej popularnych typów, takich jak łańcuchy, krotki, zmiennoprzecinkowe itp.
źródło
Trochę późno na imprezę, ale zadałem sobie to pytanie i zobaczyłem to, a potem pomyślałem o odpowiedzi. Nie wiem, czy ktoś to już opublikował. Zasadniczo zauważyłem jednak, że wszystkie typy iterowalne mają w swoim słowniku __getitem __ () . W ten sposób można sprawdzić, czy obiekt był iterowalny, nawet nie próbując. (Kalambur przeznaczony)
źródło