Pythonでは、オブジェクトが反復可能かどうかをどのように判断しますか?


1084

のような方法はありisiterableますか?これまでに見つけた唯一の解決策は電話することです

hasattr(myObj, '__iter__')

しかし、これがどれほど簡単であるかはわかりません。


18
__getitem__オブジェクトを反復可能にするのにも十分です
Kos

4
FWIW:のiter(myObj)場合isinstance(myObj, dict)は成功します。したがってmyObjdictsのシーケンスまたは単一のdictである可能性があるを調べている場合は、どちらの場合でも成功します。シーケンスとは何かを知りたい場合に重要となる繊細さ。(Python 2内)
Ben Mosher 14

7
__getitem__オブジェクトを反復可能にするのにも十分です... インデックスがゼロから始まる場合
カルロスA.ゴメス

回答:


27

私は最近この問題をかなり研究しています。これに基づいて、私の結論は、今日ではこれが最善のアプローチであるということです。

from collections.abc import Iterable   # drop `.abc` with Python 2.7 or lower

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

上記はすでに推奨されていますが、一般的なコンセンサスは使用iter()する方が良いということです:

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

iter()この目的のためにコードでも使用しましたが、最近、__getitem__反復可能と見なされているだけのオブジェクトにますます煩わされるようになりました。__getitem__反復不可能なオブジェクトに含める正当な理由があり、それらでは上記のコードはうまく機能しません。実際の例として、Fakerを使用できます。上記のコードはそれが反復可能であることを報告していますが、実際にそれを反復しようとするとAttributeError(Faker 4.0.2でテストされています):

