使用生成器的当前状态来创建延迟迭代器

时间:2017-03-25 13:46:46

标签: python itertools

我想了解如何基于条件迭代生成器,并且在不满足条件时仅返回当前状态而不调用next()。

例如,给定从1到n的重复一系列整数,如何创建从0开始的冻结指数,直到系列中的下一个值满足条件为止?

我的尝试是:

def generator():
    number = 0
    while True:
        number +=1
        yield number
gen = generator()
start = gen.next()
def f(x):
    if x>5:
        current = gen.next()
        return current
pd.Series(list(itertools.chain(np.arange(10),np.arange(10)))).apply(f)

返回

0     NaN
1     NaN
2     NaN
3     NaN
4     NaN
5     NaN
6     2.0
7     3.0
8     4.0
9     5.0
10    NaN
11    NaN
12    NaN
13    NaN
14    NaN
15    NaN
16    6.0
17    7.0
18    8.0
19    9.0

但输出应为

0     0.0
1     0.0
2     0.0
3     0.0
4     0.0
5     0.0
6     1.0
7     2.0
8     3.0
9     4.0
10    5.0
11    5.0
12    5.0
13    5.0
14    5.0
15    5.0
16    6.0
17    7.0
18    8.0
19    9.0

但是,像current = start这样的frozenIndex中的else条件将始终返回保存的第一个生成器值。

2 个答案:

答案 0 :(得分:1)

您可以通过.send方法将数据发送到生成器来完成此类操作。这是一个(相对)简单的例子。

def conditional_counter():
    ''' An infinite counter that freezes on the last value 
        if the sent-in value is False-ish
    '''
    yield
    value = 0
    while True:
        condition = yield value
        if condition:
            value += 1

counter = conditional_counter()
counter.send(None)

for s in 'abcdefghijklmnop':
    val = counter.send(s == 'c' or s >= 'h')
    print(s, val)

<强>输出

a 0
b 0
c 1
d 1
e 1
f 1
g 1
h 2
i 3
j 4
k 5
l 6
m 7
n 8
o 9
p 10

第一次调用.send() 必须传递None作为arg,否则你会得到

TypeError: can't send non-None value to a just-started generator

所以我们用

来处理
counter.send(None)

和生成器中的第一个yield语句。 FWIW,&#34;裸体&#34; yield会产生None,就像裸return返回None一样。

这是一个更复杂的生成器,可以通过任何迭代传递。

def conditional_counter(iterable):
    ''' An iterator that freezes on the last value 
        if the sent-in value is False-ish,
        or if the iterable runs out of items.
    '''
    it = iter(iterable)
    yield
    value = next(it)
    while True:
        condition = yield value
        if condition:
            try:
                value = next(it)
            except StopIteration:
                pass


counter = conditional_counter(range(9))
counter.send(None)

for s in 'abcdefghijklmnop':
    val = counter.send(s == 'c' or s >= 'h')
    print(s, val)

<强>输出

a 0
b 0
c 1
d 1
e 1
f 1
g 1
h 2
i 3
j 4
k 5
l 6
m 7
n 8
o 8
p 8

答案 1 :(得分:0)

您可以使用 0.0

初始化flag_val
flag_val = 0.0

def frozenIndex(x):
    if x>5:
        current = gen.next()
        flag_val = current
    else: 
        current = flag_val
    return current

这应解决你想要做的事情,但你不能在收益上前后移动。