2015-01-26 51 views
13

比方說,我有在Python這樣的情況:將按照寫入的順序對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所聲稱的值?

回答

18

字典評價順序是相同的寫入,但有一個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)} 

和Guido指出:

我堅持我以前的看法:代碼應該是固定的。它看起來不屬於我的任務。

此錯誤是固定在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} 

因爲你的代碼不需要的鑰匙,先評估,您的代碼保證正常工作;即使在每個對應值之後評估鍵,鍵 - 值對仍然按順序評估。

5

按照Python docs regarding evaluation order,這應該有良好定義的行爲:

… 
{expr1: expr2, expr3: expr4} 
… 

所以:

在下面的線條,表達將在其後綴的運算順序進行評估,無論dict中的項目以迭代的順序排列,則文字字典表達式的值(和鍵!)將始終爲按照它們出現在我的Python源代碼中的順序評估

+0

良好的通話。儘管如果這對你的代碼真的很重要,你應該單元測試一下,特別是爲了便攜性的原因。 – Marcin 2015-01-26 18:51:29

+4

除了http://bugs.python.org/issue11205,它表明在該規則的Python實現中存在一個錯誤。 – 2015-01-26 18:55:34

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操作碼,該操作碼會期望對被顛倒;第一個是性能下降,而第二個意味着另一個操作碼處理每一段處理字節碼的代碼。