W Pythonie, jak określić, czy obiekt jest iteracyjny?

Czy istnieje taka metoda jak isiterable? Jedyne rozwiązanie, jakie do tej pory znalazłem, to wywołanie

hasattr(myObj, '__iter__')
Ale nie jestem pewien, czy to jest idiotyczne.
Author: John Kugelman, 2009-12-23

19 answers

  1. Sprawdzanie __iter__działa na typach sekwencji, ale zawiedzie np. na łańcuchach w Pythonie 2. Ja też chciałbym znać poprawną odpowiedź, do tego czasu jest jedna możliwość (która też by działała na strunach):

    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print some_object, 'is not iterable'
    

    Wbudowana kontrola iter dla metody __iter__ lub w przypadku łańcuchów metoda __getitem__.

  2. Innym ogólnym podejściem pythonicznym jest założenie iterowalnego, a następnie niepowodzenie, jeśli nie działa na danym obiekcie. Słowniczek Pythona:

    Styl programowania Pythonicznego, który określa typ obiektu poprzez sprawdzenie jego metody lub sygnatury atrybutów, 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."), Kładąc nacisk na Interfejsy, a nie konkretne typy, dobrze zaprojektowany kod poprawia jego elastyczność, pozwalając na podstawienie polimorficzne. Duck-typing unika testów za pomocą type () lub isinstance (). zamiast tego, zwykle używa stylu eafp (łatwiej prosić o przebaczenie niż pozwolenie).

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
    
  3. The collections moduł zawiera kilka abstrakcyjnych klas bazowych, które pozwalają zapytać klasy lub instancje, czy zapewniają określoną funkcjonalność, na przykład:

    import collections
    
    if isinstance(e, collections.Iterable):
        # e is iterable
    

    To jednak nie sprawdza klas, które są iteracyjne przez __getitem__.

 668
Author: miku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-13 11:13:23

Kaczka typowanie

try:
    iterator = iter(theElement)
except TypeError:
    # not iterable
else:
    # iterable

# for obj in iterator:
#     pass

Sprawdzanie typu

Użyj abstrakcyjnych klas bazowych. Potrzebują co najmniej Pythona 2.6 i działają tylko dla klas nowego stylu.

from collections.abc import Iterable   # import directly from collections for Python < 3.3

if isinstance(theElement, Iterable):
    # iterable
else:
    # not iterable

Jednak iter() jest nieco bardziej wiarygodne, jak opisano w dokumentacji :

Sprawdzanie isinstance(obj, Iterable) wykrywa klasy, które są zarejestrowane jako Iterowalne lub posiadające metodę __iter__(), ale nie wykrywa klas iteracyjnych __getitem__() metoda. Jedyny niezawodny sposób na określić, czy obiekt is iterable is to call iter(obj).

 495
Author: Georg Schölly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-04 12:08:05

Chciałbym rzucić trochę więcej światła na wzajemne oddziaływanie iter, __iter__ i __getitem__ i to, co dzieje się za zasłoną. Uzbrojony w tę wiedzę, będziesz w stanie zrozumieć, dlaczego najlepsze, co możesz zrobić, to [138]}

try:
    iter(maybe_iterable)
    print('iteration will probably work')
except TypeError:
    print('not iterable')

Najpierw wymienię fakty, a następnie Przypomnę, co się dzieje, gdy użyjesz pętli for w Pythonie, a następnie omówię je w celu zilustrowania faktów.

Fakty

  1. Iterator można uzyskać z dowolny obiekt o przez wywołanie iter(o), jeśli spełniony jest co najmniej jeden z poniższych warunków:

    a) o posiada metodę __iter__, która zwraca obiekt iterator. Iterator to dowolny obiekt z metodą __iter__ i __next__ (Python 2: next).

    b) o mA metodę __getitem__.

  2. Sprawdzanie instancji Iterable lub Sequence, lub sprawdzanie atrybut {[17] } nie wystarczy.

  3. Jeśli obiekt o implementuje tylko __getitem__, ale Nie __iter__, iter(o) będzie konstruować iterator, który próbuje pobrać elementy z o za pomocą indeksu integer, zaczynając od indeksu 0. Iterator wychwytuje każdy IndexError (ale żadnych innych błędów), który zostanie podniesiony, a następnie sam podniesie StopIteration.

  4. W najbardziej ogólnym sensie, nie ma innego sposobu, aby sprawdzić, czy iterator zwrócony przez iter jest zdrowy, niż wypróbować go.

  5. Jeśli obiekt o implementuje __iter__, funkcja iter zapewni że obiekt zwracany przez __iter__ jest iteratorem. Nie ma kontroli poczytalności jeśli obiekt implementuje tylko __getitem__.

  6. __iter__ wygrywa. Jeśli obiekt o implementuje zarówno __iter__, jak i __getitem__, iter(o) will call __iter__.

  7. Jeśli chcesz, aby własne obiekty były iterowalne, zawsze zaimplementuj metodę __iter__.