>>> from faker import Faker
>>> fake = Faker()
>>> iter(fake)    # No exception, must be iterable
<iterator object at 0x7f1c71db58d0>
>>> list(fake)    # Ooops
Traceback (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'

を使用する場合insinstance()、Fakerインスタンス(またはのみを持つその他のオブジェクト__getitem__)が反復可能であると誤って見なすことはありません。

>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False

以前の回答はiter()、Pythonで反復を実装する古い方法がベースになって__getitem__おり、isinstance()アプローチがそれを検出しないため、使用の方が安全であるとコメントしました。これは古いPythonバージョンでも当てはまるかもしれませんが、私の徹底的なテストに基づいて、isinstance()今日はうまく機能しています。ケースのみisinstance()の仕事をしませんでしたが、iter()やったはしていましたUserDict使用するためにはPython 2.それが関連するなら、それが可能だを使用するときにisinstance(item, (Iterable, UserDict))カバーすることを取得します。


1
またtyping.Dictによって反復可能と考えられているiter(Dict)が、list(Dict)エラーで失敗しますTypeError: Parameters to generic types must be types. Got 0.。予想通り、isinstance(Dict, Iterable)falseを返します。
PekkaKlärck

1
私は同じ結論に達しましたが、理由は異なります。を使用iterすると、「事前キャッシュ」を使用するコードの一部が不必要に遅くなります。__iter__コードが遅い場合、呼び出しが行われiterます...何かが反復可能かどうかを確認したいときはいつでも。
thorwhalen

842
  1. __iter__シーケンス型のチェックは機能しますが、Python 2の文字列などは失敗します。私も正しい答えを知りたいのですが、それまでは、1つの可能性があります(これは文字列でも機能します)。

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

    iter組み込みのためのチェック__iter__方法または文字列の場合に__getitem__方法。

  2. もう1つの一般的なpythonicアプローチは、反復可能であると想定し、指定されたオブジェクトで機能しない場合は正常に失敗することです。Python用語集:

    いくつかのタイプのオブジェクトにそのメソッドまたは属性の署名の検査ではなく、明示的な関係によって、オブジェクトの種類を判定しニシキヘビプログラミングスタイル(「それはのように見える場合はアヒルと同様に鳴く、それがなければなりません。」)インターフェースを強調することで特定のタイプではなく、適切に設計されたコードは、ポリモーフィック置換を可能にすることにより、柔軟性を向上させます。ダックタイピングは、type()またはisinstance()を使用したテストを回避します。代わりに、通常はEAFP(許可よりも許しを求めるのが簡単)スタイルのプログラミングを採用しています。

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
  3. collectionsモジュールは、彼らが特定の機能を提供する場合、たとえば、クラスまたはインスタンスを依頼することができ、いくつかの抽象基底クラスを、提供しています。

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

    ただし、これは、を介して反復可能なクラスをチェックしません__getitem__


34
[e for e in my_object]他の理由で例外をmy_object発生させる可能性がありmy_objectます。つまり、実装における未定義のバグまたは潜在的なバグです。
Nick Dandoulakis 09

37
文字列がある(シーケンスisinstance('', Sequence) == True任意の配列としては、ある(反復可能isinstance('', Iterable))。しかしhasattr('', '__iter__') == False、それは混乱するかもしれません。
jfs 2009

82
場合my_object(のような無限の、と言う非常に大きいitertools.count())あなたのリストの内包表記は、時間/メモリの多くを取るでしょう。(潜在的に無限の)リストを構築しようとしないジェネレーターを作成することをお勧めします。
Chris Lutz、

14
some_objectが他の理由(バグなど)によってTypeErrorをスローした場合はどうなりますか?「Not iterable TypeError」からどのようにそれを知ることができますか?
Shaung

54
Python 3では、hasattr(u"hello", '__iter__')戻り値True
Carlos

572

アヒルのタイピング

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

# for obj in iterator:
#     pass

型チェック

抽象基本クラスを使用します。それらは少なくともPython 2.6を必要とし、新しいスタイルのクラスでのみ機能します。

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

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

ただし、ドキュメントでiter()説明されているように、少し信頼性が高くなります

チェックでisinstance(obj, Iterable)は、Iterableとして登録されているクラス、または__iter__()メソッドを持つクラスが検出されますが、__getitem__() メソッドで反復するクラスは検出されません。オブジェクトが反復可能かどうかを判別する唯一の信頼できる方法は、を呼び出すことiter(obj)です。


18
Luciano Ramalhoによる「Fluent Python」より:Python 3.4以降、オブジェクトxが反復可能かどうかを確認する最も正確な方法は、iter(x)を呼び出し、そうでない場合はTypeError例外を処理することです。ITER(X)も、従来の考慮これは、でisinstance(X、abc.Iterable)を使用するよりも正確であるGetItem関数の反復処理可能ABCはないが、方法。
RdB

あなたが「ああ、私はisinstance(x, (collections.Iterable, collections.Sequence))代わりにiter(x)」と思っている場合、これはまだ実装されているだけで__getitem__なく実装されている反復可能なオブジェクトを検出しないことに注意してください__len__iter(x)例外を使用してキャッチします。
デール

2番目の答えは機能しません。PyUNOで行う場合iter(slide1)、すべてがうまくいきますが、isinstance(slide1, Iterable)スローしTypeError: issubclass() arg 1 must be a classます。
Hi-Angel

@ Hi-Angelは、PyUNO通知メッセージのバグのように聞こえますが、エラーメッセージにはのissubclass()代わりに表示されますisinstance()
GeorgSchölly19年

2
オブジェクトに対してiter()を呼び出すと、負荷の高い操作になる可能性があります(iter()で複数のプロセスをfork / spawnするPytorchのDataLoaderを参照してください)。
szali

126

私は、の相互作用にもう少し光当てたいと思いiter__iter__そして__getitem__、どのようなカーテンの後ろに起こります。その知識で武装すると、あなたができる最善のことはなぜであるかを理解することができます

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

最初に事実をリストしてからfor、Pythonでループを使用したときに何が起こるかを簡単に思い出させてから、事実を説明するためのディスカッションを行います。

事実

  1. あなたは、任意のオブジェクトからイテレータを取得することができますo呼び出すことによってiter(o)、次の条件の少なくとも一方が成立する場合

    oがある__iter__イテレータオブジェクトを返すメソッドを。イテレータとは__iter____next__(Python 2:)nextメソッドを持つ任意のオブジェクトです。

    B)o有する__getitem__方法。

  2. Iterableまたはのインスタンスを確認するかSequence、属性を確認する__iter__だけでは不十分です。

  3. オブジェクトの場合o器具だけ__getitem__ではなく__iter__iter(o)試行からアイテムをフェッチすること反復子建設するoインデックス0から始まる整数インデックスでの反復子は、任意のキャッチするIndexError上昇である(ただし、他のエラー)を、次に上げるStopIteration自体。

  4. 最も一般的な意味では、によって返されたイテレータiterが正しいかどうかを確認する方法は、それを試す以外にありません。

  5. オブジェクトがをo実装している__iter__場合、iter関数はによって返されるオブジェクト__iter__がイテレータであることを確認します。オブジェクトがのみを実装するかどうかの健全性チェックはありません__getitem__

  6. __iter__勝ちます。オブジェクトがとのo両方__iter__を実装している場合は__getitem__iter(o)が呼び出されます__iter__

  7. 独自のオブジェクトを反復可能にしたい場合は、常に__iter__メソッドを実装してください。

for ループ

従うためにはfor、Pythonでループを使用するときに何が起こるかを理解する必要があります。既にご存知の場合は、次のセクションに進んでください。

for item in oいくつかの反復可能なオブジェクトに使用するとo、Pythonはiter(o)反復子オブジェクトを呼び出し、戻り値として期待します。イテレータは、__next__(またはnextPython 2で)メソッドとメソッドを実装するオブジェクト__iter__です。

慣例により__iter__、イテレータのメソッドはオブジェクト自体(つまりreturn self)を返す必要があります。次にPythonは、が発生するnextまでイテレータを呼び出しますStopIteration。これはすべて暗黙的に行われますが、次のデモではそれを表示します。

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

の反復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

ディスカッションとイラスト

ポイント1および2:反復子と信頼性の低いチェックの取得

次のクラスについて考えてみます。

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

iterインスタンスで呼び出すと、が実装されてBasicIterableいるため、問題なくイテレータが返さBasicIterable__getitem__ます。

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

ただし、b__iter__属性を持たず、Iterableまたはのインスタンスとは見なされないことに注意することが重要ですSequence

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

Luciano RamalhoによるFluent Pythonが、オブジェクトが反復可能かどうかを確認する最も正確な方法として、iter可能性TypeErrorを呼び出して処理することを推奨するのはこのためです。本から直接引用:

Python 3.4以降、オブジェクトxが反復可能かどうかを確認する最も正確な方法は、反復可能でない場合は例外を呼び出しiter(x)て処理するTypeErrorことです。ABCはそうではないが、従来の方法も考慮しているisinstance(x, abc.Iterable)ため、これはを使用するよりも正確です。iter(x)__getitem__Iterable

ポイント3日:のみ提供するオブジェクトの繰り返し処理__getitem__ではなく、__iter__

BasicIterable期待どおりに作品のインスタンスを反復処理します。Pythonは、インデックスIndexErrorが発生するまで、ゼロから開始してインデックスで項目をフェッチしようとする反復子を構築します。デモオブジェクトの__getitem__メソッドは、によって返されたイテレータによってitem引数として渡されたを返すだけです。__getitem__(self, item)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

イテレータはStopIteration次のアイテムを返すことができない場合に発生し、IndexError発生したitem == 3は内部的に処理されることに注意してください。これが、ループBasicIterableでのforループが期待どおりに機能する理由です。

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

が返すイテレータiterがインデックスでアイテムにアクセスする方法の概念を理解するための別の例を次に示します。WrappedDictはから継承しません。dictつまり、インスタンスには__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

への呼び出し__getitem__dict.__getitem__、角括弧表記が単に省略形である場合に委譲されることに注意してください。

>>> w = WrappedDict({-1: 'not printed',
...                   0: 'hi', 1: 'StackOverflow', 2: '!',
...                   4: 'not printed', 
...                   'x': 'not printed'})
>>> for x in w:
...     print(x)
... 
hi
StackOverflow
!

ポイント4および5 iterでは、次の呼び出し時にイテレータをチェックします__iter__

ときにiter(o)オブジェクトに対して呼び出されoiter戻り値があることを確認します__iter__方法が存在する場合、イテレータです。つまり、返されたオブジェクトは__next__(またはnextPython 2では)およびを実装する必要があり__iter__ます。iterオブジェクト__getitem__のアイテムに整数インデックスでアクセスできるかどうかをチェックする方法がないため、だけを提供するオブジェクトの健全性チェックを実行できません。

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

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

FailIterIterableインスタンスからのイテレータの構築はすぐに失敗しますが、イテレータの構築はFailGetItemIterable成功しますが、への最初の呼び出しで例外がスローされ__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

ポイント6:__iter__勝利

これは簡単です。オブジェクトが__iter__andを実装している場合は__getitem__iterを呼び出します__iter__。次のクラスを検討してください

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

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

インスタンスをループするときの出力:

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

ポイント7:イテラブルクラスは実装する必要があります __iter__

ほとんどの組み込みシーケンスがメソッドをlist実装するのに十分である理由を自問するかもしれません。__iter____getitem__

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

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

結局のところ、(角括弧表記を使用して)__getitem__への呼び出しを委譲する上記のクラスのインスタンスに対する反復は、list.__getitem__正常に機能します。

>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
...     print(x)
... 
A
B
C

カスタム反復可能オブジェクトが実装__iter__する必要がある理由は次のとおりです。

  1. を実装すると__iter__、インスタンスは反復可能と見なされ、isinstance(o, collections.abc.Iterable)を返しTrueます。
  2. によって返されたオブジェクトが__iter__イテレータでない場合、はiterすぐに失敗し、を発生させTypeErrorます。
  3. の特別な処理は__getitem__、下位互換性のために存在します。Fluent Pythonからの引用:

これが、Pythonシーケンスが反復可能である理由__getitem__です。それらはすべてを実装します。実際、標準シーケンスもを実装し__iter__ます。これは__getitem__、下位互換性のためにの特別な処理が存在し、将来はなくなる可能性があるためです(ただし、私がこれを書いているため、非推奨ではありません)。


述語を定義するために安全であるis_iterable返すことによってTruetryブロックとFalseexcept TypeErrorブロック?
alancalvitti

これは素晴らしい答えです。これは、getitemプロトコルの直感的で不幸な性質を強調していると思います。追加されるべきではありませんでした。
Neil G

31

これでは不十分です。によって返されるオブジェクトは__iter__、反復プロトコル(つまり、nextメソッド)を実装する必要があります。ドキュメントの関連セクションを参照してください。

Pythonでは、「チェック」ではなく「試してみる」ことをお勧めします。


9
「アヒルのタイピング」と思いますか?:)
ウィレム2009

