Python dict文字是否会按照它编写的顺序进行评估?

时间:2015-01-26 18:49:32

标签: python dictionary

让我们说我在Python中遇到过这样的情况:

_avg = {'total':0.0, 'count':0}    # HACK: side-effects stored here

def procedure(n):
  _avg['count'] += 1
  _avg['total'] += n
  return n

def get_average():
  return _avg['total'] / _avg['count']

my_dict = {
  'key0': procedure(0),
  'key2': procedure(2),
  'key1': get_average()
}
assert(my_dict['key1'] == 1.0)

我知道my_dict.keys()的顺序是未定义的,但我想知道的是,通过像这样的文字的初始化是否保证按特定顺序发生。 my_dict['key1']的值是否始终为1.0

3 个答案:

答案 0 :(得分:19)

字典评估顺序应该与写入相同,但有一个outstanding bug 之前进行评估。 (该错误最终在Python 3.5中修复)。

引用reference documentation

  

Python从左到右评估表达式。

并从错误报告中获取:

  

运行以下代码显示"2 1 4 3",但在参考手册中   http://docs.python.org/reference/expressions.html#expression-lists   评估顺序描述为{expr1: expr2, expr3: expr4}

def f(i):
    print i
    return i

{f(1):f(2), f(3):f(4)}

和圭多说:

  

我坚持以前的观点:代码应该修复。它看起来不像是我的任务。

这个错误在Python 3.5中得到修复,因此在Python 3.4及更早版本中,仍然会在键之前评估值:

>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=4, micro=2, releaselevel='final', serial=0)
>>> def f(i):
...     print(i)
...     return i
... 
>>> {f(1):f(2), f(3):f(4)}
2
1
4
3
{1: 2, 3: 4}

由于您的代码不需要首先评估密钥,因此您的代码可以保证正常工作;即使在每个相应值之后评估键,仍然按顺序评估键值对。

答案 1 :(得分:6)

根据Python docs regarding evaluation order,这应该有明确定义的行为:

  

在以下几行中,表达式将按其后缀的算术顺序进行评估:

…
{expr1: expr2, expr3: expr4}
…

因此,无论dict中的项目以什么顺序结束迭代,文字字典表达式的值(和键!)将始终评估< / em>的顺序与我在Python源代码中出现的顺序相同。

答案 2 :(得分:2)

在反汇编的字节码中可以非常清楚地看到Python 3.4.2上的当前行为:在键之前评估值,而不是从左到右。

>>> dis.dis(lambda: {f('1'): f('2'), f('3'): f('4')})
  1           0 BUILD_MAP                2
              3 LOAD_GLOBAL              0 (f)
              6 LOAD_CONST               1 ('2')
              9 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             12 LOAD_GLOBAL              0 (f)
             15 LOAD_CONST               2 ('1')
             18 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             21 STORE_MAP
             22 LOAD_GLOBAL              0 (f)
             25 LOAD_CONST               3 ('4')
             28 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             31 LOAD_GLOBAL              0 (f)
             34 LOAD_CONST               4 ('3')
             37 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             40 STORE_MAP
             41 RETURN_VALUE

然而,这也说明了为什么这也不是那么容易解决的原因:STORE_MAP按此顺序预期值和键;更改顺序要么需要在每对之后添加ROT_TWO操作码,要么 STORE_MAP_EX操作码可以预期这些对被反转;第一个是性能下降,而第二个意味着要处理每个处理字节码的代码中的另一个操作码。