for pętle

Aby podążać dalej, musisz zrozumieć, co się dzieje, gdy używasz pętli for w Pythonie. Jeśli już wiesz, możesz przejść do następnej sekcji.

Kiedy używasz for item in o dla jakiegoś obiektu iteracyjnego o, Python wywołuje iter(o) i oczekuje obiektu iteracyjnego jako zwracanej wartości. Iterator to dowolny obiekt implementujący metodę __next__ (lub next w Pythonie 2) oraz metodę __iter__.

Zgodnie z konwencją, metoda __iter__ iteratora powinna zwracać sam obiekt (np. return self). Python następnie wywołuje next na iteratorze, dopóki StopIteration nie zostanie wywołane. Wszystko to jest to spowodowane tym, że nie jest to możliwe.]}

import random

class DemoIterable(object):
    def __iter__(self):
        print('__iter__ called')
        return DemoIterator()

class DemoIterator(object):
    def __iter__(self):
        return self

    def __next__(self):
        print('__next__ called')
        r = random.randint(1, 10)
        if r == 5:
            print('raising StopIteration')
            raise StopIteration
        return r

Iteracja nad 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

Dyskusja i ilustracje

W punkcie 1 i 2: uzyskanie iteratora i niepewne kontrole

Rozważmy następującą klasę:

class BasicIterable(object):
    def __getitem__(self, item):
        if item == 3:
            raise IndexError
        return item

Wywołanie iter z instancją BasicIterable zwróci iterator bez żadnych problemów, ponieważ BasicIterable implementuje __getitem__.

>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>

Jednak ważne jest, aby należy zauważyć, że b nie ma atrybutu __iter__ i nie jest uważany za instancję Iterable lub Sequence:

>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False

Dlatego Fluent Python Luciano Ramalho zaleca wywołanie iter i obsługę potencjału TypeError jako najdokładniejszy sposób sprawdzenia, czy obiekt jest iteracyjny. Cytowanie bezpośrednio z książki:

W Pythonie 3.4 najdokładniejszym sposobem sprawdzenia, czy obiekt x jest iteracyjny, jest wywołanie iter(x) i obsługa TypeError wyjątek , jeśli nie jest. jest to dokładniejsze niż użycie isinstance(x, abc.Iterable), ponieważ iter(x) uwzględnia również metodę legacy __getitem__, podczas gdy Iterable ABC nie.

W punkcie 3: iteracja nad obiektami, które dostarczają tylko __getitem__, ale nie __iter__

Iteracja nad instancją BasicIterable działa zgodnie z oczekiwaniami: Python tworzy iterator, który próbuje pobrać elementy według indeksu, zaczynając od zera, aż do wywołania IndexError. Metoda __getitem__ obiektu demo po prostu zwraca item, który został dostarczony jako argument do __getitem__(self, item) przez iterator zwracany przez 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 StopIteration, gdy nie może zwrócić następnego elementu i że IndexError, który jest podniesiony dla item == 3 jest obsługiwany wewnętrznie. Dlatego pętla BasicIterable z pętlą for działa zgodnie z oczekiwaniami:

>>> for x in b:
...     print(x)
...
0
1
2

Oto kolejny przykład, w jaki sposób iterator zwracany przez iter próbuje uzyskać dostęp do elementów za pomocą indeksu. WrappedDict Nie dziedziczenie z dict, co oznacza, że instancje nie będą miały metody __iter__.

class WrappedDict(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__
        except KeyError:
            raise IndexError

Zwróć uwagę, że wywołania do {[18] } są delegowane do dict.__getitem__, dla których zapis z nawiasem kwadratowym 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)
... 
hi
StackOverflow
!

