我如何确保在Python处理过程中不会急切地评估短路点之后的任意函数调用列表?

Ken*_*ama 3 python yield list short-circuiting python-3.x

例如,给定

def expensive_call(x):
    print(x)
    if x == "d":
        return x
def expensive_call_2(x, y):
    print(x)
    print(y)
    return x + y

a = [expensive_call("a"), expensive_call_2("b", "c"), expensive_call("d")]
next((e for e in a if e is not None), 'All are Nones')
Run Code Online (Sandbox Code Playgroud)

输出是

a
b
c
d
Out[22]: 'bc'
Run Code Online (Sandbox Code Playgroud)

由于expensive_call("d")急切地进行了评估,因此请注意,即使next在第二次呼叫出现呼叫短路且输出为“ bc”的情况下,也会打印“ d ”。

我正在对列表中的调用进行硬编码a,而a不必是列表数据结构。

一种可能的解决方案如下:

a = ['expensive_call("a")', 'expensive_call_2("b", "c")', 'expensive_call("d")']
def generator():
    for e in a:
        r = eval(e)
        if r is not None:
            yield r
next(generator(), 'All are Nones')
Run Code Online (Sandbox Code Playgroud)

输出是

a
b
c
Out[23]: 'bc'
Run Code Online (Sandbox Code Playgroud)

如预期的。但是,我真的不喜欢必须使用eval。我也不想使用任何最初将函数指针和参数分开的解决方案,例如(expensive_call, ("a"))。理想情况下,我会喜欢

a = lazy_magic([expensive_call("a"), expensive_call_2("b", "c"), expensive_call("d")])
next((e for e in a if e is not None), 'All are Nones')
Run Code Online (Sandbox Code Playgroud)

请注意,https://stackoverflow.com/a/3405828/2750819是一个类似的问题,但仅在函数具有相同的方法签名时才适用。

Pet*_*ood 5

您可以将它们全部放入函数中并产生结果:

def gen():
    yield expensive_call("a")
    yield expensive_call_2("b", "c")
    yield expensive_call("d")


result = next(
    (value for value in gen() if value is not None),
    'All are Nones')
Run Code Online (Sandbox Code Playgroud)

另一个解决方案是使用partial应用程序:

from functools import partial

calls = [partial(expensive_call, 'a'),
         partial(expensive_call_2, 'b', 'c'),
         partial(expensive_call, 'd')]
Run Code Online (Sandbox Code Playgroud)

然后评估:

next((result for call in calls
      for result in [call()]
      if result is not None),
     'All results None')
Run Code Online (Sandbox Code Playgroud)