奇怪的dict.get行为

时间:2016-07-28 11:33:08

标签: python dictionary

似乎即使密钥存在于字典中,也会调用回退。这是预期的行为吗?怎么解决它?

>>> i = [1,2,3,4]
>>> c = {}
>>> c[0]= 0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop from empty list

6 个答案:

答案 0 :(得分:1)

执行c.get(0, i.pop())时,i.pop()部分会在之前评估,它返回的结果会传递给c.get(...)。这就是因为如果列表i由于先前的.pop()调用而为空而出现错误的原因。

为了解决这个问题,您应该在尝试从中弹出元素之前检查列表是否为空,或者只是尝试捕获可能的异常:

if not i:
    # do not call do i.pop(), handle the case in some way

default_val = i.pop()

try:
  c.get(0, i.pop())
except IndexError:
    # gracefully handle the case in some way, e.g. by exiting

default_val = i.pop()

第一种方法称为LBYL(&#34;在你跳跃之前看看&#34; ),而第二种方法称为EAFP(&#34;更容易要求宽恕而不是许可&#34; )。后者在Python中通常是首选,并且被认为更加Pythonic,因为代码不会被大量的安全检查混乱,尽管LBYL方法也有其优点,并且可以像可读的一样(依赖于用例)。 / p>

答案 1 :(得分:1)

这是预期的结果,因为您直接调用>>> query = "String: {} Number: {}" >>> param = ['text', 1] >>> query.format(*(repr(i) for i in param)) "String: 'text' Number: 1" 之前调用的i.pop()

答案 2 :(得分:1)

在字典检查密钥是否存在之前,确实会对dict.get的默认参数进行评估。事实上,它甚至在调用get方法之前进行了评估!您的get来电与此相同:

default = i.pop() # this happens unconditionally
c.get(0, default)
default = i.pop() # this one too
c.get(0, default)
#...

如果要指定仅用于填充缺少的字典值的可调用对象,则可能需要使用collections.defaultdict。它需要一个可调用的,完全按照这种方式使用:

c = defaultdict(i.pop) # note, no () after pop
c[0] = 0
c[0] # use regular indexing syntax, won't pop anything

请注意,与get调用不同,调用返回的值实际上会在之后存储在字典中,这可能是不合需要的。

答案 3 :(得分:1)

这是预期的行为,因为i.pop()是在c.get(...)之前计算的表达式。想象一下,如果不是这样的话会发生什么。你可能有这样的事情:

def myfunction(number):
    print("Starting work")
    # Do long, complicated setup
    # Do long, complicated thing with number

myfunction(int('kkk'))

您何时会对int('kkk')进行评估?只要myfunction()使用它(在参数之后)就可以了吗?在漫长而复杂的设置之后,它最终将具有ValueError。如果您要说x = int('kkk'),您何时会期望ValueError?首先评估右侧,并立即发生ValueError。 x未定义。

有几种可能的解决方法:

c.get(0) or i.pop()

这可能适用于大多数情况,但如果c.get(0)可能返回不是None的Falsey值,则无法工作。更安全的方式是更长一点:

try:
    result = c[0]
except IndexError:
    result = i.pop()

当然,我们喜欢EAFP(更容易请求宽恕而不是许可),但您可以请求许可:

c[0] if 0 in c else i.pop()

(致@soon的信用)

答案 4 :(得分:1)

除了使用 if ... else ... 之外,没有其他方法可以解决此问题!
在您的情况下,此代码将起作用:

c[0] if 0 in c else i.pop()

答案 5 :(得分:0)

在调用get函数之前,都会计算两个参数。因此,在所有调用中,即使存在密钥,列表的大小也会减少1。

尝试类似的东西 如果c.has_key(0):     打印c [0] 其他:     打印i.pop()