Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Pythonic方法链接python生成器函数以形成管道_Python_Generator - Fatal编程技术网

Pythonic方法链接python生成器函数以形成管道

Pythonic方法链接python生成器函数以形成管道,python,generator,Python,Generator,我正在使用python进行管道代码重构 假设我们有一系列的生成器函数,我们希望将这些函数链接起来,形成一个数据处理管道 例如: #!/usr/bin/python import itertools def foo1(g): for i in g: yield i + 1 def foo2(g): for i in g: yield 10 + i def foo3(g): for i in g: yield 'foo3:

我正在使用python进行管道代码重构

假设我们有一系列的生成器函数,我们希望将这些函数链接起来,形成一个数据处理管道

例如:

#!/usr/bin/python
import itertools

def foo1(g):
    for i in g:
        yield i + 1

def foo2(g):
    for i in g:
        yield 10 + i

def foo3(g):
    for i in g:
        yield 'foo3:' + str(i)

res = foo3(foo2(foo1(range(0, 5))))

for i in res:
    print i
输出:

foo3:11
foo3:12
foo3:13
foo3:14
foo3:15
我不认为
foo3(foo2(foo1(范围(0,5)))
是实现我的管道目标的pythonic方法。尤其是当管道中的级数较大时

我希望我能像jquery中的chain一样重写它。类似于:

range(0, 5).foo1().foo2().foo3()
或许

l = [range(0, 5), foo1, foo2, foo3]
res = runner.run(l)
但我对生成器这个话题还不熟悉,所以找不到一个方法来实现这一点


欢迎提供任何帮助。

对于这种情况,我有时喜欢使用左折叠(Python中称为
reduce
):

from functools import reduce
def pipeline(*steps):
    return reduce(lambda x, y: y(x), list(steps))

res = pipeline(range(0, 5), foo1, foo2, foo3)
或者更好:

def compose(*funcs):
    return lambda x: reduce(lambda f, g: g(f), list(funcs), x)

p = compose(foo1, foo2, foo3)
res = p(range(0, 5))

继runner.run方法之后,让我们定义此实用程序函数:

def recur(ops):
    return ops[0](recur(ops[1:])) if len(ops)>1 else ops[0]
例如:

>>> ops = foo3, foo2, foo1, range(0, 5)
>>> list( recur(ops) )
['foo3:11', 'foo3:12', 'foo3:13', 'foo3:14', 'foo3:15']
备选方案:反向排序 例如:

>>> list( backw([range(0, 5), foo1, foo2, foo3]) )
['foo3:11', 'foo3:12', 'foo3:13', 'foo3:14', 'foo3:15']

如果您的示例中的函数是一次性(或一次性)函数,下面是另一个答案。一些很好的变量命名和生成器表达式的使用对于小操作很有帮助

>>> g = range(0, 5)
>>> foo1 = (x+1 for x in g)
>>> foo2 = (x+10 for x in foo1)
>>> foo3 = ('foo3:' + str(x) for x in foo2)
>>> for x in foo3:
...     print x
...
foo3:11
foo3:12
foo3:13
foo3:14
foo3:15

您可以使用以下命令组合当前生成器函数:

另一种选择是从Monad开始,使用fmap调用组合生成器-Java 8流用户熟悉这种语法:

def main():
    odds = Just(["1", "22", "333", "4444", "55555"]) \
        .fmap(lengths) \
        .fmap(non_divisibles(2)) \
        .fmap(list) \
        .getValue()
    print(odds)   #prints [1, 3, 5]


def lengths(words: Iterable[Sized]) -> Iterable[int]:
    return map(len, words)


@curry
def non_divisibles(div: int, numbers: Iterable[int]) -> Iterable[int]:
    return (n for n in numbers if n % div)
注意,在这种情况下,函数不需要用@curry修饰。直到终端getValue()调用时,才会计算整个转换链

我不认为foo3(foo2(foo1(范围(0,5)))是实现我的管道目标的pythonic方法。尤其是当管道中的级数较大时

在我看来,有一种相当简单的链接生成器的方法:将每个生成器的结果分配给一个变量,每个变量都可以有一个描述性名称

range\u iter=range(0,5)
foo1\u iter=foo1(范围\u iter)
foo2_iter=foo2(foo1_iter)
foo3_iter=foo3(foo2_iter)
对于foo3_iter中的i:
印刷品(一)
我更喜欢使用高阶函数,例如a
reduce
或类似函数:

  • 在我的实际例子中,通常每个foo*生成器函数都需要自己的其他参数,如果使用
    reduce
    ,这是很棘手的

  • 在我的实际案例中,管道中的步骤在运行时不是动态的:对于我来说,拥有一个更适合动态案例的模式似乎有点奇怪/出乎意料

  • 这与常规函数的编写方式有点不一致,其中每个函数都被显式调用,并且每个函数的结果都被传递给下一个函数的调用。是的,我想有点重复,但我很高兴“调用函数”被重复,因为(对我来说)它真的很清楚

  • 无需导入:它使用核心语言功能


可能与或?(两者都是标准库的一部分)这听起来像是XY问题。如果你正在对数组/列表进行数字压缩,考虑使用NUMPY/BANDAS。Max的答案可能是最好的,但是你也可以用狡猾的方式滥用操作符重载(这在Python中是不赞成的),请参见此为灵感:除了比链式<代码> FoO(Foo1(…))效率低之外。即使有很多函数,这也不完全是我所说的更具可读性的。好吧,这是一个品味问题,你可以像@john1024那样为它定义一个别名,但对我来说,我只是将模式识别为一个管道,并将重点放在函数列表上作为有意义的部分,主要的是它们是从左到右管道化的而不是在question@MSeifert-这是如何降低效率的?我觉得效率是一样的。@mseifer:现在想象一下,当你有10-20个不同的生成器,它们的名称实际上是描述性的。是的,如果在更大的数据集上操作,效率不会受到影响。我不认为answer是错误的,或者不值得推荐。我只是想指出,在我看来,像
foo1(foo2(…)
这样的东西从长远来看更具可读性。但是,如果你有1000多个函数,它将不起作用!我认为将函数按相反的顺序排列是一种混乱。如果len(ops)
return ops[-1](repur(ops[:-1]),那么
return ops[-1](repur(ops[:-1])怎么办>1 else ops[0]
@rbierman非常好。是的,也可以。答案更新为反向订购代码。
def main():
    odds = list * \
         non_divisibles(2) * \
         lengths * \
         Just(["1", "22", "333", "4444", "55555"])
    print(odds.getValue())    #prints [1, 3, 5]


@curry
def lengths(words: Iterable[Sized]) -> Iterable[int]:
    return map(len, words)


@curry
def non_divisibles(div: int, numbers: Iterable[int]) -> Iterable[int]:
    return (n for n in numbers if n % div)

def main():
    odds = Just(["1", "22", "333", "4444", "55555"]) \
        .fmap(lengths) \
        .fmap(non_divisibles(2)) \
        .fmap(list) \
        .getValue()
    print(odds)   #prints [1, 3, 5]


def lengths(words: Iterable[Sized]) -> Iterable[int]:
    return map(len, words)


@curry
def non_divisibles(div: int, numbers: Iterable[int]) -> Iterable[int]:
    return (n for n in numbers if n % div)