我有一系列已连接的生成器,我想创建一个可用于包装其中一个生成器的过滤器。该过滤器包装器应将生成器和函数作为参数。如果传入流中的数据项未通过过滤器的要求,则应将其下游传递到下一个生成器,而不必通过包装的生成器。我在这里做了一个工作示例,应该使我更清楚地了解要实现的目标:
import functools
is_less_than_three = lambda x : True if x < 3 else False
def add_one(numbers):
print("new generator created")
for number in numbers:
yield number + 1
def wrapper(function, filter_):
@functools.wraps(function)
def wrapped(generator):
for data in generator:
if filter_(data):
yield from function([data])
else:
yield data
return wrapped
add_one_to_numbers_less_than_three = wrapper(add_one, is_less_than_three)
answers = add_one_to_numbers_less_than_three(range(6))
for answer in answers:
print(answer)
#new generator created
#1
#new generator created
#2
#new generator created
#3
#3
#4
#5
问题在于,它需要为每个数据项创建一个新的生成器。肯定有更好的办法?我也尝试过使用itertools.tee并拆分生成器,但是当生成器以不同速率生成值时(这确实会),这会导致内存问题。如何在不重新创建生成器且不引起内存问题的情况下完成上述代码的工作?
只要存在无状态的1:1连接,请使用function而不是生成器。
def add_one(number): # takes one number
return number + 1 # provides one number
def conditional_map(function, condition):
@functools.wraps(function)
def wrapped(generator):
return (
function(item) if condition(item)
else item for item in generator
)
return wrapped
for answer in conditional_map(add_one, lambda x: x < 3)(range(6)):
print(answer)
如果必须将数据传递给有状态的“生成器”,则它是协程,应该这样设计。这意味着yield
既用于接收并提供数据。
from itertools import count
def add_increment(start=0):
# initially receive data
number = yield
for increment in count(start):
# provide and receive data
number = yield number + increment
由于这仍然是1:1连接,因此可以与先前的conditional_map
一起使用。
mapper = add_increment()
next(mapper) # prime the coroutine - this could be done with a decorator
for answer in conditional_map(mapper.send, lambda x: x < 3)(range(6)):
print(answer)
如果需要1:n连接,则期望为每个输入接收一个生成器。
def add_some(number): # takes one number
yield number - 1
yield number
yield number + 1
def conditional_map(function, condition):
@functools.wraps(function)
def wrapped(generator):
for data in generator:
if filter_(data):
yield from function(data) # passes one *one* item
else:
yield data
return wrapped
如果需要有状态的1:n连接,则可以使用产生生成器/迭代器的协程。
def add_increments(start=0):
# initially receive data
number = yield
for increment in count(start):
# provide and receive data
number = yield (number + increment + i for i in (-1, 0, 1))