【问题标题】:Python3.8 `all()` not short circuitingPython3.8 `all()` 不短路
【发布时间】:2021-01-21 16:45:47
【问题描述】:

好吧,我的工作日可能很糟糕,但据我所知 all() 应该短路 according to the docs

all() 应该等同于:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

这也相当于菊花链and

所以下面应该做同样的事情:

all((True, print('This should print and stop'), print("this shouldn't print")))
True and print('This should print and stop') and print("this shouldn't print")

然而,最上面的两个都打印了(即,对每个部分都进行了全面评估) 但底部短路正常。

这是 Python (3.8.2) 的一个错误,还是我突然没有搞定?

回复示例:https://repl.it/join/scxbpnhc-syntactical01

【问题讨论】:

  • 在您的 all 中,第二个元素仍然是 Trueish,因为它不是空的。
  • print() 返回None,在等价表达式中被视为Falsey
  • 但是all 正在做它应该做的事情:它从语句中返回False,但需要运行print 来建立返回。运行它会导致打印...
  • 这与构造元组 t=(True, print('this'), False, print('not this')) 没有什么不同,这是在调用 all 之前完成的。
  • 是的。在精神上,当我这样做时,我的意思是它是一个发电机。第二次我意识到这是对 chepner 的回答的一个元组,我看到了我的缺陷以及与您的评论的关系。我仍然不同意“Trueish”评论,因为它从来都不是 Trueish,它只是被评估为构建一个元组。

标签: python python-3.x syntax


【解决方案1】:

all 内部的迭代会短路,但每次对 print 的调用都必须进行评估,以便在调用 all 之前创建作为参数传递的 tuple

没有def 语句,没有简单的方法可以在 Python 中创建任意惰性序列。你可以写

def foo():
    yield True
    yield print("This should print and stop")
    yield print("this shouldn't print")

all(foo())

但是没有等价的纯表达式。您需要遍历 something 以使用生成器表达式,例如

(x(y) for x, y in zip([id, print, print], 
                      [True, 
                       "This should print and stop",
                       "this shouldn't print"]))

会如您所愿。

>>> all(x(y) for x, y in zip([id, print, print], [True, "This should print and stop", "this shouldn't print"]))
This should print and stop
False

现在,printall 内部被调用,而不是作为评估传递给 all 的参数的一部分。

【讨论】:

  • Interesting.... print() 只是一个例子(我的真实代码有函数),但我不知道我不能使用 all 进行这样的延迟加载。使用zip 来解决它很有趣,但我想更改ands 更具可读性。谢谢。
  • 如果有一种方法可以编写像 (yield True, yield f(x), yield f(y)) 这样的东西作为生成器表达式,而不需要某种显式迭代,我会很高兴。
  • 我的第一个检查是看是否object is not None 然后第二个实际上是在使用该对象(加上更多)但是如果它没有短路我就会遇到问题,因此我正在使用None 对象。理想情况下,如果所有/任何除外 *args 都会很好,但 yield 将是一个有趣的向后兼容的解决方法。
  • 短路发生了,只是不在你想要的地方。对all 的参数进行评估,因此如果您想推迟函数调用,则必须将其包装在生成器中。
  • 如果有办法写出类似的东西,我会很高兴 请看我的回答。我想我给了你一个好的开始......
【解决方案2】:

Python 的all 取一个iterable as an argument

在构建问题中的示例期间,在这种情况下为元组,元组文字内的函数被调用,并将返回值插入到元组文字中;无异于:

>>> tup=(True, print('this'), False, print('not this'))
this
not this

如果你想要一种 即时组装,你可以这样做:

def example_func(*args):
    return args

g2all=(True, (print, 'this', 'and this'), False, (print, 'not this'), (example_func,1,2,3,4))

这取决于您可以按名称引用函数的事实,并且如果没有 () 调用运算符,该函数还不会被调用:

>>> g2all
(True, (<built-in function print>, 'this', 'and this'), False, (<built-in function print>, 'not this'), (<function example_func at 0x10d0a9430>, 1, 2, 3, 4))

然后您可以在 all 中使用它和生成器:

>>> all(f[0](*tuple(f[1:])) if isinstance(f, tuple) and callable(f[0]) else f for f in g2all)
this and this
False

all 找到 Falsie 值时,函数的调用将停止。

【讨论】:

  • 可能以牺牲可读性为代价。链接and 会更具可读性。这会检查可调用对象,但在我的现实生活用例中,我正在做一个 getatrr,例如:all(*x is not None, x.valid is True, x.attributes.is_hashable is True))。不过,您的解决方案可能对其他人有所帮助。
猜你喜欢
  • 2013-06-19
  • 2017-05-21
  • 2020-10-27
  • 1970-01-01
  • 2023-01-25
  • 2010-11-29
  • 2021-09-23
  • 2012-09-22
  • 2022-07-11
相关资源
最近更新 更多