isiterable
のようなメソッドはありますか?私がこれまでに見つけた唯一の解決策は電話をかけることです
hasattr(myObj, '__iter__')
しかし、これがどれほどばかげた証拠なのか私にはわかりません。
__iter__
をチェックすることはシーケンス型で機能しますが、それは失敗するでしょう。 Python 2の文字列。私は正しい答えも知りたいのですが、それまでは1つの可能性があります(これは文字列に対しても有効です)。
try:
some_object_iterator = iter(some_object)
except TypeError as te:
print some_object, 'is not iterable'
iter
組み込み関数は__iter__
メソッドをチェックし、文字列の場合は__getitem__
メソッドをチェックします。
もう1つの一般的なPythonicのアプローチは、イテラブルを仮定し、それが与えられたオブジェクトでうまくいかない場合にはうまく失敗することです。 Pythonの用語集
ある種の型オブジェクトとの明示的な関係ではなく、メソッドまたは属性シグネチャを調べることによってオブジェクトの型を決定するPythonicプログラミングスタイル( "duckのように見え、duckのようにおかしなことにa duck。 ")特定の型ではなくインターフェースを強調することで、うまく設計されたコードは多相代入を可能にすることによってその柔軟性を向上させます。ダックタイピングは、type()やisinstance()を使ったテストを避けます。 代わりに、それは通常EAFP(許可よりも許し易い)スタイルのプログラミングを採用しています。
...
try: _ = (e for e in my_object) except TypeError: print my_object, 'is not iterable'
collections
モジュールはクラスやインスタンスが特定の機能を提供しているかどうかを問い合わせることを可能にする抽象基底クラスを提供します。例えば:
from collections.abc import Iterable
if isinstance(e, Iterable):
# e is iterable
ただし、これは__getitem__
を通じて反復可能なクラスをチェックしません。
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)
を呼び出すことです。
iter
、__iter__
、および__getitem__
の相互作用と、舞台裏で何が起こるのかをもう少し詳しく説明します。その知識を身に付けて、あなたは自分ができる最善の理由が何であるかを理解できるでしょう
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
最初に事実をリストしてから、pythonでfor
ループを使用したときに何が起こるかを簡単に追いかけて、事実を説明するための議論を続けます。
以下の条件の少なくとも1つが当てはまる場合は、iter(o)
を呼び出すことによって、任意のオブジェクトo
からイテレータを取得できます。
a)o
には、イテレータオブジェクトを返す__iter__
メソッドがあります。イテレータは__iter__
と__next__
(Python 2:next
)メソッドを持つオブジェクトです。
b)o
には__getitem__
メソッドがあります。
Iterable
またはSequence
のインスタンスを確認するか、属性__iter__
を確認するだけでは不十分です。
オブジェクトo
が__getitem__
のみを実装し、__iter__
を実装しない場合、iter(o)
はインデックス0から始まるo
から項目を取得しようとするイテレータを構築します。イテレータは発生したIndexError
をキャッチします。 StopIteration
自体を発生させます。
最も一般的な意味では、iter
によって返されたイテレータが正しいかどうかを調べる方法はありません。
オブジェクトo
が__iter__
を実装している場合、iter
関数は__iter__
によって返されるオブジェクトがイテレータであることを確認します。オブジェクトが__getitem__
のみを実装しているかどうかの健全性チェックはありません。
__iter__
が勝ちます。オブジェクトo
が__iter__
と__getitem__
の両方を実装している場合、iter(o)
は__iter__
を呼び出します。
自分のオブジェクトを反復可能にしたい場合は、常に__iter__
メソッドを実装してください。
for
ループ従うためには、Pythonでfor
ループを使用したときに何が起こるかを理解する必要があります。ご存知の場合は、次のセクションに進んでください。
反復可能オブジェクトo
に対してfor item in o
を使用すると、Pythonはiter(o)
を呼び出し、戻り値としてイテレータオブジェクトを期待します。イテレータは__next__
(またはPython 2ではnext
)メソッドと__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
これが、 Fluent Python Luciano Ramalhoがiter
を呼び出すことと、オブジェクトが反復可能かどうかをチェックする最も正確な方法として潜在的なTypeError
を処理することを推奨する理由です。本から直接引用する:
Python 3.4以降、オブジェクト
x
が反復可能かどうかを調べる最も正確な方法は、iter(x)
を呼び出し、TypeError
例外が処理されない場合はそれを処理することです。isinstance(x, abc.Iterable)
は従来の__getitem__
メソッドも考慮しますが、Iterable
ABCは考慮しないため、これはiter(x)
を使用するよりも正確です。
要点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
を発生させ、item == 3
のために発生するIndexError
は内部的に処理されることに注意してください。これが、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__
を呼び出すときにイテレータをチェックします :
オブジェクトo
に対してiter(o)
が呼び出されると、iter
は、メソッドが存在する場合は__iter__
の戻り値がイテレータであることを確認します。つまり、返されるオブジェクトは__next__
(またはPython 2ではnext
)と__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__
への最初の呼び出しでExceptionがスローされます。
>>> 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__
と__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__
を実装する理由は以下の通りです:
__iter__
を実装すると、インスタンスはイテラブルと見なされ、isinstance(o, collections.Iterable)
はTrue
を返します。__iter__
によって返されたオブジェクトがイテレータではない場合、iter
は直ちに失敗し、TypeError
を送出します。__getitem__
の特別な取り扱いは、後方互換性のために存在します。 Fluent Pythonからもう一度引用します。これが、どんなPythonシーケンスでも反復可能な理由です。それらはすべて
__getitem__
を実装しています。実際、標準のシーケンスも__iter__
を実装しており、__getitem__
の特別な処理は下位互換性の理由から存在し、将来なくなるかもしれません(これを書いているので非推奨ではありませんが)。
これだけでは十分ではありません。__iter__
によって返されるオブジェクトは、反復プロトコル(つまりnext
メソッド)を実装する必要があります。 ドキュメント の関連セクションを参照してください。
Pythonでは、「チェック」ではなく「試してみる」ことをお勧めします。
try:
#treat object as iterable
except TypeError, e:
#object is not actually iterable
確認するためにチェックを実行しない あなたのアヒルが本当にアヒルなら それが反復可能かどうかを確かめるために、それがそうであるかのようにそれを扱い、そうでないなら不平を言う。
Python <= 2.5では、あなたはできませんし、そうすべきではありません - イテレータブルは「非公式な」インターフェースでした。
しかし、Python 2.6および3.0以降では、collectionsモジュールで利用可能ないくつかの組み込みABCとともに、新しいABC(abstract base class)インフラストラクチャを利用できます。
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としてcan登録すると、実行時に例外が発生します。それゆえ、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 __== "__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は自動的にそのクラスのオブジェクトを反復可能であると認識します。
ですから、もしあなたがイテラブルを期待しているのなら、それをイテレートして忘れてください。一方、入力タイプに応じてさまざまなことをする必要がある場合は、ABCインフラストラクチャが非常に便利です。
私がこれまでに見つけた最高のソリューション:
hasattr(obj, '__contains__')
基本的に、オブジェクトがin
演算子を実装しているかどうかを確認します。
利点(他のソリューションには3つすべてがありません):
__iter__
とは対照的に)を含むすべての反復可能オブジェクトによって実装される(されるべき)ノート:
私はいい解決策を見つけました ここ :
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
あなたはこれを試すことができます:
def iterable(a):
try:
(x for x in a)
return True
except TypeError:
return False
それを繰り返すジェネレータを作ることができれば(しかし、ジェネレータを使わないのでスペースをとらない)、それは繰り返し可能です。 「当たり前」のようなもののようです。最初に変数が反復可能かどうかを判断する必要があるのはなぜですか。
Python 2用語集 によると、イテラブルは
すべてのシーケンス型(
list
、str
、Tuple
など)、およびdict
およびfile
などの一部の非シーケンス型、ならびに__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
sは__iter__
メソッドを持っていないので(少なくともPython 2では、Python 3ではそうではありません)、generator
オブジェクトは__getitem__
メソッドを持っていないので、両方をチェックする必要があります。
Python 3.5 以降、型に関することには標準ライブラリの typing モジュールを使用できます。
from typing import Iterable
...
if isinstance(my_item, Iterable):
print(True)
pandas には以下のような組み込み関数があります。
from pandas.util.testing import isiterable
私はスクリプト内でiterable
関数を定義するのが便利だと思うことがよくあります。 (現在はAlfeが提案した単純化を取り入れています):
import collections
def iterable(obj):
return isinstance(obj, collections.Iterable):
そのため、読みやすい形式でオブジェクトが反復可能かどうかをテストできます。
if iterable(obj):
# act on iterable
else:
# not iterable
callable
関数と同じように
編集:あなたが厄介なインストールされている場合は、単に行うことができます:numpy import iterable
から、これは単にのようなものです
def iterable(obj):
try: iter(obj)
except: return False
return True
おかしなことがない場合は、このコードまたは上記のコードを単純に実装できます。
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オブジェクトにも影響します)
def is_iterable(x):
try:
0 in x
except TypeError:
return False
else:
return True
これは、あらゆる種類の反復可能オブジェクトに対してyesとなりますが、Python 2では文字列に対してnoと言うとなります。 (たとえば、再帰関数が文字列や文字列のコンテナを受け取ることができる場合などです。その場合は、 許しを求める が難読化の原因となることがあります。
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
型とbytearray
型の文字列に対してyesと答えます。
bytes
オブジェクトは反復可能ですTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
Python 2にはそのような型はありません。bytearray
オブジェクトは反復可能ですTrue == is_iterable(bytearray(b"abc"))
O.P. hasattr(x, '__iter__')
アプローチは、Python 3では文字列に対してyesを、Python 2ではnoとします(''
、b''
、u''
のいずれを問わず)。 @LuisMasuelliさん、バグのある__iter__
についてもお知らせします。
最も簡単な方法は、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())
ノート :
__iter__
が実装されているかどうかの区別は関係ありません。とにかく、オブジェクトを反復することはできません。__call__
が自分のオブジェクトに定義されていない場合でも、callable
name__をレイズするためにアヒルの型付けに頼ることができるのであれば、AttributeError
name__はどのようにチェックとして存在するのでしょうか。
答えはわかりませんが、私(および他のユーザー)が提供した関数を実装するか、コード内で例外をキャッチすることができます(その部分の実装は、私が書いた関数のようになります。残りのコードからイテレータを作成するので、例外を捕捉して別のTypeError
name__と区別できます。
__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
がスローされることに注意してください。代わりに this answerを参照してください。)
型からGeneratorTypeを使用できます。
>>> import types >>> types.GeneratorType <class 'generator'> >>> gen = (i for i in range(10)) >>> isinstance(gen, types.GeneratorType) True
--- utdemirによる回答が承認されました
(これにより、オブジェクトでlen
を呼び出せるかどうかを確認するのに役立ちます。)
次のコードのisiterable
funcは、objectが反復可能であれば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
ちょっとパーティーに遅れましたが、私はこの質問を自問し、これを見て、答えを考えました。誰かがすでにこれを投稿しているかどうかはわかりません。しかし、本質的に、すべての反復可能な型の辞書に「getitem」が含まれていることに気付きました。これは、試行することなくオブジェクトが反復可能かどうかを確認する方法です。 (意図したパン)
def is_attr(arg):
return '__getitem__' in dir(arg)
実際はそうではありません "正しい" しかし文字列、タプル、フロートなどのような最も一般的な型の簡単なチェックとして役立つことができます...
>>> '__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