W punkcie 4 i 5: iter sprawdza, czy iterator wywołuje __iter__:

Gdy iter(o) jest wywołana dla obiektu o, iter upewni się, że wartość zwracana __iter__, jeśli metoda jest obecna, jest iteratorem. Oznacza to, że zwrócony obiekt musi zaimplementować __next__ (lub next w Pythonie 2) i __iter__. iter nie można przeprowadzać żadnych kontroli zdrowego rozsądku dla obiektów, które tylko podaj __getitem__, ponieważ nie ma możliwości sprawdzenia, czy pozycje obiektu są dostępne przy pomocy indeksu integer.

class FailIterIterable(object):
    def __iter__(self):
        return object() # not an iterator

class FailGetitemIterable(object):
    def __getitem__(self, item):
        raise Exception

Zauważ, że konstruowanie iteratora z instancji FailIterIterable nie powiedzie się natychmiast, podczas gdy konstruowanie iteratora z instancji FailGetItemIterable powiedzie się, ale rzuci 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__
    raise Exception
Exception

W sprawie punktu 6: __iter__ wygrane

Ten jest prosty. Jeśli obiekt implementuje __iter__ i __getitem__, iter will call __iter__. Rozważmy następującą klasę

class IterWinsDemo(object):
    def __iter__(self):
        return iter(['__iter__', 'wins'])

    def __getitem__(self, item):
        return ['__getitem__', 'wins'][item]

I wyjście podczas zapętlania instancji:

>>> iwd = IterWinsDemo()
>>> for x in iwd:
...     print(x)
...
__iter__
wins

W punkcie 7: Twoje klasy iteracyjne powinny wdrożyć __iter__

Możesz zadać sobie pytanie, dlaczego większość wbudowanych sekwencji, takich jak list implementuje metodę __iter__, gdy __getitem__ byłaby wystarczająca.

class WrappedList(object): # note: no inheritance from list!
    def __init__(self, lst):
        self._list = lst

    def __getitem__(self, item):
        return self._list[item]

Mimo wszystko, iteracja nad instancjami powyższej klasy, które delegaty wywołują __getitem__ do list.__getitem__ (używając notacji z nawiasem kwadratowym), będzie działać poprawnie:

>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
...     print(x)
... 
A
B
C
W tym celu należy wykonać następujące czynności:]}
  1. jeśli zaimplementujesz __iter__, instancje będą uważane za iterable, a isinstance(o, collections.Iterable) zwrócą True.
  2. jeśli obiekt zwrócony przez __iter__ nie jest iteratorem, iter zawiedzie natychmiast i wywoła TypeError.
  3. The Specjalna obsługa __getitem__ istnieje ze względu na wsteczną kompatybilność. Cytowanie ponownie z płynnego Pythona:

Dlatego każda sekwencja Pythona jest iteracyjna: wszystkie implementują __getitem__ . W rzeczywistości, standardowe sekwencje również implementują __iter__, a Twoje też powinny, ponieważ Specjalna obsługa __getitem__ istnieje ze względu na wsteczną kompatybilność i może być gone In the future (chociaż nie jest to przestarzałe, jak to piszę).

 62
Author: timgeb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-04 16:04:01

To nie wystarczy: obiekt zwracany przez {[0] } musi zaimplementować protokół iteracji (tzn. metodę next). Zob. odpowiednią sekcję w Dokumentacja .

W Pythonie dobrą praktyką jest "próbować zobaczyć" zamiast "sprawdzać".

 28
Author: jldupont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-27 17:01:02
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

Nie sprawdzaj, czy Twoja kaczka naprawdę jest kaczką aby sprawdzić, czy jest iteracyjna, czy nie, traktuj ją tak, jakby była i narzekaj, jeśli nie była.

 18
Author: badp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-23 12:28:00

Najlepsze rozwiązanie jakie do tej pory znalazłem:

hasattr(obj, '__contains__')

, który zasadniczo sprawdza, czy obiekt implementuje operator in.

Zalety (żadne z innych rozwiązań nie ma wszystkich trzech):

  • jest to wyrażenie (działa jako lambda , W przeciwieństwie do try...z wyjątkiem variant)
  • Jest (powinna być) zaimplementowana przez wszystkie iterable, w tym stringi (W przeciwieństwie do __iter__)
  • Działa na dowolnym Pythonie >= 2.5

