Python 生成器 "chain" 在 for 循环中
Python Generator "chain" in a for loop
我正在尝试为我从数据源读取的数据设置一个 "processing pipeline",并在读取的每个项目上应用一系列运算符(使用生成器)。
一些演示相同问题的示例代码。
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = reader()
for op in operators:
vals = (op(val) for val in vals)
return vals
print(list(main()))
期望:[17, 18, 19]
实际:[31, 32, 33]
Python 似乎不是每次通过 for 循环保存 op
的值,所以它每次都应用第三个函数。 有没有办法每次通过for循环"bind"将实际的运算符函数传给生成器表达式?
我可以通过将 for 循环中的生成器表达式更改为列表理解来轻松解决这个问题,但由于实际数据要大得多,我不想在任何时候将其全部存储在内存中.
这可能是您想要的 - 创建复合函数:
import functools
def compose(functions):
return functools.reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x)
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = map(compose(operators), reader())
return vals
print(list(main()))
您可以定义一个小助手来组合函数但顺序相反:
import functools
def compose(*fns):
return functools.reduce(lambda f, g: lambda x: g(f(x)), fns)
即您可以使用 compose(f,g,h)
生成等同于 lambda x: h(g(f(x)))
的 lambda 表达式。此顺序不常见,但可确保应用您的函数 left-to-right(这可能是您所期望的):
使用这个,你的 main
就变成
def main():
vals = reader()
f = compose(add_1, add_5, add_10)
return (f(v) for v in vals)
这个问题的原因是您正在创建一个深度嵌套的生成器生成器并在 循环之后评估整个事情,当 op
被绑定到列表中的最后一个元素 -- 类似于非常常见的 "lambda in a loop" 问题。
从某种意义上说,您的代码大致相当于:
for op in operators:
pass
print(list((op(val) for val in (op(val) for val in (op(val) for val in (x for x in [1, 2, 3])))))
解决此问题的一种(不是很漂亮)方法是 zip
使用 另一个 生成器的值,重复相同的操作:
def add(n):
def add_n(val):
return val + n
return add_n
operators = [add(n) for n in [1, 5, 10]]
import itertools
def main():
vals = (x for x in [1, 2, 3])
for op in operators:
vals = (op(val) for (val, op) in zip(vals, itertools.repeat(op)))
return vals
print(list(main()))
您可以通过在新函数中创建生成器来强制绑定变量。例如
def map_operator(operator, iterable):
# closure value of operator is now separate for each generator created
return (operator(item) for item in iterable)
def main():
vals = reader()
for op in operators:
vals = map_operator(op, vals)
return vals
然而,map_operator
与 map
内置函数(在 python 3.x 中)几乎相同。因此,只需使用它即可。
我正在尝试为我从数据源读取的数据设置一个 "processing pipeline",并在读取的每个项目上应用一系列运算符(使用生成器)。
一些演示相同问题的示例代码。
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = reader()
for op in operators:
vals = (op(val) for val in vals)
return vals
print(list(main()))
期望:[17, 18, 19]
实际:[31, 32, 33]
Python 似乎不是每次通过 for 循环保存 op
的值,所以它每次都应用第三个函数。 有没有办法每次通过for循环"bind"将实际的运算符函数传给生成器表达式?
我可以通过将 for 循环中的生成器表达式更改为列表理解来轻松解决这个问题,但由于实际数据要大得多,我不想在任何时候将其全部存储在内存中.
这可能是您想要的 - 创建复合函数:
import functools
def compose(functions):
return functools.reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x)
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = map(compose(operators), reader())
return vals
print(list(main()))
您可以定义一个小助手来组合函数但顺序相反:
import functools
def compose(*fns):
return functools.reduce(lambda f, g: lambda x: g(f(x)), fns)
即您可以使用 compose(f,g,h)
生成等同于 lambda x: h(g(f(x)))
的 lambda 表达式。此顺序不常见,但可确保应用您的函数 left-to-right(这可能是您所期望的):
使用这个,你的 main
就变成
def main():
vals = reader()
f = compose(add_1, add_5, add_10)
return (f(v) for v in vals)
这个问题的原因是您正在创建一个深度嵌套的生成器生成器并在 循环之后评估整个事情,当 op
被绑定到列表中的最后一个元素 -- 类似于非常常见的 "lambda in a loop" 问题。
从某种意义上说,您的代码大致相当于:
for op in operators:
pass
print(list((op(val) for val in (op(val) for val in (op(val) for val in (x for x in [1, 2, 3])))))
解决此问题的一种(不是很漂亮)方法是 zip
使用 另一个 生成器的值,重复相同的操作:
def add(n):
def add_n(val):
return val + n
return add_n
operators = [add(n) for n in [1, 5, 10]]
import itertools
def main():
vals = (x for x in [1, 2, 3])
for op in operators:
vals = (op(val) for (val, op) in zip(vals, itertools.repeat(op)))
return vals
print(list(main()))
您可以通过在新函数中创建生成器来强制绑定变量。例如
def map_operator(operator, iterable):
# closure value of operator is now separate for each generator created
return (operator(item) for item in iterable)
def main():
vals = reader()
for op in operators:
vals = map_operator(op, vals)
return vals
然而,map_operator
与 map
内置函数(在 python 3.x 中)几乎相同。因此,只需使用它即可。