Python 仅当字典中存在键时才对值执行操作

Python 仅当字典中存在键时才对值执行操作,python,dictionary,styles,Python,Dictionary,Styles,我有以下片段,其中我找不到明确的获胜者。 你推荐哪种款式?还有比这些更好的吗 (假设self.task.flags是一个字典) 中的和[] if 'target_names' in self.task.flags: foo(self.task.flags['target_names']) 这会两次提到字典和键的名称 我还可以想象,这会在内部执行两次相同的查找, 丢弃第一种情况下的值(中的) get和临时变量 value = self.task.flags.get('target_nam

我有以下片段,其中我找不到明确的获胜者。 你推荐哪种款式?还有比这些更好的吗

(假设
self.task.flags
是一个字典)

  • 中的
    []

    if 'target_names' in self.task.flags:
        foo(self.task.flags['target_names'])
    
    这会两次提到字典和键的名称

    我还可以想象,这会在内部执行两次相同的查找, 丢弃第一种情况下的值(
    中的

  • get
    和临时变量

    value = self.task.flags.get('target_names')
    if value:
        foo(value)
    
    这消除了重复,但临时变量不是很优雅

    当发生以下情况时,其含义也与其他含义略有不同
    {'target\u names':None}
    出现在
    self.task.flags

  • 例外(“EAFP”)

    我听说在Python中通常鼓励EAFP,但我仍然担心 例外情况可能会很昂贵,尤其是在密钥丢失的情况下 出席率不高

    这也可能掩盖在
    foo
    中引发的任何异常

    try
    除了
    结构可能会在视觉上分散注意力; 当涉及到其他异常处理时,情况就更糟了

  • 丑陋的方式:

    # with impossibleValue being anything that can't possibly be one of your 
    # values, like self.task.flags or an Object created here and now, like i'll do now.
    impossibleValue = Object()
    
    # dict.get(key, defaultValue) returns dict[key] if key 
    # is in dict, otherwise returns defaultValue.
    value = self.task.flags.get('target_names', impossibleValue)
    if value != impossibleValue:
        foo(value)
    
    丑陋的方式:

    # with impossibleValue being anything that can't possibly be one of your 
    # values, like self.task.flags or an Object created here and now, like i'll do now.
    impossibleValue = Object()
    
    # dict.get(key, defaultValue) returns dict[key] if key 
    # is in dict, otherwise returns defaultValue.
    value = self.task.flags.get('target_names', impossibleValue)
    if value != impossibleValue:
        foo(value)
    

    真的,这取决于你。没有实质性的性能差异,主要担心的是小心第二种形式。如果您的值为null,因此它们不会触发“If/then”子句,那么您将错误地认为没有键

    a = {k:k**2 for k in range(1000000)}
    def nullfunc(*args, **kwargs):
        del args, kwargs
    
    def infunc(a):
        if 5000 in a:
            nullfunc(a[5000])
    
    def getvalue(a):
        value = a.get(5000)
        if value:
            nullfunc(value)
    
    def tryexcept(a):
        try:
            nullfunc(a[5000])
        except KeyError:
            pass
    
    
    In [12]: %timeit infunc(a)
    1000000 loops, best of 3: 238 ns per loop
    
    In [13]: %timeit getvalue(a)
    1000000 loops, best of 3: 256 ns per loop
    
    In [14]: %timeit tryexcept(a)
    1000000 loops, best of 3: 197 ns per loop
    
    In [15]: 
    

    真的,这取决于你。没有实质性的性能差异,主要担心的是小心第二种形式。如果您的值为null,因此它们不会触发“If/then”子句,那么您将错误地认为没有键

    a = {k:k**2 for k in range(1000000)}
    def nullfunc(*args, **kwargs):
        del args, kwargs
    
    def infunc(a):
        if 5000 in a:
            nullfunc(a[5000])
    
    def getvalue(a):
        value = a.get(5000)
        if value:
            nullfunc(value)
    
    def tryexcept(a):
        try:
            nullfunc(a[5000])
        except KeyError:
            pass
    
    
    In [12]: %timeit infunc(a)
    1000000 loops, best of 3: 238 ns per loop
    
    In [13]: %timeit getvalue(a)
    1000000 loops, best of 3: 256 ns per loop
    
    In [14]: %timeit tryexcept(a)
    1000000 loops, best of 3: 197 ns per loop
    
    In [15]: 
    

    第一个和第三个选项对我都有意义。我更愿意保留try/except,用于实际异常的情况,而不是预期的结果(例如用户错误)。如果国旗不经常出现在那里有意义的话,我个人更喜欢案例1。如果不存在标志,则没有代码要执行,则执行相反的操作也是有意义的,即:

    def foo(self, some_other_args):
        ... do something ...
        if 'target_names' not in self.task.flags:
            # Nothing further to do here, so return the result
            return result
    
        ... do something with self.task.flags['target_names'] ...
        return result
    

    然而,我知道有些人喜欢避免那样的多重回报。如果您真的希望字典中出现“target_names”,我认为您的案例3可能是更好的解决方案,因为它明确了目的(缺少的键是真正例外的,而不是标准案例)。

    第一个和第三个选项对我来说都很有意义。我更愿意保留try/except,用于实际异常的情况,而不是预期的结果(例如用户错误)。如果国旗不经常出现在那里有意义的话,我个人更喜欢案例1。如果不存在标志,则没有代码要执行,则执行相反的操作也是有意义的,即:

    def foo(self, some_other_args):
        ... do something ...
        if 'target_names' not in self.task.flags:
            # Nothing further to do here, so return the result
            return result
    
        ... do something with self.task.flags['target_names'] ...
        return result
    

    然而,我知道有些人喜欢避免那样的多重回报。如果您真的希望字典中出现“target_names”,我认为您的案例3可能是更好的解决方案,因为它明确了意图(缺少的键是真正例外的,而不是标准案例)。

    第三种方式,因为正如您所说,它符合EAFP

    请注意,在本例中,try/except子句可以写得更简洁(Python 3.4+):


    第三种方法,正如你所说,它符合EAFP

    请注意,在本例中,try/except子句可以写得更简洁(Python 3.4+):


    这完全取决于上下文。在不同的情况下,其中一种方法会比其他方法更优雅。第二种假设不是很好,因为如果值为False或None,或“”、0或任何null值,它将错误地不会触发函数调用。第二种方法只有在你知道值的可能范围时才有效。@AlexanderHuszagh:谢谢;我已经说明了其含义上的差异,尽管它没有包括其他假值(
    false
    0
    '
    )。但实际上,这是你想要的。上下文决定一切。我不建议选择2,但是。。。上下文很重要。@AlexanderHuszagh:谢谢。我应该补充一点,在最初的上下文中,我们可以安全地假设字典中的值不是假的
    foo(value)
    实际上类似于
    args.append('--target\u names=%s'%value)
    。它完全取决于上下文。在不同的情况下,其中一种方法会比其他方法更优雅。第二种假设不是很好,因为如果值为False或None,或“”、0或任何null值,它将错误地不会触发函数调用。第二种方法只有在你知道值的可能范围时才有效。@AlexanderHuszagh:谢谢;我已经说明了其含义上的差异,尽管它没有包括其他假值(
    false
    0
    '
    )。但实际上,这是你想要的。上下文决定一切。我不建议选择2,但是。。。上下文很重要。@AlexanderHuszagh:谢谢。我应该补充一点,在最初的上下文中,我们可以安全地假设字典中的值不是假的
    foo(value)
    实际上类似于
    args.append('--target\u names=%s'%value)
    。如果您打算建议这种方法,那么至少可以通过演示如何创建
    不可能的值来实现。。。最简单的方法是使用
    sentinel=object()
    来保证一个唯一的对象,然后使用身份检查,例如
    如果值不是sentinel
    …虽然这个答案可能是正确和有用的,但最好在回答的同时附上一些解释来解释它如何帮助解决问题。这在将来变得特别有用,如果有一个变化(可能无关)导致它停止工作,用户需要了解它曾经是如何工作的。注意,如果你要建议这种方法,那么至少通过演示如何创建
    I