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. 19 answers
-
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__
. -
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'
-
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__
.
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 calliter(obj)
.
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
Iterator można uzyskać z dowolny obiekt
o
przez wywołanieiter(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__
.Sprawdzanie instancji
Iterable
lubSequence
, lub sprawdzanie atrybut {[17] } nie wystarczy.Jeśli obiekt
o
implementuje tylko__getitem__
, ale Nie__iter__
,iter(o)
będzie konstruować iterator, który próbuje pobrać elementy zo
za pomocą indeksu integer, zaczynając od indeksu 0. Iterator wychwytuje każdyIndexError
(ale żadnych innych błędów), który zostanie podniesiony, a następnie sam podniesieStopIteration
.W najbardziej ogólnym sensie, nie ma innego sposobu, aby sprawdzić, czy iterator zwrócony przez
iter
jest zdrowy, niż wypróbować go.Jeśli obiekt
o
implementuje__iter__
, funkcjaiter
zapewni że obiekt zwracany przez__iter__
jest iteratorem. Nie ma kontroli poczytalności jeśli obiekt implementuje tylko__getitem__
.__iter__
wygrywa. Jeśli obiekto
implementuje zarówno__iter__
, jak i__getitem__
,iter(o)
will call__iter__
.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 obiektx
jest iteracyjny, jest wywołanieiter(x)
i obsługaTypeError
wyjątek , jeśli nie jest. jest to dokładniejsze niż użycieisinstance(x, abc.Iterable)
, ponieważiter(x)
uwzględnia również metodę legacy__getitem__
, podczas gdyIterable
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:]}
- jeśli zaimplementujesz
__iter__
, instancje będą uważane za iterable, aisinstance(o, collections.Iterable)
zwrócąTrue
. - jeśli obiekt zwrócony przez
__iter__
nie jest iteratorem,iter
zawiedzie natychmiast i wywołaTypeError
. - 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ę).
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ć".
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.
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
- Działa na dowolnym Pythonie >= 2.5
__iter__
)
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ć
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.
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)
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?
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 jak
list
,str
, ituple
) oraz niektóre typy bez sekwencji, takie jakdict
ifile
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ż str
s nie mają metody __iter__
(przynajmniej nie w Pythonie 2, w Pythonie 3 tak jest) oraz ponieważ generator
obiekty nie mają metody __getitem__
.
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.
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
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)
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ą iteracyjneTrue == 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ą iteracyjneTrue == 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__
.
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 :
- 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. -
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
.
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
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.)
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)
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.
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