EAFP/LBYL的并发性影响

4
在Python中编写并发/多线程代码时,遵循“宁愿请求原谅,而不是事先获得许可”(EAFP)习语尤为重要,而不是“先看再跳”(LBYL)。 Python的极其动态的特性意味着几乎任何事情(例如属性删除)都可能发生在看和跳之间 - 如果是这样,那还有什么意义呢?例如,请考虑:
# LBYL
if hasattr(foo, 'bar'):
    baz = foo.bar

对比。
# EAFP
try:
    baz = foo.bar
except AttributeError:
    pass

在 LBYL 示例中,调用 foo.bar 之前,foo 的属性 bar 可能会消失,那么这个检查有什么作用呢?如果存在属性可能消失的风险,您需要使用锁和/或 try/except 子句。
这里一个可能的论点是,此示例采取了极度悲观的假设,即可能运行“对抗性代码”,随时可能使您丧失优势。在大多数情况下,这种情况是非常不可能的。

只是想提醒一下,你不应该仅仅因为可以而随意添加/删除对象的属性。这是一种“在实际情况下使用时需要考虑其缺点”的决策。正如我们从你的问题中看到的那样,如果与并发问题结合使用,它可能会特别令人困惑。 - cdleary
完全同意——在这样做时,人们需要警觉与其相关的含义。 - zweiterlinde
1个回答

3

您的想法是正确的。以下是一些补充点:

如果属性大多数时候都存在,使用try:except:可能比LBYL习惯更快。

如果您不喜欢try: except:语法,也可以这样写:

item = getattr(foo, 'bar', None)
if item is None:
    ....
else:
    ....

1
+1:性能已经被优化到极致了——try/except通常更快,更可靠。对于其他线程的假设并不是“极度悲观”的,而是成功的关键。 - S.Lott
1
我不知道在CPython中,getattr是否保证是原子操作。GIL可能会使其成为原子操作,但在Jython或IronPython中,它可能不是原子操作。当然,上面的两步LBYL hasattr肯定不是原子操作。 - George V. Reilly
好观点,乔治。我猜在内部,我们有一个EAFP的try-catch块,如果出现任何问题就返回None。 - zweiterlinde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接