How can I tell whether a generator was just-started?

前端 未结 3 846
青春惊慌失措
青春惊慌失措 2020-12-28 12:49

I\'d like a function, is_just_started, which behaves like the following:

>>> def gen(): yield 0; yield 1
>>> a = gen()
>>         


        
相关标签:
3条回答
  • 2020-12-28 13:25

    This only works in 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'
    

    So, the requested function is:

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

    Out of curiosity, I looked into CPython to figure out how it was determining this... Apparently it looks at generator.gi_frame.f_lasti which is the "index of last attempted instruction in bytecode". If it's -1 then it hasn't started yet.

    Here's a py2 version:

    def is_just_started(gen):
        return gen.gi_frame is not None and gen.gi_frame.f_lasti == -1
    
    0 讨论(0)
  • 2020-12-28 13:26

    Make a new generator which simply yields from your generator of interest. It sets a flag once the first value has been consumed. Afterwards, it can simply use yield from for the rest of the items.

    Use the substitute generator as a drop in replacement for the generator you're interested in monitoring the "is_just_started" state.

    This technique is non-intrusive, and can be used even on generators for which you have no control over the source code.

    0 讨论(0)
  • 2020-12-28 13:49

    You may create a iterator and set the flag as the instance property to iterator class as:

    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()
    

    And your value check function would be like:

    def is_just_started(my_generator):
        return my_generator.is_just_started
    

    Sample run:

    >>> 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
    

    To know the difference between iterator and generator, check Difference between Python's Generators and Iterators

    0 讨论(0)
提交回复
热议问题