我如何判断发电机是否刚刚启动?

我想要一个函数.gi_running,该函数的行为如下:

>>> def gen(): yield 0; yield 1
>>> a = gen()
>>> is_just_started(a) 
True
>>> next(a)
0
>>> is_just_started(a) 
False
>>> next(a)
1
>>> is_just_started(a) 
False
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> is_just_started(a)
False

如何实现此功能?

我查看了.gi_running属性,但它似乎用于其他用途。

如果我知道需要发送到生成器的第一个值,则可以执行以下操作:

def safe_send(gen, a):
    try:
        return gen.send(a)
    except TypeError as e:
        if "just-started" in e.args[0]:
            gen.send(None)
            return gen.send(a)
        else:
            raise

但是,这似乎很可恶。

Claudiu asked 2020-02-04T08:08:45Z
3个解决方案
69 votes

这仅适用于Python 3.2+:

>>> def gen(): yield 0; yield 1
... 
>>> a = gen()
>>> import inspect
>>> inspect.getgeneratorstate(a)
'GEN_CREATED'
>>> next(a)
0
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
1
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> inspect.getgeneratorstate(a)
'GEN_CLOSED'

因此,请求的功能是:

import inspect

def is_just_started(gen):
    return inspect.getgeneratorstate(gen) == inspect.GEN_CREATED:

出于好奇,我研究了CPython以弄清它是如何确定的……显然,它看起来为generator.gi_frame.f_lasti,它是“字节码中最后一次尝试执行的指令的索引”。 如果它是-1,那么它还没有开始。

这是py2版本:

def is_just_started(gen):
    return gen.gi_frame is not None and gen.gi_frame.f_lasti == -1
Tim Tisdall answered 2020-02-04T08:09:12Z
26 votes

制作一个新的发电机,该发电机仅从您感兴趣的发电机中产生收益。 一旦使用了第一个值,它将设置一个标志。 之后,它可以简单地将yield from用于其余项目。

使用替代生成器代替您对监视“ is_just_started”状态感兴趣的生成器。

此技术是非侵入性的,甚至可以在无法控制源代码的生成器上使用。

wim answered 2020-02-04T08:09:42Z
5 votes

您可以创建一个迭代器,并将标记作为迭代器类的实例属性设置为:

class gen(object):
    def __init__(self, n):
        self.n = n
        self.num, self.nums = 0, []
        self.is_just_started = True  # Your flag

    def __iter__(self):
        return self

    # Python 3 compatibility
    def __next__(self):
        return self.next()

    def next(self):
        self.is_just_started = False  # Reset flag with next
        if self.num < self.n:
            cur, self.num = self.num, self.num+1
            return cur
        else:
            raise StopIteration()

您的价值检查功能将类似于:

def is_just_started(my_generator):
    return my_generator.is_just_started

样品运行:

>>> a = gen(2)

>>> is_just_started(a)
True

>>> next(a)
0
>>> is_just_started(a)
False

>>> next(a)
1
>>> is_just_started(a)
False

>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 19, in next
StopIteration

要了解迭代器和生成器之间的区别,请检查Python的生成器和迭代器之间的区别

Moinuddin Quadri answered 2020-02-04T08:10:19Z
translate from https://stackoverflow.com:/questions/41307038/how-can-i-tell-whether-a-generator-was-just-started