Выбор, сделанный Python 3.5, чтобы выбрать ключи при сравнении их в словаре
при построении словаря следующим образом:
dict = { True: 'yes', 1: 'No'}
когда я запускаю его в интерактивном интерпретаторе Python, дикт представлен следующим образом:
dict = {True: 'No'}
Я понимаю, что значения True и 1 равны из-за приведения типа, потому что при сравнении числовых типов суженный тип расширяется до другого типа (boolean является дочерним целым числом). Так как я понял из документации, когда мы вводим True == 1 Python конвертирует True to 1 и сравнивает их.
чего я не понимаю, почему True выбирается в качестве ключа вместо 1.
Я что-то пропустил?
6 ответов:
словари реализованы в виде хэш-таблиц и есть два важных понятия при добавлении ключей / значений здесь:хеширования и равенство.
чтобы вставить определенный ключ / значение, Python сначала вычисляет хэш значение ключа. Это хэш-значение используется для определения строки таблицы, где Python должен сначала попытаться поместить ключ/значение.
если строка хэш-таблицы пуста, отлично: новый ключ / значение может вставляться в словарь, заполняя пустую строку.
однако, если в этой строке уже что-то есть, Python должен проверить ключи на равенство. Если ключи равны (с помощью
==) тогда они считаются одним и тем же ключом, и Python просто нужно обновить соответствующее значение в этой строке.(если ключи не равны Python смотрит на другие строки в таблице, пока не найдет ключ или не достигнет пустой строки, но это не относится к этому вопросу.)
когда вы пишите
{True: 'yes', 1: 'No'}, вы говорите Python, чтобы создать новый словарь, а затем заполнить его с двумя парами ключ/значение. Они обрабатываются слева направо:True: 'yes'затем1: 'No'.у нас есть
hash(True)равен 1. КлючTrueвходит в строку 1 в хэш-таблице и строку'yes'- это его стоимость.для следующей пары Python видит, что
hash(1)также 1 и так смотрит на строку 1 таблицы. Там что-то уже есть, так что теперь Python проверяет ключи на равенство. У нас есть1 == Trueтак1считается тем же ключом, что иTrueи так его соответствующее значение изменяется на строку'No'.это приводит к словарю с одной записью:
{True: 'No'}.
если вы хотите посмотреть на кишки CPython 3.5, чтобы увидеть, что создание словаря выглядит ниже уровня surface-Python, вот более подробно.
Python-код
{True: 'yes', 1: 'No'}разбирается в токены и отдаются компилятору. Учитывая синтаксис, Python знает, что словарь должен быть создан с использованием значений внутри фигурных скобок. Байтовый код для загрузки четырех значений в стек виртуальной машины (LOAD_CONST), а затем построить словарь (BUILD_MAP) стоит в очереди.четыре постоянных значения помещаются в верхнюю часть стека в том порядке, в котором они видны:
'No' 1 'yes' Trueкод
BUILD_MAPзатем вызывается с аргументом2(Python насчитал две пары ключ / значение). Этот опкод отвечает за создание словаря из элементов в стеке. Это выглядит как этой:TARGET(BUILD_MAP) { int i; PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; for (i = oparg; i > 0; i--) { int err; PyObject *key = PEEK(2*i); PyObject *value = PEEK(2*i - 1); err = PyDict_SetItem(map, key, value); if (err != 0) { Py_DECREF(map); goto error; } } while (oparg--) { Py_DECREF(POP()); Py_DECREF(POP()); } PUSH(map); DISPATCH(); }три ключевых шага здесь заключаются в следующем:
пустая хэш-таблица создается с помощью
_PyDict_NewPresized. Небольшие словари (всего несколько элементов, как 2 в этом случае) нужна таблица с восемью строками.The
forцикла, начиная с 2 (в данном случае) и отсчет до 0.PEEK(n)- это макрос, который указывает на N-й элемент в стеке. Поэтому на первой итерации цикла мы будем иметьPyObject *key = PEEK(2*2); /* item 4 down the stack */ PyObject *value = PEEK(2*2 - 1); /* item 3 down the stack */это означает, что
*keyбудетTrueи*valueбудет'yes'на первом цикле. На втором это будет1и'No'.
PyDict_SetItemвызывается в каждом цикле поставить тег*keyи*valueв словарь. Это та же функция, которая вызывается при записиdictionary[key] = value. Он вычисляет хэш ключа, чтобы определить, где искать сначала в хэш-таблице, а затем, если необходимо, сравнить ключ с любым существующим ключом в этой строке (как описано выше).
основное помещение -
Trueи1имеют одинаковые хэши и равны друг другу - поэтому они не могут быть отдельными ключами в хэш - таблице (технически неравный объект с одинаковыми хэшами может-но хэш-коллизии снижают производительность).>>> True == 1 True >>> hash(1) 1 >>> hash(True) 1теперь давайте рассмотрим байт-кода:
import dis dis.dis("Dic = { True: 'yes', 1: 'No'}")печатается:
0 LOAD_CONST 0 (True) 3 LOAD_CONST 1 ('yes') 6 LOAD_CONST 2 (1) 9 LOAD_CONST 3 ('No') 12 BUILD_MAP 2 15 STORE_NAME 0 (Dic) 18 LOAD_CONST 4 (None) 21 RETURN_VALUEв основном происходит то, что литерал dict маркируется ключами и значениями, и они выталкиваются в стек. После этого
BUILD_MAP 2кроет две пары (ключи, значения) в словарь.скорее всего, порядок данных в стеке (который, по-видимому, определяется порядком ключей в литерале dict) и детали реализации
BUILD_MAPпринимает решение о результирующих ключах и значениях dict.похоже, что присвоение ключевых значений выполняется в порядке, определенном в литерале dict. Назначение ведет себя так же, как
d[key] = valueоперация, так что это в основном:
- если
keyне в dict (по равенству): добавитьkeydo dict- магазине
valueподkeyдано
{True: 'yes', 1: 'No'}:
- Начнем с
{}добавить
(True, 'yes')
- правда не в дикт ->
(True, hash(True))==(True, 1)это новый ключ в dict- значение ключа, равное
1до'yes'добавить
(1, 'no')
1находится в dict (1 == True) - > нет необходимости в новом ключе в словаре- значение ключа, равное
1(True) стоимостью'no'результат:
{True: 'No'}как я уже говорил, я не знаю, гарантируется ли это спецификациями Python или это просто поведение, определяемое реализацией CPython, оно может отличаться в других реализациях интерпретатора.
Trueи1разные объекты, но они оба имеют одинаковое значение:>>> True is 1 False >>> True == 1 TrueЭто похоже на две строки, которые могут иметь одинаковое значение, но хранятся в разных ячейках памяти:
>>> x = str(12345) >>> y = str(12345) >>> x == y True >>> x is y Falseсначала один элемент добавляется в словарь; затем, когда добавляется другой,это значение уже существует как ключ. Таким образом, ключ обновляется, значения ключей уникальны.
>>> {x: 1, y: 2} {"12345": 2}
если ключ уже присутствует в словаре, он не переопределяет ключ только связанное значение.
Я считаю, что писать!--1--> это по строкам:
x = {} x[True] = "a" x[1] = "b"и до достижения
x[1] = "b"ключTrueуже в диктанте, так зачем его менять? почему бы просто не переопределить значения.
заказ, кажется, причина. Пример кода:
>>> d = {True: 'true', 1: 'one'} >>> d {True: 'one'} >>> d = {1: 'one', True: 'true'} >>> d {1: 'true'}
Это неоднозначное утверждение.
логика:
d = { True: 'no', 1: 'yes'}когда python вычисляет выражение, он делает это последовательно, поэтому он эквивалентен этому.
d = dict() d[True] = 'no' d[1] = 'yes'константа True является ключом, но она оценивается в 1, поэтому вы просто устанавливаете значение для ключа дважды.
Comments