9
@willem:または「許可を求めるのではなく、許しを求める」;-)
jldupont 2009

14
@willem「許可」と「寛容」の両方のスタイルは、アヒルのタイピングと見なされます。あなたが求める場合はオブジェクトが何をすることができません、むしろそれが何よりもあるダックタイピングだと、。イントロスペクションを使用する場合、それは「許可」です。あなたがそれをやろうとして、それが機能するかどうかを確かめるだけなら、それは「許し」です。
Mark Reed、

22

Python <= 2.5では、できませんし、できません-iterableは「非公式」インターフェースでした。

しかし、Python 2.6および3.0以降では、新しいABC(抽象基本クラス)インフラストラクチャと、コレクションモジュールで利用可能ないくつかの組み込みABCを利用できます。

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)

これが望ましいか、実際に機能するかは、慣例の問題です。ご覧のとおり、反復不可能なオブジェクトをIterableとして登録できます。実行時に例外が発生します。したがって、isinstanceは「新しい」意味を取得します。これは「宣言された」型の互換性をチェックするだけであり、これはPythonでの使用に適した方法です。

一方、オブジェクトが必要なインターフェースを満たさない場合、何をしますか?次の例を見てください。

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

オブジェクトが期待したものを満たさない場合はTypeErrorをスローするだけですが、適切なABCが登録されている場合、チェックは役に立ちません。逆に、__iter__メソッドが利用可能な場合、PythonはそのクラスのオブジェクトをIterableとして自動的に認識します。

