python - методы - питон collections counter




Как python вычисляет хэш кортежа (3)

Ни. Он рассчитывается на основе хэшей этих элементов, а не содержимого (значений).

Взгляните на этот параграф в глоссарии документации python .

Что-то хешируется или нет, и как оно хэшируется, зависит от реализации его метода .__hash__() . Сам Python понятия не имеет о изменчивости объекта.

В вашем первом примере tuple происходит с самим хешем на основе его элементов, в то время как list не имеет хеша вообще - метод .__hash__() не используется для него (и по уважительной причине). Вот почему tuple с объектом list внутри него не хешируется.

Теперь, имея это в виду, давайте посмотрим на документацию по модели данных python и что она должна сказать по теме:

По умолчанию пользовательские классы имеют __eq__() и __hash__() ; с ними все объекты сравниваются неравномерно (кроме самих себя) и x.__hash__() возвращает соответствующее значение, так что x == y подразумевает, что x is y и hash(x) == hash(y) .

Вот почему вам не нужно определять .__hash__() для ваших классов - python делает это для вас в этом случае. Однако реализация по умолчанию не использует поля экземпляра в учетной записи. Вот почему вы можете изменять значения внутри своего объекта, не изменяя его хэш.

В этом отношении вы правы - реализация по умолчанию ( CPython ) хеширования для пользовательских классов основана на id() объекта, а не на значениях внутри него. Это деталь реализации, но она отличается от версий Python. В более поздних версиях Python связь между hash() и id() связана с некоторой рандомизацией.

Но как это на самом деле хеш?

Хотя детали довольно сложны и, вероятно, связаны с некоторой продвинутой математикой, реализация хеш-функции для кортежных объектов записывается на C и может быть видна here (см. static Py_hash_t tuplehash(PyTupleObject *v) .

Расчет включает в себя XORing константу с хэшами каждого из элементов кортежа. Строка, отвечающая за хэширование элементов, такова:

y = PyObject_Hash(*p++);

Итак, чтобы ответить на ваш первоначальный вопрос: он делает кучу XOR hokus-pocus с хэшами каждого из своих элементов . Независимо от того, используется ли содержимое этих элементов, зависит их специфические хэш-функции.

В python, если у меня есть кортеж со многими элементами, является ли его хэш вычисленным из содержимого его элементов id или его элементов?

В этом примере,

a = (1, [1,2])
hash(a)

Он ошибочно говорит, что список раскалывается. Поэтому я предполагаю, что это не вычисляется по id, или, вероятно, есть проверка того, является ли элемент изменчивым.

Теперь посмотрим на этот пример

class A: pass
a0 = A()
ta = (1, a0)
hash(ta)  # -1122968024
a0.x = 20
hash(ta)  # -1122968024

Здесь оказывается, что хэш ta не изменяется с модификацией его элемента, т. a0 . Значит, для вычисления хеша используется идентификатор a0 ? Является ли a0 каким-то образом считаемым неизменным? Как python знает, является ли тип изменчивым?

Теперь рассмотрим этот случай

b = (1, 2)
id(b)  # 3980742764
c = (1, 2)
id(c)  # 3980732588
tb = (1, b)
tc = (1, c) 
hash(tb)  # -1383040070
hash(tc)  # -1383040070

Кажется, что содержание b и c используется для вычисления хэша.

Как я должен понимать эти примеры?


Основной контракт хэширования состоит в том, что равные объекты имеют равные хэши . В частности, хеширование непосредственно не заботится о изменчивости или мутации; он заботится только о мутации, которая влияет на сопоставления равенства .

Ваш первый кортеж не сотрясается, потому что мутация вложенного списка изменит поведение кортежа при сравнении равенств.

Мутация a0 во втором примере не влияет на хэш кортежа, потому что это не влияет на сопоставления равенства. a0 все еще только равен самому себе, а его хэш не изменяется.

tb и tc в вашем третьем примере имеют равные хэши, потому что они равны кортежам, независимо от того, являются ли их элементы одними и теми же объектами.

Все это означает, что кортежи не могут (напрямую) использовать id для хэшей. Если бы они это сделали, одинаковые кортежи с четкими, но равными элементами могли бы хешировать по-разному, нарушая контракт хэширования. Без специальных типов элементов, единственные кортежи, которые могут использоваться для вычисления собственных хэшей, являются хешами их элементов, поэтому кортежи основывают свои хэши на хэшах своих элементов.


хэш tuple основан на содержимом , а не на _id_s кортежей. И хеши вычисляются рекурсивно: если один элемент не хешируется (например, элемент list ), то сам кортеж не является хешируемым.

Совершенно нормально, если a и b являются кортежами и a == b , то hash(a) == hash(b) (если хэши можно вычислить, конечно), даже если a is not b .

(напротив, hash(a) == hash(b) не означает, что a == b )

Передаваемая информация часто не очень полезна, например, из-за интерполяции объектов python.





tuples