Py学习  »  Python

在Python中进行处理时,如何确保任意函数调用的列表不会经过短路点而被急切地求值?

Kent Shikama • 6 年前 • 2171 次点击  

例如,给定

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

输出是

a
b
c
d
Out[22]: 'bc'

自从 expensive_call("d") 被热切地评价,注意“d”被打印即使 next 第二次呼叫时呼叫短路,输出为“BC”。

我把名单上的电话都硬编码了 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')

输出是

a
b
c
Out[23]: 'bc'

如所愿。但是,我不喜欢使用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')

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

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/48298
文章 [ 2 ]  |  最新文章 6 年前
rassar
Reply   •   1 楼
rassar    6 年前

您可以使用以下装饰器:

def lazy_fn(fn):
    return lambda *args: lambda: fn(*args)

(也可以表示为 lazy_fn = lambda fn: lambda *args: lambda: fn(*args) 如果你喜欢羊肉。)

像这样使用:

@lazy_fn
def expensive_call(x):
    print(x)
    if x == "d":
        return x

@lazy_fn
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")]
print(next((e for e in map(lambda i: i(), a) if e is not None), 'All are Nones'))

输出:

a
b
c
bc

注意不要使用 for e in a ,您需要使用 for e in map(lambda i: i(), a) 是的。

Peter Wood
Reply   •   2 楼
Peter Wood    6 年前

您可以将它们全部放入一个函数中,并生成结果:

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

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

from functools import partial

calls = [partial(expensive_call, 'a'),
         partial(expensive_call_2, 'b', 'c'),
         partial(expensive_call, 'd')]

然后评估:

next((result for call in calls
      for result in [call()]
      if result is not None),
     'All results None')