In Python, how do I determine if an object is iterable?
有没有像
1 | hasattr(myObj, '__iter__') |
但我不确定这是多么愚蠢的证明。
检查
1 2 3 4 | try: some_object_iterator = iter(some_object) except TypeError as te: print some_object, 'is not iterable' |
另一个一般的方法是假设一个可重复的,如果它不能在给定的对象上工作,那么就优雅地失败。python词汇表:
Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.
...
1
2
3
4 try:
_ = (e for e in my_object)
except TypeError:
print my_object, 'is not iterable'
1 2 3 4 | from collections.abc import Iterable if isinstance(e, Iterable): # e is iterable |
但是,这不会检查可通过
鸭子打字
1 2 3 4 5 6 7 8 9 | try: iterator = iter(theElement) except TypeError: # not iterable else: # iterable # for obj in iterator: # pass |
类型检查
使用抽象基类。它们至少需要python 2.6,并且只适用于新样式的类。
1 2 3 4 5 6 | from collections.abc import Iterable # import directly from collections for Python < 3.3 if isinstance(theElement, Iterable): # iterable else: # not iterable |
但是,如文档所述,
Checking
isinstance(obj, Iterable) detects classes that are
registered as Iterable or that have an__iter__() method, but
it does not detect classes that iterate with the__getitem__()
method. The only reliable way to determine whether an object
is iterable is to calliter(obj) .
我想进一步说明一下
1 2 3 4 5 | try: iter(maybe_iterable) print('iteration will probably work') except TypeError: print('not iterable') |
我将首先列出事实,然后快速提醒您在python中使用
如果以下至少一个条件成立,则可以通过调用
检查
如果一个对象
在最一般的意义上,除了尝试一下,没有办法检查
如果对象
江户十一〔一〕胜。如果一个对象
如果要使自己的对象不可访问,请始终实现
为了跟进,您需要了解在python中使用
当您将
按照惯例,迭代器的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | >>> 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点:获取迭代器和不可靠的检查好的。
考虑以下类别:好的。
1 2 3 4 5 | class BasicIterable(object): def __getitem__(self, item): if item == 3: raise IndexError return item |
使用
1 2 3 | >>> b = BasicIterable() >>> iter(b) <iterator object at 0x7f1ab216e320> |
但是,需要注意的是,
1 2 3 4 5 6 7 | >>> from collections import Iterable, Sequence >>> hasattr(b, '__iter__') False >>> isinstance(b, Iterable) False >>> isinstance(b, Sequence) False |
这就是为什么Luciano Ramalho的Fluent python建议调用
As of Python 3.4, the most accurate way to check whether an object
x is iterable is to calliter(x) and handle aTypeError exception if it isn’t. This is more accurate than usingisinstance(x, abc.Iterable) , becauseiter(x) also considers the legacy__getitem__ method, while theIterable ABC does not.Ok.
在第3点:迭代只提供
迭代
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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 |
请注意,迭代器在不能返回下一项时引发
1 2 3 4 5 6 | >>> for x in b: ... print(x) ... 0 1 2 |
下面是另一个示例,目的是让我们了解
1 2 3 4 5 6 7 8 9 | 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 |
注意,对
1 2 3 4 5 6 7 8 9 10 | >>> 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点:当它调用
当为对象
1 2 3 4 5 6 7 | class FailIterIterable(object): def __iter__(self): return object() # not an iterator class FailGetitemIterable(object): def __getitem__(self, item): raise Exception |
注意,从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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点:
这个很简单。如果一个对象实现了
1 2 3 4 5 6 | class IterWinsDemo(object): def __iter__(self): return iter(['__iter__', 'wins']) def __getitem__(self, item): return ['__getitem__', 'wins'][item] |
以及在实例上循环时的输出:好的。
1 2 3 4 5 6 | >>> iwd = IterWinsDemo() >>> for x in iwd: ... print(x) ... __iter__ wins |
在第7点:您的iterable类应该实现
您可能会问自己,当
1 2 3 4 5 6 | class WrappedList(object): # note: no inheritance from list! def __init__(self, lst): self._list = lst def __getitem__(self, item): return self._list[item] |
毕竟,对上述类的实例(委托调用
1 2 3 4 5 6 7 | >>> wl = WrappedList(['A', 'B', 'C']) >>> for x in wl: ... print(x) ... A B C |
您的自定义iterables实现
That is why any Python sequence is iterable: they all implement
__getitem__ . In fact,
the standard sequences also implement__iter__ , and yours should too, because the
special handling of__getitem__ exists for backward compatibility reasons and may be
gone in the future (although it is not deprecated as I write this).Ok.
好啊。
这还不够:
在Python中,一个好的实践是"尝试查看"而不是"检查"。
1 2 3 4 | try: #treat object as iterable except TypeError, e: #object is not actually iterable |
不要检查你的鸭子是否真的是一只鸭子,要想看它是否是一只鸭子,就把它当作是一只鸭子来对待,如果不是,就抱怨。
在python<=2.5中,不能也不应该——iterable是一个"非正式"接口。
但是,由于python 2.6和3.0,您可以利用新的ABC(抽象基类)基础设施以及一些内置的ABC,这些ABC在collections模块中可用:
1 2 3 4 5 6 7 8 9 10 11 | 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对象注册为iterable,它将在运行时引发异常。因此,isinstance获得了一个"新"的含义——它只是检查"声明的"类型兼容性,这是进入Python的一个好方法。
另一方面,如果您的对象不满足您需要的接口,您将要做什么?举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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() try: just_iter(5) except: print_exc() try: Iterable.register(NotIterable) ni = NotIterable() check_and_raise(ni) except: print_exc() |
如果对象不满足您的期望,您只会抛出一个类型错误,但是如果注册了正确的ABC,您的检查将无效。相反,如果
所以,如果您只是期望一个iterable,那么就重复它,然后忘记它。另一方面,如果您需要根据输入类型做不同的事情,您可能会发现ABC基础结构非常有用。
迄今为止我找到的最佳解决方案是:
它主要检查对象是否实现了
优势(其他解决方案都没有这三个):
- 它是一个表达式(用作lambda,而不是try…except variant)
- 它(应该)由所有iterables实现,包括字符串(与
__iter__ 相反) - 在任何python上工作>=2.5
笔记:
- python的"请求原谅,而不是允许"的哲学在列表中没有很好的效果,比如你同时拥有iterables和non iterables,你需要根据元素的类型对每个元素进行不同的处理(在尝试时对iterables进行处理,在尝试时对non iterables进行处理除外,但是它看起来很丑陋和误导人)
- 这个问题的解决方案试图在对象上进行实际迭代(例如,obj中的x代表x),以检查它是否是不可迭代的,这可能会对大型iterable s(特别是如果您只需要iterable s的前几个元素,例如)造成显著的性能损失,应该避免。
我在这里找到了一个很好的解决方案:
1 2 | isiterable = lambda obj: isinstance(obj, basestring) \ or getattr(obj, '__iter__', False) |
你可以试试这个:
1 2 3 4 5 6 | def iterable(a): try: (x for x in a) return True except TypeError: return False |
如果我们可以生成一个迭代它的生成器(但不要使用生成器,这样它就不会占用空间),那么它是不可迭代的。好像是"该死"的东西。为什么您首先需要确定一个变量是否是可重复的?
根据python 2术语表,iterables是
all sequence types (such as
list ,str , andtuple ) and some non-sequence types likedict andfile and objects of any classes you define with an__iter__() or__getitem__() method. Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), ...). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object.
当然,考虑到Python的一般编码风格,基于这样一个事实,即"请求原谅比请求允许更容易",一般的期望是使用
1 2 3 4 5 | try: for i in object_in_question: do_something except TypeError: do_something_for_non_iterable |
但是如果您需要明确地检查它,您可以通过
由于python 3.5,您可以使用标准库中的输入模块进行与类型相关的操作:
1 2 3 4 5 6 | from typing import Iterable ... if isinstance(my_item, Iterable): print(True) |
在脚本中,我经常发现定义
1 2 3 4 | import collections def iterable(obj): return isinstance(obj, collections.Iterable): |
因此,您可以测试任何对象是否以非常可读的形式是可重写的。
1 2 3 4 | if iterable(obj): # act on iterable else: # not iterable |
就像你要破坏docx1〔6〕功能一样
编辑:如果安装了numpy,只需:从
1 2 3 4 | def iterable(obj): try: iter(obj) except: return False return True |
如果您没有numpy,可以简单地实现此代码或上面的代码。
熊猫有这样的内置功能:
1 | from pandas.util.testing import isiterable |
1 2 3 4 5 6 7 | def is_iterable(x): try: 0 in x except TypeError: return False else: return True |
这将对所有类型的iterable对象说"是",但对python 2中的字符串说"否"。(这就是我想要的,例如,当一个递归函数可以接受一个字符串或一个字符串容器时。在这种情况下,请求宽恕可能会导致混淆,最好先请求许可。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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) |
这里的许多其他策略会对字符串说"是"。如果你想用的话就用它们。
1 2 3 4 5 6 7 8 | 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()将对
- python 3中的
bytes 对象是不可重复的True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) ,python 2中没有这种类型。 - python 2和3中的
bytearray 对象是不可重复的True == is_iterable(bytearray(b"abc")) 。
o.p.
考虑到python的duck类型,最简单的方法是捕获错误(python完全知道它期望从对象变成迭代器的内容):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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()) |
笔记:
我想我理解你的顾虑:如果没有为我的对象定义
我不知道答案,但是您可以实现我(和其他用户)给出的函数,或者只是在代码中捕获异常(在该部分中的实现将类似于我编写的函数),只需确保将迭代器创建与代码的其余部分隔离开来,这样您就可以捕获异常并将其与另一个EDOCX1区分开来。〔12〕。
如果对象不可重设,下面代码中的
1 2 | def isiterable(object_): return hasattr(type(object_),"__iter__") |
例子
1 2 3 4 5 6 7 8 9 10 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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 |
由于明显的原因,没有一个iterable对象不会实现这一点。但是,它不捕获不实现它的用户定义的iterables,也不捕获
You can use GeneratorType from types:
1
2
3
4
5
6 >>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True--- accepted answer by utdemir
(这有助于检查是否可以在对象上调用
我一直不明白为什么python有
当然,即使速度较慢,也更容易执行
由于几乎所有其他答案都建议使用
为了Python2的缘故,我将使用lambda来提高性能…
(在python 3中,无论您使用什么来定义函数,
1 | iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__') |
注意,对于带有
大多数不可迭代的对象应该依赖于
(由于这是标准的,它也会影响C对象)
不是真正的"正确",但可以作为快速检查最常见的类型,如字符串、元组、浮点数等…
1 2 3 4 5 6 7 8 9 10 11 12 | >>> '__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 |
除了常规的尝试和例外,你可以运行帮助。
1 2 | temp= [1,2,3,4] help(temp) |
帮助将提供可以在该对象上运行的所有方法(它可以是任何对象,也可能不是示例中的列表),在本例中是temp。
注意:这将是您手动执行的操作。