したがって、イテラブルを期待するだけの場合は、反復して忘れてください。一方、入力の種類に応じて異なる処理を行う必要がある場合は、ABCインフラストラクチャが非常に役立つことがあります。


13
except:初心者向けのサンプルコードでは、ベアを使用しないでください。それは悪い習慣を促進します。
jfs 09

JFS:しませんでしたが、複数の例外発生コードを実行する必要があり、特定の例外をキャッチしたくありませんでした...このコードの目的はかなり明確だと思います。
Alan Franzoni、

21
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

アヒルが本当にアヒルであるかどうかを確認するためにチェックを実行して、それが反復可能であるかどうかを確認しないでください。


3
技術的には、反復中に計算によってがスローされたりTypeError、ここからスローされたりする場合がありますが、基本的にはそうです。
Chris Lutz、

6
@willem:timeitを使用してベンチマークを実行してください。多くの場合、Python例外はifステートメントよりも高速です。彼らは、通訳を通る少し短い経路を取ることができます。
S.Lott、2009

2
@willem:IronPythonには(CPythonと比較して)遅い例外があります。
jfs 2009

2
実用的なtry:ステートメントは本当に高速です。したがって、例外がほとんどない場合、try-exceptは高速です。多くの例外が予想される場合は、「if」の方が高速です。
Arne Babenhauserheide 2012年