Uwagi:

  • Filozofia Pythona "proś o przebaczenie, a nie pozwolenie" nie działa dobrze, gdy np. na liście masz zarówno iteraby, jak i nie-iteraby i musisz traktować każdy element inaczej w zależności od jego typu (traktowanie iterabli na try i nie-iterabli na except będzie działać, ale będzie wyglądać brzydko i myląco)
  • rozwiÄ ... zania tego problemu, ktĂłre prĂłbujÄ ... faktycznie iterowaä ‡ nad obiektem (np. [X dla x w obj]) aby sprawdzić, czy iterable jest iterable, może to spowodować znaczące kary za wydajność dla dużych iterabli (zwłaszcza jeśli potrzebujesz tylko kilku pierwszych elementów iterable, na przykład) i należy go unikać
 16
Author: Vlad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-18 15:47:00

W Pythonie

Ale od wersji Python 2.6 i 3.0 można wykorzystać nową infrastrukturę ABC (abstract base class) wraz z niektórymi wbudowanymi ABC, które są dostępne w module collections:

from collections import Iterable

class MyObject(object):
    pass

mo = MyObject()
print isinstance(mo, Iterable)
Iterable.register(MyObject)
print isinstance(mo, Iterable)

print isinstance("abc", Iterable)
Teraz, czy jest to pożądane, czy faktycznie działa, jest tylko kwestią konwencji. Jak widzisz, możesz zarejestrować obiekt nie-iterowalny jako Iterowalny - i spowoduje to wywołanie wyjątku w czasie wykonywania. W związku z tym isinstance nabiera "nowego" znaczenia - sprawdza tylko zgodność "zadeklarowanego" typu, co jest dobrym rozwiązaniem w Pythonie.

Z drugiej strony, jeśli twój obiekt nie spełnia potrzebnego interfejsu, co zamierzasz zrobić? Weźmy następujący przykład:

from collections import Iterable
from traceback import print_exc

def check_and_raise(x):
    if not isinstance(x, Iterable):
        raise TypeError, "%s is not iterable" % x
    else:
        for i in x:
            print i

def just_iter(x):
    for i in x:
        print i


class NotIterable(object):
    pass

if __name__ == "__main__":
    try:
        check_and_raise(5)
    except:
        print_exc()
        print

    try:
        just_iter(5)
    except:
        print_exc()
        print

    try:
        Iterable.register(NotIterable)
        ni = NotIterable()
        check_and_raise(ni)
    except:
        print_exc()
        print

Jeśli obiekt nie spełnia tego, czego oczekujesz, po prostu rzucasz TypeError, ale jeśli właściwe ABC zostało zarejestrowane, twoja kontrola jest bezużyteczna. Przeciwnie, jeśli dostępna jest Metoda __iter__, Python będzie automatycznie rozpoznaje obiekt tej klasy jako iteracyjny.

Więc jeśli po prostu oczekujesz iterowalnego, powtarzaj to i zapomnij o tym. Z drugiej strony, jeśli musisz robić różne rzeczy w zależności od typu wejścia, Infrastruktura ABC może okazać się przydatna.

 15
Author: Alan Franzoni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-10 21:55:26

Znalazłem fajne rozwiązanie Tutaj :

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)
 11
Author: jbochi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-10 21:52:55

Możesz spróbować tego:

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

Jeśli możemy stworzyć generator, który nad nim iteruje (ale nigdy nie używa generatora, aby nie zajmował miejsca), jest iteracyjny. Wygląda na coś w rodzaju "duh". Dlaczego musisz określić, czy zmienna jest w ogóle możliwa do powtórzenia?

 10
Author: Chris Lutz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-23 12:21:12

Zgodnie z Słowniczek Pythona 2, iterable to

Wszystkie typy sekwencji (takie jaklist, str, i tuple) oraz niektóre typy bez sekwencji, takie jak dict i file oraz obiekty dowolnych klas zdefiniowanych za pomocą metody __iter__() lub __getitem__(). Iterable mogą być używane w pętli for I w wielu innych miejscach, gdzie potrzebna jest sekwencja (zip (), map (),...). Gdy obiekt iterable jest przekazywany jako argument do wbudowanej funkcji iter (), zwraca iterator dla obiekt.

Oczywiście, biorąc pod uwagę ogólny styl kodowania Pythona oparty na fakcie, że "łatwiej prosić o przebaczenie niż pozwolenie.", ogólnym oczekiwaniem jest użycie

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

