Подтвердить что ты не робот

Будет ли литерал Python dict быть оценен в том порядке, в котором он написан?

Скажем, что у меня такая ситуация на 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() равен undefined, но мне интересно, возможно ли, что инициализация через такой литерал произойдет в определенном порядке. Будет ли значение my_dict['key1'] всегда соответствовать 1.0?

4b9b3361

Ответ 1

Порядок оценки словаря должен быть таким же, как и написанный, но есть выдающаяся ошибка, где оцениваются значения перед клавишами. (Исправлена ​​ошибка в Python 3.5).

Цитата из справочной документации :

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}

Поскольку ваш код не требует проверки ключей сначала, ваш код гарантированно работает правильно; пары ключ-значение по-прежнему оцениваются по порядку, даже если ключи оцениваются после каждого соответствующего значения.

Ответ 2

В соответствии с документами Python относительно порядка оценки, это должно иметь четко определенное поведение:

В следующих строках выражения будут вычисляться в арифметическом порядке их суффиксов:

…
{expr1: expr2, expr3: expr4}
…

Таким образом, независимо от того, какой порядок элементов в dict заканчивается итерацией, значения (и ключи!) словарного словаря всегда будут оцениваться в том же порядке, что и в моем исходном коде на Python.

Ответ 3

Текущее поведение на 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 код операции, который ожидает, что пары будут отменены; первым будет снижение производительности, тогда как второе будет означать еще один код операции для обработки в каждом фрагменте кода, который имеет дело с байт-кодом.