for循环

时间:2016-01-25 14:30:12

标签: python generator

我正在尝试为我正在从数据源读取的数据设置“处理管道”,并在读取每个项目时应用一系列运算符(使用生成器)。

一些演示相同问题的示例代码。

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循环时将实际的运算符函数“绑定”到生成器表达式?

我可以通过将for循环中的生成器表达式更改为列表解析来解决这个问题,但由于实际数据要大得多,我不希望将它全部存储在内存中的任何一点。 / p>

4 个答案:

答案 0 :(得分:2)

您可以定义一个小帮助程序,它组成函数,但顺序相反

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表达式。此订单并不常见,但可确保您的功能从左到右应用(可能与您的预期相符):

使用此功能,您的main变为

def main():
    vals = reader()
    f = compose(add_1, add_5, add_10)
    return (f(v) for v in vals)

答案 1 :(得分:2)

您可以通过在新函数中创建生成器来强制绑定变量。例如

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_operatormap内置(在python 3.x中)完全相同。所以只需使用它。

答案 2 :(得分:1)

这可能是你想要的 - 创建一个复合函数:

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

答案 3 :(得分:1)

这个问题的原因是你正在创建一个深度嵌套的生成器生成器,并在循环后评估整个事物,当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()))