Ale jeśli chcesz to sprawdzić jawnie, możesz przetestować na iterowalne przez hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Musisz sprawdzić, czy nie ma obu, ponieważ strs nie mają metody __iter__ (przynajmniej nie w Pythonie 2, w Pythonie 3 tak jest) oraz ponieważ generator obiekty nie mają metody __getitem__.

 8
Author: Anaphory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-25 10:14:50

Często znajduję w skryptach wygodny sposób zdefiniowania iterable funkcji. (Teraz zawiera sugerowane uproszczenie Alfe):

import collections

def iterable(obj):
    return isinstance(obj, collections.Iterable):

Więc możesz sprawdzić, czy jakikolwiek obiekt jest iteracyjny w bardzo czytelnej formie

if iterable(obj):
    # act on iterable
else:
    # not iterable

Jak zrobiłbyś z callable funkcją

EDIT: jeśli masz zainstalowany numpy, możesz po prostu zrobić: z numpy import iterable, co jest po prostu czymś w rodzaju

def iterable(obj):
    try: iter(obj)
    except: return False
    return True

Jeśli nie masz numpy, możesz po prostu zaimplementować ten kod, lub powyższy.

 4
Author: fmonegaglia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-26 15:52:54

Pandy mają wbudowaną funkcję taką jak Ta:

from pandas.util.testing import isiterable
 4
Author: Sören,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 12:27:19

Od Python 3.5 możesz użyć modułu typing z biblioteki standardowej dla rzeczy związanych z typem:

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)
 4
Author: Rotareti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 13:13:26
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

To powie " tak "wszystkim obiektom iterowalnym, ale powie" nie " ciągom w Pythonie 2 . (To jest to, czego chcę na przykład, gdy funkcja rekurencyjna może przyjąć ciąg znaków lub kontener łańcuchów. W tej sytuacji, Prośba o przebaczenie może prowadzić do obfuscode i lepiej najpierw poprosić o pozwolenie.)

import numpy

class Yes:
    def __iter__(self):
        yield 1;
        yield 2;
        yield 3;

class No:
    pass

class Nope:
    def __iter__(self):
        return 'nonsense'

assert is_iterable(Yes())
assert is_iterable(range(3))
assert is_iterable((1,2,3))   # tuple
assert is_iterable([1,2,3])   # list
assert is_iterable({1,2,3})   # set
assert is_iterable({1:'one', 2:'two', 3:'three'})   # dictionary
assert is_iterable(numpy.array([1,2,3]))
assert is_iterable(bytearray("not really a string", 'utf-8'))

assert not is_iterable(No())
assert not is_iterable(Nope())
assert not is_iterable("string")
assert not is_iterable(42)
assert not is_iterable(True)
assert not is_iterable(None)

Wiele innych strategii tutaj powie tak ciągów. Użyj ich, jeśli tego chcesz.

import collections
import numpy

assert isinstance("string", collections.Iterable)
assert isinstance("string", collections.Sequence)
assert numpy.iterable("string")
assert iter("string")
assert hasattr("string", '__getitem__')

Uwaga: is_iterable () powie tak dla łańcuchów typu bytes i bytearray.

  • bytes obiekty w Pythonie 3 są iteracyjne True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) nie ma takiego typu w Pythonie 2.
  • bytearray obiekty w Pythonie 2 i 3 są iteracyjne True == is_iterable(bytearray(b"abc"))

Podejście O. P. hasattr(x, '__iter__') powie tak ciągom w Pythonie 3 i nie w Pythonie 2 (bez względu na to, czy '' czy b'' Czy u''). Dzięki @ LuisMasuelli za Zauważenie, że również zawiedzie Cię na buggy __iter__.

 2
Author: Bob Stein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-25 20:02:17

Najprostszym sposobem, szanując typowanie kaczki Pythona , jest wychwycenie błędu (Python doskonale wie, czego oczekuje od obiektu, który stanie się iteratorem):

class A(object):
    def __getitem__(self, item):
        return something

class B(object):
    def __iter__(self):
        # Return a compliant iterator. Just an example
        return iter([])

class C(object):
    def __iter__(self):
        # Return crap
        return 1

class D(object): pass

def iterable(obj):
    try:
        iter(obj)
        return True
    except:
        return False

