【问题标题】:isinstance(foo, types.GeneratorType) or inspect.isgenerator(foo)?isinstance(foo, types.GeneratorType) 还是 inspect.isgenerator(foo)?
【发布时间】:2013-10-30 06:29:21
【问题描述】:

在Python中似乎有两种方法可以测试一个对象是否是一个生成器:

import types
isinstance(foo, types.GeneratorType)

或:

import inspect
inspect.isgenerator(foo)

本着“应该有一种——最好只有一种——明显的方式来做到这一点。”的精神,推荐这些方式中的一种而不是另一种(大概他们做同样的事情......如果不是,请赐教!)?

【问题讨论】:

  • 你为什么要首先检查它?
  • isgenerator 我认为更多的是用于函数(尚未被调用),而 isinstance 通常指的是已由某物返回或构造的实例化生成器(这就是我的 2c 问题)
  • @ThiefMaster:公平的问题,显然在 Python 中要避免类型检查。我有一个函数,它在 99% 的时间内遍历列表两次。在传入生成器的 1% 的时间内,我需要先将其结晶为列表。
  • 很确定他们做同样的事情。我认为您使用哪个并不重要,但我认为 isinstance(foo, types.GeneratorType) 更惯用。
  • 我认为更好的解决方案是记录该函数不适用于生成器,它需要某种容器对象。然后,调用者可以使用list(gen) 或在他们有生成器的情况下调用它。

标签: python performance


【解决方案1】:

您可以进行类型检查,但您可能不想检查 just 生成器。你真正想要的是检查'迭代器',或者更确切地说,你想要两个迭代器。

import collections, itertools

def cheap_tee(foo):
    if isinstance(foo, collections.Iterator):
        # this is already an iterator, we need to 'tee' it
        return itertools.tee(foo)
    elif isinstance(foo, collections.Iterable):
        # this is already an iterable, get two iterators from it:
        return iter(foo), iter(foo)
    raise TypeError("Don't know how to cheaply copy these", foo)

这将适用于远程可迭代的任何东西,而不仅仅是生成器表达式。某些类型将提供自定义迭代器,这些迭代器适用于不容易用生成器表达式或生成器表示的数据结构,或者在 C 中实现为迭代器。要么还提供 __copy__ 机制,itertools.tee 可以实际使用并赢得也不必费心重复工作。只有当它真的已经是 tee 无法为您复制的迭代器时,它才会使用空间,为您完成所有结晶。

【讨论】:

    【解决方案2】:

    它们是 100% 等效的:

    >>> print(inspect.getsource(inspect.isgenerator))
    def isgenerator(object):
        """Return true if the object is a generator.
    
        Generator objects provide these attributes:
            __iter__        defined to support interation over container
            close           raises a new GeneratorExit exception inside the
                            generator to terminate the iteration
            gi_code         code object
            gi_frame        frame object or possibly None once the generator has
                            been exhausted
            gi_running      set to 1 when generator is executing, 0 otherwise
            next            return the next item from the container
            send            resumes the generator and "sends" a value that becomes
                            the result of the current yield-expression
            throw           used to raise an exception inside the generator"""
        return isinstance(object, types.GeneratorType)
    

    我想说使用isinstance(object, types.GeneratorType) 应该是首选方式,因为它更清晰、更简单。 还有inspect.isgenerator只在python2.6中加入,也就是说使用isinstance更加向后兼容。

    他们可能添加了isgenerator 对称函数isgeneratorfunction,它做了一些不同的事情。

    【讨论】:

    • +1 用于提醒我注意 inspect 模块中的 getsource 函数。太酷了!
    • 我怀疑添加它的原因是它可以作为第二个参数传递给inspect.getmembers,允许您遍历对象属性,仅查找(例如)生成器对象。
    • @dequestarmappartialsetattr 它可能是。另一个明显的选择是使用partial,但为了仅传递第二个参数,您必须使用关键字 args,我相信isinstance 不支持,因此唯一的其他方法是使用一个lambda(与传递isgenerator完全相同)。
    【解决方案3】:

    你应该可以做到:

    try:
        x = possible_generator.next()
        mylist = [x] + list(possible_generator)
    
    except:
        pass
    

    这将区分生成器和内置迭代器;但是,如果您有一个类似于列表但也实现了 next 的自定义类,那么它将失败。

    【讨论】:

    • 这也将生成器转换为列表,这违背了使用生成器的初衷。
    • 但这就是他想要做的......他要走两次,所以如果它已经是一个列表,请保留原件,否则将生成器扁平化为一个列表。
    • 我错过了那条评论。
    猜你喜欢
    • 2014-03-20
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 2017-12-23
    • 1970-01-01
    • 2023-03-17
    • 2010-12-12
    相关资源
    最近更新 更多