用于循环步骤

时间:2013-10-04 01:00:43

标签: python reflection for-loop operators

我创建了一个python脚本,可以根据指定的步骤函数循环一系列值。

#!/usr/bin/python

def mul(value, step): return value * step
def inc(value, step): return value + step
def step_range(start, end, step, func):
    while start <= end:
        yield start
        start = func(start, step)

def main():
    for x in step_range(1, 64, 2, mul): 
        print '%d, '%(x),
    print
    for x in step_range(0, 64, 8, inc): 
        print '%d, '%(x),

if __name__ == '__main__':
    main()

输出:

$ python test.py
1,  2,  4,  8,  16,  32,  64,
0,  8,  16,  24,  32,  40,  48,  56,  64,

无论如何我可以摆脱辅助功能,以便用户可以做这样的事情吗?

for x in step_range(1, 64, *2): 
    ...

def step_range(start, end, step):
    while start <= end:
        yield start
        start = start ?step?

?标记我难倒的地方......我查看了operator模块,但我必须知道mul(a, b)add(a, b)函数的两个参数。

5 个答案:

答案 0 :(得分:4)

更明确的方法可能是

def step_range(start, end, func):
    while start <= end:
        yield start
        start = func(start)

然后,例如,你可以做

[a for a in step_range(1, 10, lambda x: x * 2)] # [1, 2, 4, 8]

这样你就不仅限于乘法或加法。当你读它时,它也更加Pythonic和清晰。

答案 1 :(得分:1)

没有办法摆脱这个功能。调用者不能传递要计算的表达式,只能传递要调用的函数。 (另外,*2甚至不是表达式,它只是表达式的一个片段......)


但是除了def之外,Python还有很多方法可以构建函数。例如,有lambda和更高阶函数,例如partial

>>> for x in step_range(1, 64, lambda x: x*2):
...     pass
>>> from functools import partial
>>> from operator import mul
>>> for x in step_range(1, 64, partial(mul, 2): 
...     pass

但这取决于来电者,而不是来电者。

(如果您习惯使用Haskell和其他使用curried函数的函数式语言,那么您可以将(*) 22 *作为函数传递,partial就是你的方式手动执行此操作,operator.mul相当于(*)。)


如果这还不够好,您可以通过构建(或搜索并安装)“表达式树库”来创建“快速lambdas”。然后你可以编写这样的代码:

>>> for x in step_range(1, 64, _1 * 2):
...     pass

或者您可以使用像MacroPy这样的宏处理器。


如果你真的,真的想要(而且你真的,真的没有,但只是为了完整性......),你可以采用表达式片段的字符串表示,完成它,然后eval正确的背景:

>>> for x in step_range(1, 64, "*2"):
...     pass

但我不会在step_range内展示如何做到这一点,因为你真的,真的不想这样做。

答案 2 :(得分:1)

我导入了operator,我相信我想出了一个很好的解决方案:

我保持原始的step_range()功能完整,我摆脱了助手。我还添加了在递减值的情况下修改条件的功能。

#!/usr/bin/python

import operator

def step_range(start, end, step, step_func, compare_func):
    while compare_func(start, end):
        yield start
        start = step_func(start, step)

def main():
    print [x for x in step_range(1, 64, 2, operator.mul, operator.le)]
    print [x for x in step_range(64, 0, 2, operator.div, operator.gt)]
    print [x for x in step_range(0, 64, 8, operator.add, operator.le)]
    print [x for x in step_range(64, 0, 8, operator.sub, operator.ge)]

if __name__ == '__main__':
    main()

输出:

$ python test.py
[1, 2, 4, 8, 16, 32, 64]
[64, 32, 16, 8, 4, 2, 1]
[0, 8, 16, 24, 32, 40, 48, 56, 64]
[64, 56, 48, 40, 32, 24, 16, 8, 0]

感谢帮助人员!

答案 3 :(得分:0)

你想要一个lambda或一个闭包。基本上,您是要传递未命名的函数,还是将状态绑定到函数并将函数传递给状态?

答案 4 :(得分:0)

这样做的一种破坏方法是构造一个笨拙的字符串,如下所示:

import operator

functions = {"*": operator.mul,
             "+": operator.add}

def step_range(start, end, step_string):
    """The function generates all the elements from start to end by
    successively applying the step_string.

    start and end are integers
    step_string is a string formatted like this:
    - the first character is either "+" or "*"
    - the rest of the string forms a number

    Some doctests:

    >>> [x for x in step_range(1, 64, "*2")]
    [1, 2, 4, 8, 16, 32, 64]

    >>> [x for x in step_range(0, 64, "+8")]
    [0, 8, 16, 24, 32, 40, 48, 56, 64]

    """
    func_sign, param = step_string[0], step_string[1:]

    func = functions[func_sign]


    while start <= end:
        yield start
        start = func(start, int(param))