在Python中遍历列表或单个元素

30 投票
8 回答
25742 浏览
提问于 2025-04-16 21:37

我想要遍历一个不知道的函数的输出结果。可惜的是,我不知道这个函数是返回一个单独的值还是一个元组(也就是一组值)。这应该是一个常见的问题,应该有标准的方法来处理这个情况——而我现在的做法看起来很糟糕。

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

8 个回答

2

也许用 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(字符串),或者是从这些类型派生出来的任何类,这个方法就能奏效。

7

到处都写代码并不是个好主意。我们可以写一个函数来处理这些事情。这里有个我之前想出来的建议,适用于类似的问题。这个函数特别处理字符串(通常是可以迭代的),把它们当作单独的项目来处理,这正是我通常想要的效果。

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

更新 这里有一个生成器版本,使用了比较新的(Python 3.3)yield from 语句。

def iterfy(iterable):
    if isinstance(iterable, str):
        yield iterable
    else:
        try:
            # need "iter()" here to force TypeError on non-iterable
            # as e.g. "yield from 1" doesn't throw until "next()"
            yield from iter(iterable)
        except TypeError:
            yield iterable
34

解决这个问题的最通用方法是使用 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 不被认为是有害的吗?它不是会让你只能用一种类型吗?用 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

撰写回答