【问题标题】:Iterating over list or single element in python迭代python中的列表或单个元素
【发布时间】:2011-10-06 08:27:41
【问题描述】:

我想迭代一个未知函数的输出。不幸的是,我不知道该函数是返回单个项目还是一个元组。这一定是一个标准问题,并且必须有一个标准的方法来处理这个问题——我现在所拥有的非常丑陋。

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

【问题讨论】:

  • 标准方法是让函数返回一个 1 元组。
  • @Ignacio:当然,如果您可以控制该功能。这个问题似乎是说他没有。
  • @Fred Larson:你总是至少拥有这么多的控​​制权:lambda *a, **kw: (f(*a, **kw), )
  • @Rosh,那么你必须测试结果是否嵌套在一个元组中。
  • @senderle:希望在某个级别上该函数是已知的并且可以被包装。我还没有看到不可预测的 API。 ;)

标签: python


【解决方案1】:

您需要执行以下操作:

iterator = (x,) if not isinstance(x, (tuple, list)) else x

然后

for i in iterator:
    #do stuff

【讨论】:

  • 这不适用于元组(问题中提到)。它也不适用于列表的子类。
  • 你应该有:iterator = (x,) if type(x) != tuple else x
  • not isinstance(x, cls) 几乎总是优于 type(x) != cls
  • 哦,不知道。怎么会? (我会在答案中更改它。)
  • @TorelTwiddler:通过直接与类进行比较,您排除了所有子类。例如。这不适用于命名元组。
【解决方案2】:

将代码包含在您需要的任何地方并不是特别优雅。因此,编写一个进行按摩的函数。这是我为类似的先前问题提出的建议。它将特殊情况下的字符串(通常是可迭代的)作为单个项目,这是我发现我通常想要的。

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

更新这是一个使用 new-ish (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

【讨论】:

  • 我更喜欢iterrify 这个名字。 ;)
【解决方案3】:

也许最好使用collections.Iterable 来确定输出是否是可迭代的。

import collections

x = UnknownFunction()
if not isinstance(x, collections.Iterable): x = [x]

for ii in x:
    #do stuff

如果 x 的类型是其中任何一个 - listtupledictstr,以及从这些派生的任何类,这将起作用。

【讨论】:

    【解决方案4】:

    这个问题最通用的解决方案是使用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):
    

    现在有些人可能会想,就像我曾经做过的那样,“不是isinstanceconsidered 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
    

    【讨论】:

    • 请注意,如果您只想包含类似列表和元组的类型,您可以测试collections.Sequence
    • 我也得到了isinstance('aa',collections.Iterable) 作为True,这不是我所期望的。有调整吗?
    • @Dinesh,返回 True 是正确的——字符串是字符的可迭代对象!如果您希望您的代码对字符串有不同的行为,则必须添加一个测试。使用 isinstance(x, basestring) 捕获字符串和 unicode 对象。
    • collections而不是从collections.abc使用或导入ABC(如Iterable)自Python 3.3以来已被弃用,在3.9中它将停止工作。请改用from collections.abc import Iterable
    • @Michele 确实如此。这在几个方面已经过时了——basestring 在 Python 3 中根本不存在!我会尽可能更新它,但如果您愿意,请随时编辑。
    【解决方案5】:

    您也可以尝试使用 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)
    

    【讨论】:

      【解决方案6】:

      您可以定义一个函数来确保返回值支持迭代(strdicttuple 等——包括不直接从这些类继承的用户定义的序列类型),而不是检查如果是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)
      

      【讨论】:

        【解决方案7】:

        如果您使用生成器,您可能会获得更好的性能。这应该适用于 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
        

        【讨论】:

          【解决方案8】:

          我喜欢其他人使用 Itreable suggested 的方法。尽管在某些情况下可能会更好。更多的是EAFP(https://docs.python.org/3.5/glossary.html#term-eafp)方式:

          In [10]: def make_iter(x): 
              ...:         try: 
              ...:             return iter(x) 
              ...:         except TypeError: 
              ...:             # We seem to be dealing with something that cannot be itereated over. 
              ...:             return iter((x,)) 
              ...:              
          
          In [11]: make_iter(3)                                                                                                                                                                         
          Out[11]: <tuple_iterator at 0x7fa367b29590>
          
          In [13]: make_iter((3,))                                                                                                                                                                      
          Out[13]: <tuple_iterator at 0x7fa367b4cad0>
          
          In [14]: make_iter([3])                                                                                                                                                                       
          Out[14]: <list_iterator at 0x7fa367b29c90>
          

          这并不麻烦检查我们正在处理的内容。我们只是尝试获取一个迭代器,如果它失败了,我们假设它失败了,因为我们正在处理一些无法迭代的东西(好吧,看起来它真的不能迭代)。所以我们只需创建一个元组并从中创建一个迭代器。

          【讨论】:

            猜你喜欢
            • 2016-04-20
            • 1970-01-01
            • 2015-01-17
            • 1970-01-01
            • 2015-04-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-09-21
            相关资源
            最近更新 更多