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