2
例外オブジェクトは、「as e」をTypeError追加する代わりに「」を追加してキャッチする必要はありません, eか?
HelloGoodbye 2016

21

Python 3.5以降では、型に関連することのために標準ライブラリの型付けモジュールを使用できます。

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)

18

これまでに見つけた最良の解決策:

hasattr(obj, '__contains__')

これは基本的に、オブジェクトがin演算子を実装しているかどうかをチェックします。

利点(他のどのソリューションも3つすべてを備えていない):

  • それは式です(try ... exceptバリアントは対照的に、ラムダとして機能します
  • (とは対照的に)文字列を含むすべてのイテラブルによって実装されるべきです(すべきです__iter__
  • Python 2.5以上で動作します

ノート:

  • 「許可ではなく許しを求める」というPythonの哲学は、たとえばリストに反復可能と反復不可能の両方があり、型ごとに異なる方法で各要素を処理する必要がある場合はうまく機能しません(試行時に反復可能を処理し、 exceptのイテラブル機能しますが、お尻が醜く誤解を招くように見えます)
  • オブジェクトを実際に反復して(たとえば、[x for x in obj])、反復可能かどうかを確認しようとするこの問題の解決策は、大きな反復可能オブジェクト(特に、反復可能オブジェクトの最初のいくつかの要素だけが必要な場合)に重大なパフォーマンスペナルティを引き起こす可能性があります。例)そして、避けるべきです

3
いいですが、stackoverflow.com / questions / 1952464 /…で提案されているcollectionsモジュールを使用しないのはなぜですか?私にはもっと表現力があるようです。
Dave Abrahams、

1
明確さを失うことなく、より短く(追加のインポートは必要ありません)、「contains」メソッドを持つことは、何かがオブジェクトのコレクションであるかどうかをチェックする自然な方法のように感じられます。
Vlad

46
何かが何かを含むことができるからといって、必ずしも反復可能であるとは限りません。たとえば、ユーザーはポイントが3Dキューブ内にあるかどうかを確認できますが、このオブジェクトをどのように反復しますか?
ケーシークボール

13
これは誤りです。iterable自体は、少なくともPython 3.4では、containsをサポートしいません。
Peter Shinners、2015年

15

あなたはこれを試すことができます:

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

それを反復するジェネレーターを作成できる場合(ただし、ジェネレーターを使用しないため、スペースを占有しない)、反復可能です。「ドゥー」のようなもののようです。そもそも変数が反復可能かどうかを判断する必要があるのはなぜですか?


どうiterable(itertools.repeat(0))ですか?:)
badp

5
@badp (x for x in a)は、ジェネレータを作成するだけで、aに対して反復を行いません。
catchmeifyoutry 2009

5
試してみるのと(x for x in a)まったく同じiterator = iter(a)ですか?または、2つが異なる場合がありますか?
最大

for _ in a: breakもっと簡単ではありませんか?遅いですか?
Mr_and_Mrs_D 2015年

2
@Mr_and_Mrs_Dテストされたオブジェクトが後で反復される反復子(位置をリセットできないため、1アイテム短くなります)である場合は不良です。ガベージジェネレータを作成しても、反復されないため、オブジェクトを反復しません。ただし、反復可能でない場合は100%TypeErrorが発生するかどうかはわかりません。
Tcll 2018

