web-dev-qa-db-ja.com

オブジェクトがPythonのイテレータであるかどうかを確認するにはどうすればよいですか?

next()メソッドをチェックできますが、それで十分ですか?イデオマティックな方法はありますか?

40
Juanjo Conti

Python 2.6以降では、このような動作チェックの設計されたイディオムは、標準ライブラリのcollectionsモジュールにある抽象基本クラスを使用した「メンバーシップチェック」です。

_>>> import collections
>>> isinstance('ciao', collections.Iterable)
True
>>> isinstance(23, collections.Iterable)
False
>>> isinstance(xrange(23), collections.Iterable)
True
_

確かに、この種のチェックは、新しい抽象基本クラスの主要な設計上の理由です(2番目に重要なのは、場合によっては「ミックスイン機能」を提供することです。そのため、これらは単なるインターフェイスではなくABCですが、そうではありません。 t _collections.Iterable_に適用され、isinstanceまたはissubclassでそのようなチェックを許可するために厳密に存在します。 ABCを使用すると、実際には継承しないクラスをサブクラスとして「登録」できるため、そのようなクラスをそのようなチェックのABCの「サブクラス」にすることができます。また、特別なメソッド(この場合は___iter___)に必要なすべてのチェックを内部で実行できるため、実行する必要はありません。

Pythonの古いリリースで立ち往生している場合は、「許可よりも許しを求める方がよい」:

_def isiterable(x):
  try: iter(x)
  except TypeError: return False
  else: return True
_

しかし、それは新しいアプローチほど速くて簡潔ではありません。

この特殊なケースでは、特殊なケースの文字列(反復可能ですが、ほとんどのアプリケーションコンテキストはとにかく「スカラー」として扱いたい)が必要になることがよくあります。反復可能性をチェックするために使用しているアプローチが何であれ、そのような特別なケーシングが必要な場合は、isinstance(x, basestring)のチェックを追加するだけです。例:

_def reallyiterable(x):
  return not isinstance(x, basestring) and isinstance(x, collections.Iterable)
_

編集:コメントで指摘されているように、質問は、オブジェクトが反復可能であるかどうかではなく、反復可能であるかどうかに焦点を当てています***(すべての反復子は反復可能です) 、ただしその逆ではありません-すべてのイテレータがイテレータであるとは限りません)。 isinstance(x, collections.Iterator)は、その状態を具体的にチェックするための完全に類似した方法です。

58
Alex Martelli

オブジェクトがイテレータプロトコルを実装している場合、そのオブジェクトは反復可能です。
__iter__()メソッドの存在を次の方法で確認できます。

hasattr(object,'__iter__')

in Python 2.xこのアプローチでは、strオブジェクトや、unicode、xrange、bufferなどの他の組み込みシーケンスタイプが欠落しています。Python 3で機能します。

別の方法は、iterメソッドでテストすることです:

try:
   iter(object)
except TypeError:
   #not iterable
17
systempuntoout

イテレータになるには、オブジェクトは次の3つのテストに合格する必要があります。

  • objには___iter___メソッドがあります
  • objにはnextメソッド(または___next___ in Python 3)
  • obj.__iter__()objを返します

したがって、独自のテストは次のようになります。

_def is_iterator(obj):
    if (
            hasattr(obj, '__iter__') and
            hasattr(obj, 'next') and      # or __next__ in Python 3
            callable(obj.__iter__) and
            obj.__iter__() is obj
        ):
        return True
    else:
        return False
_
6
Ethan Furman

pythonソースコードドキュメントコメントからの回答:

{pythonインストールパス} /Versions/3.5/lib/python3.5/types.py

# Iterators in Python aren't a matter of type but of protocol.  A large
# and changing number of builtin types implement *some* flavor of
# iterator.  Don't check the type!  Use hasattr to check for both
# "__iter__" and "__next__" attributes instead.
2
yoyo

他の回答が示唆しているよりも良い方法があります。

Pythonには、IterableIteratorの2種類があります。オブジェクトはIterableを提供できる場合はIteratorです。オブジェクトでiter()を使用するとそうなります。 next()を使用してその要素を順番に参照できる場合、オブジェクトはIteratorです。たとえば、map()Iteratorを返し、listIterableです。

ここに 詳細 があります。

以下のコードは、これらのタイプをチェックする方法を示しています。

from collections.abc import Iterable, Iterator

r = [1, 2, 3]
e = map(lambda x:x, r)

print(isinstance(r, Iterator)) # False, because can't apply next
print(isinstance(e, Iterator)) # True
print(isinstance(r, Iterable)) # True, because can apply iter()
print(isinstance(e, Iterable)) # True, note iter() returns self
0
Shital Shah

この例は、本Effective Pythonからのものであり、この post に示されています。

Iterableはイテレータを生成します。イテレータもイテレータですが、イテレータとして生成されます。

>>> list_iter = iter([])
>>> iter(list_iter) is list_iter
True
0
Tengerye

質問はイテレータではなくイテレータに関するものであり、イテレータの使用を検討しているため、これを行う最も簡単でPythonの方法

iterable = [1,2]
iterator = iter(iterable)


def isIterator(obj):
    try:
        next(obj, None)
        return True
    except TypeError:
        return False

>>> isIterator(iterable)
False
>>> isIterator(iterator)
True

はい。 next()をチェックするだけで十分です

0
user2390183