我对成语和可读性有疑问,对于这个特例,似乎存在Python哲学的冲突:
我想从字典B中构建字典A.如果B中不存在特定键,则不执行任何操作并继续。
哪种方式更好?
try:
A["blah"] = B["blah"]
except KeyError:
pass
或
if "blah" in B:
A["blah"] = B["blah"]
“做并请求原谅”与“简单明了”。
哪个更好,为什么?
答案 0 :(得分:62)
例外不是条件限制。
条件版本更清晰。这很自然:这是简单的流量控制,这是条件的设计,而不是例外。
在循环中执行这些查找时,异常版本主要用作优化:对于某些算法,它允许从内部循环中消除测试。它没有这个好处。它有一个很小的优点,它可以避免两次说"blah"
,但如果你做了很多这样的事情,你应该有一个帮助move_key
函数。
一般情况下,我强烈建议默认情况下坚持使用条件版本,除非您有特殊原因不这样做。条件语是显而易见的方式,这通常强烈建议您选择一种解决方案而不是另一种解决方案。
答案 1 :(得分:49)
还有第三种方法可以避免异常和双重查找,如果查找很昂贵,这可能很重要:
value = B.get("blah", None)
if value is None:
A["blah"] = value
如果您希望字典包含None
值,您可以使用更为深奥的常量,例如NotImplemented
,Ellipsis
或创建一个新常量:
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)
答案 2 :(得分:14)
根据我的理解,你想用dict B中的键值对更新dict 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}
>>>
答案 3 :(得分:7)
Python性能维基直接引用:
除了第一次,每次看到一个单词时,if语句的测试失败。如果要计算大量单词,很多单词可能会出现多次。在一个值的初始化只发生一次并且该值的增加将多次发生的情况下,使用try语句会更便宜。
因此,似乎两种选择都是可行的,具体取决于情况。有关详细信息,您可以查看以下链接:Try-except-performance
答案 4 :(得分:3)
我认为这里的一般规则是A["blah"]
通常会存在,如果是这样尝试 - 除非好,否则请使用if "blah" in b:
我认为“尝试”在时间上便宜但“除了”更贵。
答案 5 :(得分: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})
答案 6 :(得分:2)
其他语言中的规则是为特殊条件保留例外,即在常规使用中不会发生的错误。不知道该规则如何适用于Python,因为该规则不应存在StopIteration。
答案 7 :(得分:1)
就个人而言,我倾向于第二种方法(但使用has_key
):
if B.has_key("blah"):
A["blah"] = B["blah"]
这样,每个赋值操作只有两行(而不是4行,有try / except),任何抛出的异常都将是真正的错误或你错过的东西(而不仅仅是试图访问那些不是'那里)。
事实证明(请参阅您问题的评论),has_key
已被弃用 - 所以我猜它最好写成
if "blah" in B:
A["blah"] = B["blah"]
答案 8 :(得分:1)
除了讨论可读性之外,我认为性能在某些情况下也很重要。快速 timeit 基准测试表明测试(即“请求许可”)实际上比处理异常(即“请求宽恕”)略快。
这是设置基准的代码,生成一个较大的随机键值对字典:
setup = """
import random, string
d = {"".join(random.choices(string.ascii_letters, k=3)): "".join(random.choices(string.ascii_letters, k=3)) for _ in range(10000)}
"""
然后是 if
测试:
stmt1 = """
key = "".join(random.choices(string.ascii_letters, k=3))
if key in d:
_ = d[key]
"""
给我们:
>>> timeit.timeit(stmt=stmt1, setup=setup, number=1000000)
1.6444563979999884
而利用异常的方法
stmt2 = """
key = "".join(random.choices(string.ascii_letters, k=3))
try:
_ = d[key]
except KeyError:
pass
"""
给我们:
>>> timeit.timeit(stmt=stmt2, setup=setup, number=1000000)
1.8868465850000575
有趣的是,将 key
代从实际基准测试提升到设置中,然后一遍又一遍地寻找相同键,提供了截然不同的数字:
>>> timeit.timeit(stmt=stmt1, setup=setup, number=100000000)
2.3290171539999847
>>> timeit.timeit(stmt=stmt2, setup=setup, number=100000000)
26.412447488999987
我不想推测这是否强调了测试与异常处理的好处,或者字典是否缓冲了先前查找的结果,从而使基准测试结果偏向于测试......?
答案 9 :(得分:0)
为什么不这样做:
def try_except(x,col):
try:
return x[col]
except:
return None
list(map(lambda x: try_except(x,'blah'),A))
答案 10 :(得分:0)
从@Suppress("UNCHECKED_CAST")
@Throws(Exception::class)
fun addAdditionalEnvironmentVariables(additionalEnvironmentVariables: Map<String, String>) {
try {
val processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment")
val theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment")
theEnvironmentField.isAccessible = true
val env = theEnvironmentField.get(null) as MutableMap<String, String>
env.putAll(additionalEnvironmentVariables)
val theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment")
theCaseInsensitiveEnvironmentField.isAccessible = true
val cienv = theCaseInsensitiveEnvironmentField.get(null) as MutableMap<String, String>
cienv.putAll(additionalEnvironmentVariables)
} catch (e: NoSuchFieldException) {
val classes = Collections::class.java.getDeclaredClasses()
val env = System.getenv()
for (cl in classes) {
if ("java.util.Collections\$UnmodifiableMap" == cl.getName()) {
val field = cl.getDeclaredField("m")
field.setAccessible(true)
val obj = field.get(env)
val map = obj as MutableMap<String, String>
map.clear()
map.putAll(additionalEnvironmentVariables)
}
}
}
}
开始并引入assignment expressions (PEP 572)(Python 3.8
运算符),我们可以在变量:=
中捕获条件值dictB.get('hello', None)
,以便都检查它是否不是value
(因为None
返回关联的值或dict.get('hello', None)
),然后在条件的正文中使用它:
None
答案 11 :(得分:0)