13

私はここに素晴らしい解決策を見つけました:

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)

11

Python 2 Glossaryによると、イテラブルは

すべてのシーケンス型(のようなliststrtuple)などいくつかの非シーケンス型dictfileし、あなたが定義したクラスのオブジェクト__iter__()または__getitem__()方法。イテラブルは、forループや、シーケンスが必要な他の多くの場所(zip()、map()など)で使用できます。組み込み可能関数iter()に引数として反復可能オブジェクトが渡されると、オブジェクトの反復子が返されます。

もちろん、「許可よりも許しを求める方が簡単だ」という事実に基づいたPythonの一般的なコーディングスタイルを考えると、一般的な期待は、

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

しかし、明示的にチェックする必要がある場合は、でイテラブルをテストできますhasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")。にstr__iter__メソッドがないため(少なくともPython 2にはない)、generatorオブジェクトにはメソッドがないため、両方を確認する必要があります__getitem__


8

私はスクリプトの中でiterable関数を定義するのに便利だと思うことがよくあります。(現在、Alfeが提案する簡略化が組み込まれています):

import collections

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

そのため、非常に読みやすい形式で反復可能なオブジェクトがあるかどうかをテストできます

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

callable関数で行うように

編集:numpyがインストールされている場合numpy import iterableは、単に次のようにすることができます:from 、これは単に次のようなものです

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

numpyがない場合は、このコードまたは上記のコードを単純に実装できます。


3
if x: return True else: return Falsexブール値で)のように実行するときはいつでも、これをと書くことができますreturn x。あなたのケースreturn isinstance(…)では何もありませんif
Alfe、2013年

Alfeのソリューションの方が優れていることを認識しているので、答えを編集して、単にそれを言うだけにしないでください。代わりに、回答に両方のバージョンがあります。不要な冗長性。これを修正するために編集を送信します。
ToolmakerSteve

2
「例外:return False」行で「TypeError」をキャッチする必要があります。すべてをキャッチすることは悪いパターンです。
Mariusz Jamro 14

ことを知っている。そのコードの一部を、一般的な例外を使用するNumPyライブラリから翻訳しました。
fmonegaglia 14

コードがNumPyから取得されたからといって、それが良いパターンであるとは限りません...すべてのパターンをキャッチする必要があるのは、プログラム内で明示的にエラー処理を行っている場合のみです。
Tcll 2018

5

そのような組み込み関数があります:

from pandas.util.testing import isiterable

ただし、これは__iter__、シーケンスなどが実際に気になっているかどうかを確認するだけです。
EAD

4

なぜpythonがそうであるのかcallable(obj) -> bool、そうでないのかは常にわからないiterable(obj) -> bool...
確かにhasattr(obj,'__call__')それが遅くても簡単に実行できる。

他のほぼすべての回答がtry/の使用を推奨しているexcept TypeErrorため、例外のテストは一般的にどの言語でも悪い習慣と見なされていますが、ここではiterable(obj) -> bool私がより好きになり、頻繁に使用する実装を示します。

Python 2のために、私はその追加のパフォーマンス向上のためにラムダを使用します...
(Python 3では、関数の定義に何を使用するかは関係ありません。def速度はとほぼ同じですlambda

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

この関数はの__iter__テストを行わないため、を使用したオブジェクトの方が高速に実行されることに注意してください__getitem__

ほとんどの反復可能オブジェクトは、オブジェクトが反復可能になるために必要ですが、__iter__特別な場合のオブジェクトがにフォールバックする場所に依存する__getitem__必要があります。
(これは標準なので、Cオブジェクトにも影響します)


彼は動作するコードを提供しておらず、Pythonのパフォーマンスについて話すことは言うまでもありません...この答えは、私がここで何度も行ったことを見たように、本当に便宜上のものでした。
Tcll

3
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

これは、あらゆる種類の反復可能なオブジェクトに対して「はい」と答えますが、Python 2の文字列に対しては「いいえ」と答えます。(たとえば、再帰関数が文字列または文字列のコンテナーを取得できる場合に必要なものです。その場合、許しを求めると難読化につながる可能性があります。最初に許可を求めることをお勧めします。)

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)

ここで他の多くの戦略は、文字列にイエスと言うでしょう。必要な場合は、それらを使用してください。

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__')

注:is_iterable()は、タイプbytesとの文字列に対してyesと言いますbytearray

  • bytesPython 3のオブジェクトは反復可能True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))ですPython 2にはそのようなタイプはありません。
  • bytearray Python 2および3のオブジェクトは反復可能です True == is_iterable(bytearray(b"abc"))

OPのhasattr(x, '__iter__')アプローチは、(どんなにかどうかはPython 2にはPython 3の文字列にyesと言わないと何だろう''か、b''またはu'')。それに気づいてくれた@LuisMasuelliにも感謝します__iter__


2

Pythonのダックタイピングを尊重する最も簡単な方法は、エラーをキャッチすることです(Pythonは、オブジェクトがイテレータになることを期待して何をしているのかを完全に知っています)。

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())

  1. __iter__例外タイプが同じである場合、オブジェクトが反復可能でないか、バグが実装されているかは区別されません。とにかく、オブジェクトを反復処理することはできません。
  2. 私はあなたの懸念を理解していると思います:オブジェクトにif が定義されていない場合に発生callableするためにダックタイピングに頼ることができるかどうかのチェックはどのように存在しますか?AttributeError__call__

    答えはわかりませんが、私(および他のユーザー)が提供した関数を実装するか、コードで例外をキャッチすることができます(その部分の実装は、私が書いた関数のようになります-確実に分離してください)残りのコードからイテレータを作成して、例外をキャプチャし、それを別のコードと区別できるようにしますTypeError


1

isiterable次のコードのfuncは、Trueオブジェクトが反復可能かどうかを返します。反復可能でない場合はFalse

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

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

2
上記の非常に多くの詳細な回答と多くの賛成投票で、説明のつかない回答が表示されます... meh
Nrzonline

裸のコードを投稿しないでください。これが何をしているかの説明も含めてください。
ジョナサンミー2017

1

__iter__属性をチェックする代わりに、属性をチェックすることもでき__len__ます。これは、文字列を含むすべてのPython組み込みイテラブルによって実装されます。

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

不可解なオブジェクトは、明らかな理由でこれを実装しません。ただし、それを実装していないユーザー定義のイテラブルをキャッチしたり、ジェネレーター式iterを処理したりすることはできません。ただし、これは一行で行うことができor、ジェネレーターの単純な式チェックを追加すると、この問題が解決します。(書き込みによりtype(my_generator_expression) == generatorがスローされることに注意してNameErrorください。代わりにこの回答を参照しください。)

タイプからGeneratorTypeを使用できます。

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

--- utdemirによる回答の承認

(これによりlen、オブジェクトを呼び出すことができるかどうかを確認するのに役立ちます。)


残念ながら、すべての反復可能なオブジェクトが使用するわけではありません__len__...この場合、通常は2つのオブジェクト間の距離を計算する不適切な使用です。obj.dist()簡単に置き換えることができる場所。
Tcll

うん。ほとんどのユーザー定義のイテラブルはiterとgetitemを実装していますが、lenは実装していません。ただし、組み込みのタイプではそれが可能であり、その上でlen関数を呼び出すことができるかどうかを確認したい場合は、lenの確認がより安全です。しかし、あなたは正しいです。
DarthCadeus

0

本当に「正しい」わけではありませんが、文字列、タプル、フロートなどの最も一般的な型のクイックチェックとして機能します。

>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False

0

ちょっとパーティーに遅れましたが、私は自分自身にこの質問をして、これが答えを考えているのを見ました。誰かがすでにこれを投稿したかどうかはわかりません。しかし本質的に、すべての反復可能な型のdictに__getitem __()があることに気付きました。これは、オブジェクトを試行せずに反復可能かどうかを確認する方法です。(パン意図)

def is_attr(arg):
    return '__getitem__' in dir(arg)

残念ながら、これは信頼できません。
timgeb

1
セットオブジェクトは、もう1つの反例です。
レイモンドヘッティンガー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.