2015-01-26 8 views
13

Powiedzmy, że mam takiej sytuacji w Pythonie:Czy literał literowy Pythona zostanie oceniony w kolejności, w jakiej został napisany?

_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) 

wiem, że kolejność my_dict.keys() jest niezdefiniowana, ale zastanawiam się, co jest to, czy inicjalizacji poprzez dosłowne tak jest gwarantowane w określonej kolejności. Czy wartość my_dict['key1'] zawsze będzie równa 1.0 jako zapewniona?

Odpowiedz

18

słownik porządek ocena powinny być taka sama, jak napisane, ale jest outstanding bug gdzie wartości są oceniane przed kluczy. (Błąd został ostatecznie naprawiony w Pythonie 3.5).

Cytując z reference documentation:

Python ocenia wyrażenia od lewej do prawej.

iz raportu o błędzie:

Running następujący kod pokazuje "2 1 4 3", ale w podręczniku http://docs.python.org/reference/expressions.html#expression-lists kolejność ocena opisany jako {expr1: expr2, expr3: expr4}

def f(i): 
    print i 
    return i 

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

i Guido podanej :

Wciąż trzymam się mojej opinii: kod powinien zostać naprawiony. To nie przypomina mi zadania.

Ten problem został rozwiązany w Pythonie 3.5, więc na Pythonie 3.4 i wcześniej wartości są nadal oceniane przed klawiszy:

>>> 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} 

Ponieważ kod nie wymaga klawiszy należy najpierw ocenić, Twój kod ma gwarancję, że działa poprawnie; pary klucz-wartość są nadal oceniane w kolejności, nawet jeśli klucze są oceniane po każdej odpowiadającej wartości.

5

Według Python docs regarding evaluation order, to powinno być dobrze zdefiniowane zachowanie:

W kolejnych wierszach, wyrażenia będą oceniane w arytmetyczna kolejności ich przyrostków:

… 
{expr1: expr2, expr3: expr4} 
… 

Tak , niezależnie od tego, w jakiej kolejności pozycje w pliku dict kończą się ciągiem iterowanym, wartości (i klucze!) dosłownego wyrażenia słownikowego będą zawsze wynosić oszacowano w takiej samej kolejności, w jakiej pojawiają się one w kodzie źródłowym Pythona.

+0

Dobra rozmowa. Chociaż to naprawdę ma znaczenie dla twojego kodu, powinieneś prawdopodobnie to sprawdzić, szczególnie ze względu na przenośność. – Marcin

+4

Z wyjątkiem http://bugs.python.org/issue11205, który pokazuje, że istnieje błąd w implementacji Pythona z tą regułą. –

2

Obecne zachowanie w języku Python 3.4.2 może być wyraźnie widoczne w zdemontowanym bajcie kodu: wartości są obliczane przed klawiszami, a nie od lewej do prawej.

>>> 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 

Jednak ten pokazuje również, dlaczego to nie jest też tak prosto rozwiązać: wartości i klucze są oczekiwane przez STORE_MAP w tym celu; zmiana kolejności wymagałaby dodania po każdej parze kodu operacyjnego ROT_TWO lub kodu operacyjnego, który mógłby oczekiwać odwrócenia par; pierwszym byłby spadek wydajności, podczas gdy drugi oznaczałby jeszcze jeden kod operacyjny do obsługi każdego kodu, który zajmuje się bajtem kodu.

Powiązane problemy