Краткий ответ (TL; DR)
Это потому, что в первом тесте реализация CPython dict
создаст новый dict из списка, но во втором копируется только словарь. Копирование занимает меньше времени, чем разбор списка.
Дополнительная информация
Рассмотрим этот код:
import dis
dis.dis("dict([('foo', 1), ('bar', 'bar'), ('baz', 100)])", depth=10)
print("------------")
dis.dis("dict({'foo': 1, 'bar': 'bar', 'baz': 100})", depth=10)
Где
Модуль dis поддерживает анализ байт-кода CPython с помощью
разбирать его.
Что позволяет нам видеть выполненные операции с байт-кодом. Вывод показывает
1 0 LOAD_NAME 0 (dict)
2 LOAD_CONST 0 (('foo', 1))
4 LOAD_CONST 1 (('bar', 'bar'))
6 LOAD_CONST 2 (('baz', 100))
8 BUILD_LIST 3
10 CALL_FUNCTION 1
12 RETURN_VALUE
------------
1 0 LOAD_NAME 0 (dict)
2 LOAD_CONST 0 (1)
4 LOAD_CONST 1 ('bar')
6 LOAD_CONST 2 (100)
8 LOAD_CONST 3 (('foo', 'bar', 'baz'))
10 BUILD_CONST_KEY_MAP 3
12 CALL_FUNCTION 1
14 RETURN_VALUE
Из вывода вы можете увидеть:
- Оба вызова должны загрузить имя
dict
, которое будет вызываться.
- После этого первый метод загружает список в память (
BUILD_LIST
), тогда как второй создает словарь (BUILD_CONST_KEY_MAP
) (см. здесь )
- По этой причине, когда вызывается функция dict (шаг
CALL_FUNCTION
(см. здесь )), во втором случае она значительно короче, поскольку словарь уже создан, поэтому он просто делает копию вместо того, чтобы перебирать список для создания хеш-таблицы.
Примечание : с помощью байт-кода вы не можете окончательно решить, что CALL_FUNCTION
делает это, так как его реализация написана на C, и только читая его, вы действительно можете это знать (см. Ответ Martijn Pieters для точного объяснения того, как эта часть работает). Тем не менее, это помогает увидеть, как объект словаря уже создан за пределами dict()
(пошагово, а не синтаксически в примере), в то время как со списком это не так.
Редактировать
Чтобы было ясно, когда вы говорите
Существует несколько способов создания словаря в python
Это правда, что, делая:
dkwargs = {'foo': 1, 'bar': 'bar', 'baz': 100}
Вы создаете словарь в том смысле, что интерпретатор преобразует выражение в объект словаря, хранящийся в памяти, и указывает на него переменную dkwargs
. Однако, выполнив: dict(**kwargs)
или, если вы предпочитаете dict(kwargs)
, вы на самом деле не создаете словарь , а просто копируете уже существующий объект (и важно подчеркнуть копирование ):
>>> dict(dkwargs) is dkwargs
False
dict(kwargs)
заставляет Python создать новый объект; однако это не означает, что он должен перестроить объект. На самом деле, эта операция бесполезна, потому что на практике это равные объекты (хотя и не один и тот же).
>>> id(dkwargs)
2787648914560
>>> new_dict = dict(dkwargs)
>>> id(new_dict)
2787652299584
>>> new_dict == dkwargs
True
>>> id(dkwargs) is id(new_dict)
False
Где id:
Возвращает «идентичность» объекта. Это целое число, которое гарантированно будет уникальным и постоянным для этого объекта в течение его жизни [...]
Сведения о реализации CPython : Это адрес объекта в памяти.
Если, конечно, вы не хотите специально дублировать объект, чтобы изменить один, чтобы изменения не были связаны с другой ссылкой.