assert iterable(A())
assert iterable(B())
assert iterable(C())
assert not iterable(D())

Uwagi :

  1. nie ma znaczenia rozróżnienie, czy obiekt nie jest iterowalny, czy zaimplementowano błąd __iter__, Jeśli typ wyjątku jest taki sam: w każdym razie nie będziesz mógł iterować obiektu.
  2. Myślę, że rozumiem twoje obawy: w jaki sposób callable istnieje jako sprawdzanie, czy mogę również polegać na pisaniu kaczek, aby wywołać AttributeError, Jeśli {[4] } nie jest zdefiniowany dla mojego obiektu, ale nie jest tak w przypadku sprawdzania iteracyjnego?

    Nie znam odpowiedzi, ale możesz albo zaimplementować funkcję, którą podałem (I inni użytkownicy), albo po prostu złapać wyjątek w kodzie (twoja implementacja w tej części będzie taka, jak funkcja, którą napisałem - upewnij się, że odizolujesz tworzenie iteratora od reszty kodu, abyś mógł przechwycić wyjątek i go odróżnić z innego TypeError.

 1
Author: Luis Masuelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-10 21:57:15

Func isiterable w poniższym kodzie zwraca True jeśli obiekt jest iterowalny. if it ' s not iterable returns False

def isiterable(object_):
    return hasattr(type(object_), "__iter__")

Przykład

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
 1
Author: Nomad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-16 01:44:14

Zamiast sprawdzać atrybut __iter__, możesz sprawdzić atrybut __len__, który jest zaimplementowany przez każdy wbudowany w Pythona iterowalny, w tym ciągi znaków.

>>> hasattr(1, "__len__")
False
>>> hasattr(1.3, "__len__")
False
>>> hasattr("a", "__len__")
True
>>> hasattr([1,2,3], "__len__")
True
>>> hasattr({1,2}, "__len__")
True
>>> hasattr({"a":1}, "__len__")
True
>>> hasattr(("a", 1), "__len__")
True

None-iterable objects nie zaimplementowałyby tego z oczywistych powodów. Nie łapie jednak iterabli zdefiniowanych przez Użytkownika, które go nie implementują, ani wyrażeń generatora, z którymi iter może sobie poradzić. Można to jednak zrobić w wierszu, a dodanie prostego sprawdzania wyrażenia or dla generatorów naprawiłoby ten problem. (Zauważ, że pisanie type(my_generator_expression) == generator rzuci NameError. Zobacz to odpowiedź zamiast.)

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

(to czyni go użytecznym do sprawdzenia, czy można wywołać len na obiekcie.)

 0
Author: DarthCadeus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-14 08:25:19

Zawsze mi umykało, dlaczego python ma callable(obj) -> bool, ale nie iterable(obj) -> bool...
z pewnością jest to łatwiejsze do zrobienia hasattr(obj,'__call__'), nawet jeśli jest wolniejsze.

Ponieważ prawie każda inna odpowiedź zaleca użycie try/except TypeError, jeśli testowanie wyjątków jest ogólnie uważane za złą praktykę wśród dowolnego języka, oto implementacja iterable(obj) -> bool, którą polubiłem i używam często:

Ze względu na Pythona 2, użyję lambdy tylko dla tego dodatkowego zwiększenia wydajności...
(w Pythonie 3 nie w zależności od tego, czego używasz do definiowania funkcji, def ma mniej więcej taką samą prędkość jak lambda)

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

Zauważ, że funkcja ta wykonuje się szybciej dla obiektów z __iter__, ponieważ nie testuje dla __getitem__.

Większość obiektów iteracyjnych powinna polegać na __iter__, gdzie obiekty o szczególnym przypadku powracają do __getitem__, chociaż oba są wymagane, aby obiekt był iteracyjny.
(a ponieważ jest to standard, dotyczy również obiektów C)

 0
Author: Tcll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-14 16:18:03

Poza regularnymi próbami i wyjątkami, można uruchomić pomoc.

temp= [1,2,3,4]
help(temp)

Help podaje wszystkie metody, które mogą być uruchomione na tym obiekcie (może to być dowolny obiekt i może nie być listą jak na przykład), co w tym przypadku jest temp.

Uwaga: To byłoby coś, co można zrobić ręcznie.

 -2
Author: prudhvi Indana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-30 15:47:16