我想迭代未知函数的输出。不幸的是,我不知道该函数是返回单个项还是元组。这必须是一个标准问题,必须有一个标准的方法来解决这个问题 - 我现在拥有的是非常难看的。
x = UnknownFunction()
if islist(x):
iterator = x
else:
iterator = [x]
def islist(s):
try:
len(s)
return True
except TypeError:
return False
for ii in iterator:
#do stuff
这个问题最常见的解决方案是使用isinstance
和抽象基类collections.Iterable
。
import collections
def get_iterable(x):
if isinstance(x, collections.Iterable):
return x
else:
return (x,)
你可能也想测试basestring
,正如Kindall所暗示的那样。
if isinstance(x, collections.Iterable) and not isinstance(x, basestring):
现在有些人可能会想,就像我曾经做过的那样,“不是isinstance
considered harmful?它不会锁定你使用某种类型吗?不会使用hasattr(x, '__iter__')
更好吗?”
答案是:不是涉及抽象基类。实际上,您可以使用__iter__
方法定义自己的类,并且它将被识别为collections.Iterable
的实例,即使您没有子类collections.Iterable
。这是有效的,因为collections.Iterable
定义了一个__subclasshook__
,它确定传递给它的类型是否是它实现的任何定义的Iterable。
>>> class MyIter(object):
... def __iter__(self):
... return iter(range(10))
...
>>> i = MyIter()
>>> isinstance(i, collections.Iterable)
True
>>> collections.Iterable.__subclasshook__(type(i))
True
将代码包含在您需要的任何地方并不是特别优雅。所以写一个做按摩的功能。以下是我提出的类似上一个问题的建议。它是特殊情况的字符串(通常可以迭代)作为单个项目,这是我通常想要的。
def iterfy(iterable):
if isinstance(iterable, basestring):
iterable = [iterable]
try:
iter(iterable)
except TypeError:
iterable = [iterable]
return iterable
用法:
for item in iterfy(unknownfunction()):
# do something
您需要执行以下操作:
iterator = (x,) if not isinstance(x, (tuple, list)) else x
然后
for i in iterator:
#do stuff
也许最好使用collections.Iterable
来确定输出是否是可迭代的。
import collections
x = UnknownFunction()
if not isinstance(x, collections.Iterable): x = [x]
for ii in x:
#do stuff
如果x的类型是这些中的任何一个 - list
,tuple
,dict
,str
,任何来自这些的类,这将起作用。
您也可以尝试使用operator.isSequenceType函数
import operator
x = unknown_function()
if not operator.isSequenceType(x) and not isinstance(x, basestring):
x = (x,)
for item in x:
do_something(item)
您可以定义一个函数,确保返回值支持迭代(str
,dict
,tuple
等 - 包括不直接从这些类继承的用户定义的序列类型),而不是直接检查它是否是tuple
或list
。
def ensure_iterable(x):
return (x,) if not hasattr(x, '__iter__') else x
x = ensure_iterable(UnknownFunction())
for i in x:
do_something(i)
如果使用生成器,您可能会获得更好的性能。这应该适用于python 3.3及更高版本。
from collections import Iterable
def iterrify(obj):
"""
Generator yielding the passed object if it's a single element or
yield all elements in the object if the object is an iterable.
:param obj: Single element or iterable.
"""
if isinstance(obj, (str, bytes)): # Add any iterables you want to threat as single elements here
yield obj
elif isinstance(obj, Iterable): # Yield from the iterables.
yield from obj
else: # yield single elements as is.
yield obj