`if key in dict` 与 `try/ except` - 哪个更易读?
我有一个关于习语和可读性的问题,对于这种特殊情况,Python 哲学似乎存在冲突:
我想从字典 B 构建字典 A。如果 B 中不存在特定键,则不执行任何操作并继续。
哪种方式更好?
try:
A["blah"] = B["blah"]
except KeyError:
pass
或
if "blah" in B:
A["blah"] = B["blah"]
“做并请求宽恕”与“简单和明确”。
哪个更好,为什么?
I have a question about idioms and readability, and there seems to be a clash of Python philosophies for this particular case:
I want to build dictionary A from dictionary B. If a specific key does not exist in B, then do nothing and continue on.
Which way is better?
try:
A["blah"] = B["blah"]
except KeyError:
pass
or
if "blah" in B:
A["blah"] = B["blah"]
"Do and ask for forgiveness" vs. "simplicity and explicitness".
Which is better and why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
还有第三种方法可以避免异常和双重查找,如果查找成本很高,这可能很重要:
如果您希望字典包含
None
值,则可以使用一些更深奥的常量例如NotImplemented
、Ellipsis
或创建一个新的:无论如何,使用
update()
对我来说是最具可读性的选项:There is also a third way that avoids both exceptions and double-lookup, which can be important if the lookup is expensive:
In case you expect the dictionary to contain
None
values, you can use some more esoteric constants likeNotImplemented
,Ellipsis
or make a new one:Anyway, using
update()
is the most readable option for me:例外不是条件。
条件版本更清晰。这是很自然的:这是简单的流程控制,这就是条件语句的设计目的,而不是例外。
异常版本主要用作在循环中进行这些查找时的优化:对于某些算法,它允许消除内部循环的测试。这里没有这个好处。它有一个小优点,那就是它避免了说两次“blah”,但如果你做了很多这样的事情,你可能应该有一个辅助函数 move_key 。
一般来说,我强烈建议默认情况下坚持使用条件版本,除非您有特定原因不这样做。条件是执行此操作的明显方式,这通常是优先选择一种解决方案而不是另一种解决方案的强烈建议。
Exceptions are not conditionals.
The conditional version is clearer. That's natural: this is straightforward flow control, which is what conditionals are designed for, not exceptions.
The exception version is primarily used as an optimization when doing these lookups in a loop: for some algorithms it allows eliminating tests from inner loops. It doesn't have that benefit here. It has the small advantage that it avoids having to say
"blah"
twice, but if you're doing a lot of these you should probably have a helpermove_key
function anyway.In general, I'd strongly recommend sticking with the conditional version by default unless you have a specific reason not to. Conditionals are the obvious way to do this, which is usually a strong recommendation to prefer one solution over another.
据我了解,您想用字典 B 中的键值对更新字典 A
更新
是一个更好的选择。示例:
From what I understand, you want to update dict A with key,value pairs from dict B
update
is a better choice.Example:
直接引用Python性能wiki:
因此,根据具体情况,这两种选择似乎都是可行的。有关更多详细信息,您可能想查看此链接: 尝试-除了性能
Direct quote from Python performance wiki:
So it seems that both options are viable depending from situation. For more details you might like to check this link out: Try-except-performance
我认为这里的一般规则是
A["blah"]
通常存在,如果是这样,则尝试-例外是好的,如果不存在则使用if "blah" in b:
I认为“尝试”在时间上是便宜的,但“除外”则更昂贵。
I think the general rule here is will
A["blah"]
normally exist, if so try-except is good if not then useif "blah" in b:
I think "try" is cheap in time but "except" is more expensive.
我认为除非此代码有意义,否则您应该采用第二个示例:
请记住,只要有一个不在
B
中的密钥,代码就会中止。如果这段代码有意义,那么您应该使用异常方法,否则使用测试方法。在我看来,因为它更短并且清楚地表达了意图,所以它比异常方法更容易阅读。当然,告诉您使用
update
的人是正确的。如果您使用支持字典理解的 Python 版本,我强烈推荐以下代码:I think the second example is what you should go for unless this code makes sense:
Keep in mind that code will abort as soon as there is a key that isn't in
B
. If this code makes sense, then you should use the exception method, otherwise use the test method. In my opinion, because it's shorter and clearly expresses the intent, it's a lot easier to read than the exception method.Of course, the people telling you to use
update
are correct. If you are using a version of Python that supports dictionary comprehensions, I would strongly prefer this code:其他语言中的规则是为特殊情况保留例外,即在常规使用中不会发生的错误。不知道该规则如何适用于 Python,因为根据该规则 StopIteration 不应该存在。
The rule in other languages is to reserve exceptions for exceptional conditions, i.e. errors that don't occur in regular use. Don't know how that rule applies to Python, as StopIteration shouldn't exist by that rule.
从
Python 3.8
开始,并引入赋值表达式 (PEP 572)(:=
运算符),我们可以在变量value< 中捕获条件值
dictB.get('hello', None)
/code> 以便检查它是否不是None
(因为dict.get('hello', None)
返回关联值或None
code>) 然后在条件体内使用它:Starting
Python 3.8
, and the introduction of assignment expressions (PEP 572) (:=
operator), we can capture the condition valuedictB.get('hello', None)
in a variablevalue
in order to both check if it's notNone
(asdict.get('hello', None)
returns either the associated value orNone
) and then use it within the body of the condition:除了讨论可读性之外,我认为在某些场景下性能也很重要。快速 timeit 基准测试表明测试(即“请求许可”)是实际上比处理异常(即“请求宽恕”)稍快一些。
下面是设置基准的代码,生成一个较大的随机键值对字典:
然后
if
测试:给我们:
而利用异常的方法
给我们:
有趣的是,提升
>密钥
从实际基准生成到设置中,然后一遍又一遍地寻找相同密钥,提供了截然不同的数字:我不想推测这是否强调了测试与异常处理,或者字典是否缓冲了先前查找的结果,从而使基准测试结果偏向于测试……
In addition to discussing readability, I think performance also matters in some scenarios. A quick timeit benchmark indicates that a test (i.e. “asking permission”) is actually slightly faster than handling the exception (i.e. “asking forgiveness”).
Here’s the code to set up the benchmark, generating a largeish dictionary of random key-value pairs:
Then the
if
test:gives us:
whereas the approach utilizing the exception
gives us:
Interestingly, hoisting the
key
generation from the actual benchmark into the setup and therewith looking for the same key over and over, delivers vastly different numbers:I don’t want to speculate whether this emphasizes the benefits of a test vs. exception handling, or if the dictionary buffers the result of the previous lookup and thus biases the benchmark results towards testing… ????
就我个人而言,我倾向于第二种方法(但使用
has_key
):这样,每个赋值操作只有两行(而不是使用 try/ except 的 4 行),并且抛出的任何异常都将是真实的错误或您错过的事情(而不是仅仅尝试访问不存在的密钥)。
事实证明(请参阅对您问题的评论),
has_key
已被弃用 - 所以我想最好写成Personally, I lean towards the second method (but using
has_key
):That way, each assignment operation is only two lines (instead of 4 with try/except), and any exceptions that get thrown will be real errors or things you've missed (instead of just trying to access keys that aren't there).
As it turns out (see the comments on your question),
has_key
is deprecated - so I guess it's better written as尽管所接受的答案强调“三思而后行”原则可能适用于大多数语言,但基于 python 原理,更多 pythonic 可能是第一种方法。更不用说它是 python 中合法的编码风格。重要的是确保您在正确的上下文中使用 try except 块并遵循最佳实践。例如。在 try 块中做太多事情,捕获非常广泛的异常,或者更糟糕 - 裸露的 except 子句等。
请参阅此处的 python 文档参考。
另外,这个来自核心成员之一 Brett 的博客开发人员,简要介绍了其中大部分内容。
请参阅此处<的另一个SO讨论< /a>:
Though the accepted answer's emphasize on "look before you leap" principle might apply to most languages, more pythonic might be the first approach, based on the python principles. Not to mention it is a legitimate coding style in python. Important thing is to make sure you are using the try except block in the right context and is following best practices. Eg. doing too many things in a try block, catching a very broad exception, or worse- the bare except clause etc.
See the python docs reference here.
Also, this blog from Brett, one of the core devs, touches most of this in brief.
See another SO discussion here: