关于python:`如果键入dict`与`try / except` – 这是更具可读性的成语?

`if key in dict` vs. `try/except` - which is more readable idiom?

我有一个关于习语和可读性的问题,对于这个特定的案例,似乎有一个与Python哲学的冲突:

我想从字典B构建字典A。如果B中不存在特定的键,则不执行任何操作并继续。

哪条路更好?

1
2
3
4
try:
    A["blah"] = B["blah"]
except KeyError:
    pass

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

"做并请求宽恕"与"简单和直率"。

哪个更好,为什么?


例外不是条件。

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

异常版本主要用作在循环中进行这些查找时的优化:对于某些算法,它允许从内部循环中消除测试。这里没有这种好处。它的一个小优点是它避免了说两次"blah",但如果你做了很多这样的事情,你可能应该有一个助手move_key的功能。

一般来说,我强烈建议在默认情况下坚持使用条件版本,除非您有特定的理由不这样做。条件是实现这一点的明显方法,这通常是一种强烈的建议,即选择一种解决方案而不是另一种解决方案。


还有第三种方法可以避免出现异常和重复查找,如果查找成本很高,这一点很重要:

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

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

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

不管怎样,使用update()对我来说是最易读的选项:

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


据我所知,您希望使用dict b中的键、值对更新dict a

update是更好的选择。

1
A.update(B)

例子:

1
2
3
4
5
6
>>> 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}
>>>


从python performance wiki直接引用:

Except for the first time, each time a word is seen the if statement's test fails. If you are counting a large number of words, many will probably occur multiple times. In a situation where the initialization of a value is only going to occur once and the augmentation of that value will occur many times it is cheaper to use a try statement.

因此,根据具体情况,这两种选择似乎都是可行的。有关详细信息,请查看此链接:尝试性能除外


我认为第二个例子是,除非这段代码有意义,否则您应该选择:

1
2
3
4
5
6
try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

记住,只要有一个键不在B中,代码就会立即中止。如果这段代码有意义,那么应该使用异常方法,否则使用测试方法。在我看来,因为它较短,并且清楚地表达了意图,所以比异常方法更容易阅读。

当然,人们告诉你使用update是正确的。如果您使用的是支持字典理解的Python版本,那么我强烈希望使用以下代码:

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

我认为这里的一般规则是:A["blah"]通常存在吗?如果不存在,除非是好的,否则使用if"blah" in b:

我认为"尝试"在时间上是便宜的,但"例外"更贵。


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


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

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

这样,每个分配操作只有两行(而不是4行,带有try/except),任何抛出的异常都将是真正的错误或您遗漏的东西(而不是仅仅尝试访问不存在的键)。

事实证明(见对你问题的评论),has_key是不赞成的——所以我想最好写为

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


启动Python 3.8和引入赋值表达式(pep 572)(:=操作符),我们可以在变量value中捕获条件值dictB.get('hello', None),以检查它是否为None(因为dict.get('hello', None)返回关联值或None,然后在条件:

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

为什么不这么做:

1
2
3
4
5
6
7
def try_except(x,col):
    try:
        return x[col]
    except:
        return None

list(map(lambda x: try_except(x,'blah'),A))