有方法isiterable
吗?到目前为止我找到的唯一解决方案是打电话
hasattr(myObj, '__iter__')
Run Code Online (Sandbox Code Playgroud)
但我不确定这是多么万无一失.
mik*_*iku 795
检查__iter__
序列类型的工作,但它会失败,例如Python 2中的字符串.我也想知道正确的答案,在此之前,这里有一种可能性(也适用于字符串):
try:
some_object_iterator = iter(some_object)
except TypeError as te:
print some_object, 'is not iterable'
Run Code Online (Sandbox Code Playgroud)
所述iter
内置的检查的__iter__
方法或串的情况下的__getitem__
方法.
另一种通用的pythonic方法是假设一个可迭代的,然后如果它不能在给定的对象上工作则优雅地失败.Python词汇表:
Python的编程风格,通过它的方法检查用于确定对象的类型或属性的签名,而不是由某些类型的对象("如果它看起来像一个明确的关系鸭子,叫声像鸭子,那一定是一个鸭子通过强调接口".)而不是特定类型,精心设计的代码通过允许多态替换来提高其灵活性.Duck-typing避免使用type()或isinstance()进行测试.相反,它通常采用EAFP(更容易请求宽恕而非许可)的编程风格.
...
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
但是,这不会检查可迭代的类__getitem__
.
Geo*_*lly 549
try:
iterator = iter(theElement)
except TypeError:
# not iterable
else:
# iterable
# for obj in iterator:
# pass
Run Code Online (Sandbox Code Playgroud)
使用抽象基类.他们至少需要Python 2.6并且只适用于新式类.
from collections.abc import Iterable # import directly from collections for Python < 3.3
if isinstance(theElement, Iterable):
# iterable
else:
# not iterable
Run Code Online (Sandbox Code Playgroud)
检查
isinstance(obj, Iterable)
检测到注册为Iterable或具有__iter__()
方法的类,但它不检测使用该__getitem__()
方法迭代的类.确定对象是否可迭代的唯一可靠方法是调用iter(obj)
.
tim*_*geb 103
我想更多地了解窗帘背后的相互作用iter
,__iter__
以及__getitem__
幕后发生的事情.有了这些知识,你就能理解为什么你能做的最好
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
Run Code Online (Sandbox Code Playgroud)
我将首先列出事实,然后快速提醒您for
在python中使用循环时会发生什么,然后进行讨论以说明事实.
如果至少满足下列条件之一,则可以o
通过调用从任何对象获取迭代器iter(o)
:
a)o
有一个__iter__
返回迭代器对象的方法.迭代器是具有__iter__
和__next__
(Python 2 :)方法的任何对象next
.
b)o
有一种__getitem__
方法.
检查Iterable
or 的实例Sequence
或检查属性__iter__
是不够的.
如果一个对象o
只实现__getitem__
,但不会__iter__
,iter(o)
它将构造一个迭代器,它尝试从o
整数索引中获取项目,从索引0开始.迭代器将捕获IndexError
所引发的任何(但没有其他错误),然后StopIteration
自行引发.
从最普遍的意义上讲,iter
除了尝试之外,没有办法检查返回的迭代器是否合理.
如果一个对象o
实现__iter__
,该iter
函数将确保返回的对象__iter__
是一个迭代器.如果对象仅实现,则不进行健全性检查__getitem__
.
__iter__
胜.如果一个对象o
同时实现了__iter__
和__getitem__
,iter(o)
将调用__iter__
.
如果要使自己的对象可迭代,请始终实现该Iterable
方法.
__iter__
循环为了跟进,您需要了解Sequence
在Python中使用循环时会发生什么.如果您已经知道,请随意跳到下一部分.
当您使用for
某个可迭代对象时for
,Python会调用for item in o
并期望迭代器对象作为返回值.迭代器是实现o
(或iter(o)
在Python 2中)方法和__next__
方法的任何对象.
按照惯例,next
迭代器的方法应该返回对象本身(即__iter__
).Python然后调用__iter__
迭代器直到return self
被引发.所有这些都是隐式发生的,但以下演示使其可见:
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
Run Code Online (Sandbox Code Playgroud)
迭代next
:
>>> 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
Run Code Online (Sandbox Code Playgroud)
在第1点和第2点:获取迭代器和不可靠的检查
考虑以下课程:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
Run Code Online (Sandbox Code Playgroud)
StopIteration
使用实例调用DemoIterable
将返回迭代器而没有任何问题因为iter
实现BasicIterable
.
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
Run Code Online (Sandbox Code Playgroud)
但是,重要的是要注意,BasicIterable
没有__getitem__
属性,并且不被视为b
或的实例__iter__
:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
Run Code Online (Sandbox Code Playgroud)
这就是Luciano Ramalho的Fluent Python建议调用Iterable
和处理潜力Sequence
作为检查对象是否可迭代的最准确方法的原因.直接从书中引用:
从Python 3.4开始,检查对象
iter
是否可迭代的最准确方法是调用TypeError
并处理x
异常(如果不是).这比使用更准确iter(x)
,因为TypeError
还考虑了遗留isinstance(x, abc.Iterable)
方法,而iter(x)
ABC则没有.
在第3点:迭代仅提供__getitem__
但不提供的对象Iterable
__getitem__
按预期迭代一个工作实例:Python构造一个迭代器,它尝试按索引获取项目,从零开始,直到__iter__
引发一个.演示对象的BasicIterable
方法只返回由返回的迭代器IndexError
作为参数提供__getitem__
的内容item
.
>>> 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
Run Code Online (Sandbox Code Playgroud)
需要注意的是,迭代器抛出__getitem__(self, item)
时,它不能返回的下一个项目,而且iter
它提高了StopIteration
内部处理.这就是为什么循环IndexError
使用item == 3
循环按预期工作的原因:
>>> for x in b:
... print(x)
...
0
1
2
Run Code Online (Sandbox Code Playgroud)
这是另一个例子,以便BasicIterable
通过索引尝试访问项目来驱动返回迭代器的概念.for
不继承iter
,这意味着实例将没有WrappedDict
方法.
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
Run Code Online (Sandbox Code Playgroud)
请注意,调用dict
委托给__iter__
方括号表示法只是一种简写.
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
Run Code Online (Sandbox Code Playgroud)
在第4点和第5点:__getitem__
调用时检查迭代器dict.__getitem__
:
当iter
被调用的对象__iter__
,iter(o)
将确保的返回值o
,如果方法存在,是一个迭代器.这意味着返回的对象必须实现iter
(或__iter__
在Python 2中)和__next__
.next
不能对仅提供的对象执行任何健全性检查__iter__
,因为它无法检查对象的项是否可通过整数索引访问.
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
Run Code Online (Sandbox Code Playgroud)
请注意,从iter
实例构造迭代器会立即失败,同时从__getitem__
成功构造迭代器,但会在第一次调用时抛出异常FailIterIterable
.
>>> 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
Run Code Online (Sandbox Code Playgroud)
第6点:FailGetItemIterable
胜利
这个很简单.如果一个对象实现__next__
和__iter__
,__iter__
将调用__getitem__
.考虑以下课程
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
Run Code Online (Sandbox Code Playgroud)
循环实例时的输出:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
Run Code Online (Sandbox Code Playgroud)
在第7点:您的可迭代类应该实现 iter
您可能会问自己,为什么大多数内置序列都会__iter__
实现一个__iter__
方法list
就足够了.
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
Run Code Online (Sandbox Code Playgroud)
毕竟,在重复上面的类,它代表调用的情况下__iter__
,以__getitem__
(使用方括号),将正常工作:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
Run Code Online (Sandbox Code Playgroud)
您的自定义iterables应实现的原因__getitem__
如下:
list.__getitem__
,实例将被视为iterables,__iter__
并将返回__iter__
.isinstance(o, collections.abc.Iterable)
不是迭代器,True
则会立即失败并引发一个__iter__
.iter
出于向后兼容性原因,存在特殊处理.再次从Fluent Python引用:这就是为什么任何Python序列都是可迭代的:它们都是实现的
TypeError
.事实上,标准序列也实现了__getitem__
,你的也应该实现,因为由于__getitem__
向后兼容性的原因而存在特殊处理,并且可能在将来消失(尽管我写这篇文章时并没有弃用).
Pek*_*rck 88
我最近一直在研究这个问题。基于此,我的结论是,现在这是最好的方法:
from collections.abc import Iterable # drop `.abc` with Python 2.7 or lower
def iterable(obj):
return isinstance(obj, Iterable)
Run Code Online (Sandbox Code Playgroud)
上面已经推荐过,但普遍的共识是使用iter()
会更好:
def iterable(obj):
try:
iter(obj)
except Exception:
return False
else:
return True
Run Code Online (Sandbox Code Playgroud)
我们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'
Run Code Online (Sandbox Code Playgroud)
如果我们使用insinstance()
,我们不会意外地认为 Faker 实例(或任何其他只有 的对象__getitem__
)是可迭代的:
>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False
Run Code Online (Sandbox Code Playgroud)
较早的答案评论说,使用iter()
更安全,因为在 Python 中实现迭代的旧方法是基于__getitem__
这种isinstance()
方法的,而该方法不会检测到这一点。这对于旧的 Python 版本可能是正确的,但基于我非常详尽的测试,isinstance()
现在效果很好。唯一isinstance()
不起作用但起作用的情况iter()
是UserDict
在使用 Python 2 时。如果这是相关的,则可以使用它isinstance(item, (Iterable, UserDict))
来覆盖。
Ala*_*oni 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)
Run Code Online (Sandbox Code Playgroud)
现在,无论这是可取的还是实际的,只是一个惯例问题.如您所见,您可以将非可迭代对象注册为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
Run Code Online (Sandbox Code Playgroud)
如果对象不满足您的期望,您只需抛出TypeError,但如果已经注册了正确的ABC,则您的检查无效.相反,如果该__iter__
方法可用,Python将自动将该类的对象识别为Iterable.
所以,如果你只是期望一个迭代,迭代它并忘记它.另一方面,如果您需要根据输入类型执行不同的操作,您可能会发现ABC基础结构非常有用.
bad*_*adp 21
try:
#treat object as iterable
except TypeError, e:
#object is not actually iterable
Run Code Online (Sandbox Code Playgroud)
不要运行检查,看看你的鸭子是否真的是一只鸭子,看它是否可以迭代,把它看作是不是,如果不是就抱怨.
Vla*_*lad 18
到目前为止我找到的最佳解决方案:
hasattr(obj, '__contains__')
它基本上检查对象是否实现了in
运算符.
优点(其他解决方案都没有全部三个):
__iter__
)笔记:
Rot*_*eti 16
从Python 3.5开始,您可以使用标准库中的输入模块来处理类型相关的事情:
from typing import Iterable
...
if isinstance(my_item, Iterable):
print(True)
Run Code Online (Sandbox Code Playgroud)
Chr*_*utz 14
你可以试试这个:
def iterable(a):
try:
(x for x in a)
return True
except TypeError:
return False
Run Code Online (Sandbox Code Playgroud)
如果我们可以使一个迭代它的生成器(但从不使用生成器,因此它不占用空间),它是可迭代的.看起来像是一种"呃"的东西.为什么你需要首先确定一个变量是否可迭代?
jbo*_*chi 13
我发现了一个很好的解决方案在这里:
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
Run Code Online (Sandbox Code Playgroud)
Ana*_*ory 10
根据Python 2词汇表,迭代是
所有序列类型(如
list
,str
,和tuple
)和一些非序列类型,如dict
和file
以及你与定义任何类的对象__iter__()
或__getitem__()
方法.Iterables可用于for循环以及需要序列的许多其他地方(zip(),map(),...).当一个可迭代对象作为参数传递给内置函数iter()时,它返回该对象的迭代器.
当然,考虑到Python的一般编码风格,基于"更容易请求宽恕而不是许可"这一事实,一般的期望是使用
try:
for i in object_in_question:
do_something
except TypeError:
do_something_for_non_iterable
Run Code Online (Sandbox Code Playgroud)
但是如果你需要明确地检查它,你可以测试一个可迭代的hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
.您需要检查两者,因为str
s没有__iter__
方法(至少在Python 2中没有,在Python 3中它们没有)并且因为generator
对象没有__getitem__
方法.
在我的脚本中,我经常发现定义iterable
函数很方便.(现在纳入了Alfe的建议简化):
import collections
def iterable(obj):
return isinstance(obj, collections.Iterable):
Run Code Online (Sandbox Code Playgroud)
所以你可以用非常易读的形式测试任何对象是否可以迭代
if iterable(obj):
# act on iterable
else:
# not iterable
Run Code Online (Sandbox Code Playgroud)
就像你对这个callable
功能一样
编辑:如果您安装了numpy,您可以简单地执行:from numpy import iterable
,这就像是
def iterable(obj):
try: iter(obj)
except: return False
return True
Run Code Online (Sandbox Code Playgroud)
如果你没有numpy,你可以简单地实现这个代码,或上面的代码.