`if key in dict` vs. try / except`-哪个更易读?


93

我有一个关于成语和可读性的问题,对于这种特殊情况,似乎存在Python哲学上的冲突:

我想从字典B构建字典A。如果B中不存在特定键,则什么也不做,继续。

哪种方法更好?

try:
    A["blah"] = B["blah"]
except KeyError:
    pass

要么

if "blah" in B:
    A["blah"] = B["blah"]

“请原谅”与“简单明了”。

哪个更好?为什么?


1
第二个示例可能更好地写为if "blah" in B.keys()if B.has_key("blah")
girasquid 2010年

2
A.update(B)不会为你工作?
SilentGhost

21
@Luke:has_key已弃用赞成in和检查B.keys()改变的O(1)操作成为O(n)之一。
kindall 2010年

4
@卢克:不是,不是。.has_key已被废弃,keys造成在py2k不需要的列表,并且是多余的py3k
SilentGhost

2
'build'A,例如,A是空的吗?而且我们只想要某些键?使用理解:A = dict((k, v) for (k, v) in B if we_want_to_include(k))
Karl Knechtel

Answers:


75

异常不是有条件的。

条件版本更清晰。这很自然:这是直接的流控制,这是条件设计的目的,而不是异常。

在循环中进行这些查找时,异常版本主要用作优化:对于某些算法,它允许从内部循环中消除测试。这里没有那个好处。它有一个小的优势,它避免了重复说"blah"两次,但是,如果您要进行很多次操作,则move_key无论如何您都应该具有辅助功能。

通常,除非您有特殊原因,否则我强烈建议默认情况下使用条件版本。有条件的是执行此操作的明显方法,通常强烈建议您优先选择一种解决方案。


3
我不同意 如果您说“做X,如果不行,就做Y”。反对有条件解决方案的主要原因是,您不得不增加编写"blah"频率,这会导致更容易出错。
glglgl

6
而且,尤其是在Python中,EAFP的使用非常广泛。
glglgl

8
对于Python以外的我所知道的任何语言,此答案都是正确的。
托马什Zato -恢复莫妮卡

3
如果您像在Python中使用条件一样使用异常,我希望其他人不必阅读它。
格伦·梅纳德

那么,最终判决是什么?:)
floatingpurr

60

还有第三种避免异常和双重查询的方法,如果查询很昂贵,这可能很重要:

value = B.get("blah", None)
if value is not None: 
    A["blah"] = value

如果您希望字典包含None值,则可以使用一些更深奥的常量,例如NotImplementedEllipsis或创建一个新的常量:

MyConst = object()
def update_key(A, B, key):
    value = B.get(key, MyConst)
    if value is not MyConst: 
        A[key] = value

无论如何,使用update()对我来说是最易读的选项:

a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)

14

据我了解,您想使用字典B中的键值对更新字典A

update 是更好的选择。

A.update(B)

例:

>>> A = {'a':1, 'b': 2, 'c':3}
>>> B = {'d': 2, 'b':5, 'c': 4}
>>> A.update(B)
>>> A
{'a': 1, 'c': 4, 'b': 5, 'd': 2}
>>> 

很抱歉,“如果B中不存在特定键”,应该更清楚一些,但是我只想复制B中存在特定键的值。并非全部在B.
LeeMobile 2010年

1
@LeeMobile -A.update({k: v for k, v in B.iteritems() if k in specificset})
五花八门

8

来自Python性能Wiki的直接报价:

除了第一次以外,每次看到一个单词时,if语句的测试都会失败。如果要计算大量单词,许多单词可能会多次出现。在一个值的初始化仅发生一次且该值的增加将发生多次的情况下,使用try语句会更便宜。

因此,根据情况,这两种选择似乎都是可行的。有关更多详细信息,您可能想查看此链接:Try-except-performance


这是一个有趣的读物,但我认为有些不完整。所使用的dict只有1个元素,我怀疑较大的dict将对性能产生重大影响
user2682863

3

我认为这里的一般规则A["blah"]通常会存在,如果这样,则try-except是好的,如果不是,则使用if "blah" in b:

我认为“尝试”在时间上很便宜,但“除外”则更昂贵。


10
默认情况下,不要从优化角度处理代码;从可读性和可维护性的角度进行处理。除非目标明确是优化,否则这是错误的标准(如果是优化,答案是基准测试,而不是猜测)。
格伦·梅纳德

我可能应该把最后一点放在方括号中或以某种方式含糊-我的主要观点是第一点,我认为它具有第二点的附加优势。
尼尔2010年

3

我认为第二个示例是您应该追求的,除非这段代码有意义:

try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

请记住,一旦没有密钥,代码将立即中止B。如果此代码有意义,则应使用异常方法,否则应使用测试方法。我认为,因为它更短并且清楚地表达了意图,所以它比异常方法更容易阅读。

当然,告诉您使用的人update是正确的。如果您使用的是支持字典理解的Python版本,则强烈建议您使用以下代码:

updateset = {'foo', 'bar', 'baz'}
A.update({k: B[k] for k in updateset if k in B})

“请记住,一旦B中没有键,代码将立即中止。” -这就是为什么最好的做法是仅在try:块中放入绝对最小值,通常这是一行。第一个示例作为循环的一部分会更好,例如for key in ["foo", "bar", "baz"]: try: A[key] = B[key]
Zim

2

其他语言的规则是为例外情况保留例外,即在常规使用中不会发生的错误。不知道该规则如何适用于Python,因为该规则不应该存在StopIteration。


我认为这种栗色源于异常处理成本很高的语言,因此会对性能产生重大影响。我从未见过任何真正的理由或理由。
John La Rooy 2010年

@JohnLaRooy不,性能并不是真正的原因。异常是一种非本地的goto,有人认为这会阻碍代码的可读性。但是,以这种方式使用异常在Python中被认为是惯用的,因此上述内容不适用。
伊恩·高德比

有条件的返回也是“非本地goto”,许多人更喜欢这种样式,而不是在代码块末尾检查标记。
考伯特

1

就个人而言,我倾向于第二种方法(但使用has_key):

if B.has_key("blah"):
  A["blah"] = B["blah"]

这样,每个赋值操作只有两行(而try / except则为4行),并且抛出的任何异常都将是真正的错误或您错过的事情(而不仅仅是尝试访问不存在的键) 。

事实证明(请参阅您的问题的评论),has_key已弃用-因此,我认为将其写为

if "blah" in B:
  A["blah"] = B["blah"]

1

从开始Python 3.8,并引入赋值表达式(PEP 572):=运算符),我们可以捕获条件值dictB.get('hello', None)到变量value中,以检查条件值是否不是Nonedict.get('hello', None)返回关联值或None),然后在条件:

# dictB = {'hello': 5, 'world': 42}
# dictA = {}
if value := dictB.get('hello', None):
  dictA["hello"] = value
# dictA is now {'hello': 5}

如果值== 0,则此操作失败
Eric

0

尽管公认的答案强调“跨越式先行”原则可能适用于大多数语言,但基于python原则,更多的pythonic可能是第一种方法。更不用说这是python中的合法编码风格。重要的是要确保在正确的上下文中使用try try块并遵循最佳实践。例如。在try块中做太多的事情,捕获到非常广泛的异常,或更糟的是-仅有的except子句等。

寻求宽恕比允许容易。(EAFP)

请参见Python文档参考这里

此外,来自Brett(核心开发人员之一)的博客简要介绍了其中的大部分内容。

在这里查看另一个SO讨论:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.