Функция hash() в Python, хэш-значение объекта
Функция hash() возвращает хеш-значение объекта, если оно есть. Хэш-значения являются целыми числами. Они используются для быстрого сравнения ключей словаря во время поиска в словаре.
Равные числовые значения имеют одинаковое значение хеш-функции, даже если они имеют разные типы, как в случае с 1 и 1.0 .
Пользовательские типы могут переопределять метод __hash__() , результат которого будет использован при вызове функции hash() . Однако, следует помнить, что функция hash() обрезает значение в соответствии с битностью хоста.
Примечание:
Большинство неизменяемых встроенных объектов Python являются хешируемыми и имеют хеш-значение. Изменяемые контейнеры, такие как списки или словари, не имеют хеш-значений
Примеры получения хэш-значений объектов.
>>> hash(1) 1 >>> hash(1.0) 1 >>> hash('1') -3723884734378080930 >>> hash('строка') -295037195106125010 >>> hash((1,2,3)) 2528502973977326415
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Функция abs(), абсолютное значение числа
- Функция all(), все элементы True
- Функция any(), хотя бы один элемент True
- Функция ascii(), преобразует строку в ASCII
- Функция bin(), число в двоичную строку
- Класс bool(), логическое значение объекта
- Функция breakpoint(), отладчик кода
- Класс bytearray(), преобразует в массив байтов
- Класс bytes(), преобразует в строку байтов
- Функция callable(), проверяет можно ли вызвать объект
- Функция chr(), число в символ Юникода
- Класс classmethod, делает функцию методом класса
- Функция compile() компилирует блок кода Python
- Класс complex(), преобразует в комплексное число
- Функция delattr(), удаляет атрибут объекта
- Класс dict() создает словарь
- Функция dir(), все атрибуты объекта
- Функция divmod(), делит числа с остатком
- Функция enumerate(), счетчик элементов последовательности
- Функция eval(), выполняет строку-выражение с кодом
- Функция exec(), выполняет блок кода
- Функция filter(), фильтрует список по условию
- Класс float(), преобразует в вещественное число
- Функция format(), форматирует значение переменной
- Класс frozenset(), преобразует в неизменяемое множество
- Функция getattr(), значение атрибута по имени
- Функция globals(), переменные глобальной области
- Функция hasattr(), наличие атрибута объекта
- Функция hash(), хэш-значение объекта
- Функция help(), справка по любому объекту
- Функция hex(), число в шестнадцатеричную строку
- Функция id(), идентификатор объекта
- Функция input(), ввод данных с клавиатуры
- Класс int(), преобразует в тип int
- Функция isinstance(), принадлежность экземпляра к классу
- Функция issubclass(), проверяет наследование класса
- Функция iter(), создает итератор
- Функция len(), количество элементов объекта
- Класс list(), преобразовывает в список
- Функция locals(), переменные локальной области
- Функция map(), обработка последовательности без цикла
- Функция max(), максимальное значение элемента
- Класс memoryview(), ссылка на буфер обмена
- Функция min(), минимальное значение элемента
- Функция next(), следующий элемент итератора
- Класс object(), возвращает безликий объект
- Функция oct(), число в восьмеричную строку
- Функция open(), открывает файл на чтение/запись
- Функция ord(), число символа Unicode
- Функция pow(), возводит число в степень
- Функция print(), печатает объект
- Класс property(), метод класса как свойство
- Класс range(), генерирует арифметические последовательности
- Функция repr(), описание объекта
- Функция reversed(), разворачивает последовательность
- Функция round(), округляет число
- Класс set(), создает или преобразовывает в множество
- Функция setattr(), создает атрибут объекта
- Класс slice(), шаблон среза
- Функция sorted(), выполняет сортировку
- Декоратор staticmethod(), метод класса в статический метод
- Класс str(), преобразует объект в строку
- Функция sum(), сумма последовательности
- Функция super(), доступ к унаследованным методам
- Класс tuple(), создает или преобразует в кортеж
- Класс type(), возвращает тип объекта
- Функция vars(), словарь переменных объекта
- Функция zip(), объединить элементы в список кортежей
- Функция __import__(), находит и импортирует модуль
- Функция aiter(), создает асинхронный итератор
- Функция anext(), следующий элемент асинхронного итератора
object.__hash__
Вызывается встроенной функцией hash(), а также для операций над элементами коллекций, использующих хеши, включая множества (set), статичные множества (frozenset), словари (dict).
Единственным требованием является то, чтобы объекты являющиеся равными имели одинаковый хеш. Рекомендуется неким образом смешать (например, при помощи исключающего «или») значения хешей элементов составляющих объект, которые также играют роль при сравнении объектов.
class Mine: pass
Mine().__hash__() # например -9223363288920356208
Если класс не определил __eq__(), то не должен определять и данный метод, иначе экземпляры нельзя будет использовать в качестве элементов коллекций, использующих хеши.
Если класс представляет изменяемые объекты и в нём определён __eq__(), то не должен реализовывать и данный метод, ввиду того, что реализация коллекций, поддерживающих хеширование требует, чтобы хеш ключа был неизменяемым (если изменится хеш, то он будет отнесен не к той ячейке хеша).
Пользовательские классы уже реализуют метод __eq__() и данный. Реализация гарантирует, что любые объекты не равные между собой (конечно, исключая самих себя) и x.__hash__() возвращает соответствующее значение, такое, что x == y подразумевает одновременно x is y и hash(x) == hash(y) .
+py2.6: Атрибуту __hash__ можно присвоить значение None, чтобы явно указать на то, что объекты не поддерживают хеширование.
Для классов, переопределивших __eq__(), но не данный метод, сам атрибут __hash__ будет неявно сброшен в None . Когда данный метод None, объекты будут поднимать TypeError, при попытке получения хеша для них, а также будут корректно определяться как не поддерживающие хеширование при проверке isinstance(obj, collections.Hashable) .
Если требуется, чтобы класс переопределивший __eq__() сохранил реализацию данного метода, предлагаемую родительским классом, необходимо явно указать на это интерпретатору при помощи __hash__ = .__hash__ .
Если требуется для класса, не переопределившего __eq__(), отключить возможность хеширования, нужно указать __hash__ = None . Если класс переопределил данный метод и при этом явно поднимает TypeError, то он будет неверно опознан как поддерживающий хеширование при проверке isinstance(obj, collections.Hashable) .
На заметку
+py3.3: Рандомизация хешей включена по умолчанию. +py3.2.3: Смотрите также описание ключа запуска интерпретатора -R и переменной среды PYTHONHASHSEED .
По умолчанию значения, получаемые данным методом для строк, байтов и датверемени «подсолены» неким случайным значением. Не смотря на их неизменность внутри каждого отдельного процесса Python, уникальность не гарантируется между стартами Python.
Это сделано с целью защиты от атак отказа в обслуживании, вызываемых специально подготовленными данными, эксплуатирующими затратные вставки в словари со сложностью O(n^2). Более полная информация (англ.).
Смена хешей влияет на процесс итерирования в словарях, множествах и прочих отображениях. Питон никогда не гарантировал тот или иной порядок прохода (он обычно разнится для 32- и 64-битных сборок).
+py2.5: метод может возвращать объект long integer, далее 32-битное целое получается их хеша данного объекта. hash() усекает значение, возвращаемое данным методом до размера Py_ssize_t. Обычно для 64-битных это 8 байт, и 4 байта для 32-битных сборок. Если метод должен функционировать на сборках с разной битностью, следует проверить размерность для этих сборок. Сделать это можно следующим образом python -c «import sys; print(sys.hash_info.width)» .
Python и теория множеств
В Python есть очень полезный тип данных для работы с множествами – это set. Об этом типе данных, примерах использования, и небольшой выдержке из теории множеств пойдёт речь далее.

Следует сразу сделать оговорку, что эта статья ни в коем случае не претендует на какую-либо математическую строгость и полноту, скорее это попытка доступно продемонстрировать примеры использования множеств в языке программирования Python.
- Множество
- Множества в Python
- Хешируемые объекты
- Принадлежность множеству
- Мощность множества
- Перебор элементов множества
- Равные множества
- Непересекающиеся множества
- Подмножество и надмножество
- Объединение множеств
- Добавление элементов в множество
- Пересечение множеств
- Разность множеств
- Удаление элементов из множества
- Симметрическая разность множеств
Множество
Множество – это математический объект, являющийся набором, совокупностью, собранием каких-либо объектов, которые называются элементами этого множества. Или другими словами:
Множество – это не более чем неупорядоченная коллекция уникальных элементов.

Что значит неупорядоченная? Это значит, что два множества эквивалентны, если содержат одинаковые элементы.

Элементы множества должны быть уникальными, множество не может содержать одинаковых элементов. Добавление элементов, которые уже есть в множестве, не изменяет это множество.
Множества, состоящие из конечного числа элементов, называются конечными, а остальные множества – бесконечными. Конечное множество, как следует из названия, можно задать перечислением его элементов. Так как темой этой статьи является практическое использование множеств в Python, то я предлагаю сосредоточиться на конечных множествах.
Множества в Python
Множество в Python можно создать несколькими способами. Самый простой – это задать множество перечислением его элементов в фигурных скобках:
fruits =
Единственное ограничение, что таким образом нельзя создать пустое множество. Вместо этого будет создан пустой словарь:
wrong_empty_set = <> print(type(wrong_empty_set)) # Вывод
Для создания пустого множества нужно непосредственно использовать set() :
correct_empty_set = set() print(type(correct_empty_set)) # Вывод
Также в set() можно передать какой-либо объект, по которому можно проитерироваться (Iterable):
color_list = ["red", "green", "green", "blue", "purple", "purple"] color_set = set(color_list) print(color_set) # Вывод (порядок может быть другим):
Ещё одна возможность создания множества – это использование set comprehension. Это специальная синтаксическая конструкция языка, которую иногда называют абстракцией множества по аналогии с list comprehension (Списковое включение).
numbers = [1, 2, 2, 2, 3, 3, 4, 4, 5, 6] # Единственное отличие со списковыми включениями - это # использование фигурных скобок вместо квадратных even_numbers = < number for number in numbers if number % 2 == 0 >print(even_numbers) # Вывод (порядок может быть другим):
Хешируемые объекты
Существует ограничение, что элементами множества (как и ключами словарей) в Python могут быть только так называемые хешируемые (Hashable) объекты. Это обусловлено тем фактом, что внутренняя реализация set основана на хеш-таблицах. Например, списки и словари – это изменяемые объекты, которые не могут быть элементами множеств. Большинство неизменяемых типов в Python (int, float, str, bool, и т.д.) – хешируемые. Неизменяемые коллекции, например tuple, являются хешируемыми, если хешируемы все их элементы.
# Множество кортежей (tuple) records = < ("Москва", 17_200_000), ("Санкт-Петербург", 5_400_000), ("Новосибирск", 1_600_000), ("Москва", 17_200_000), >for city, population in records: print(city) # Вывод (порядок может быть другим): Москва Новосибирск Санкт-ПетербургОбъекты пользовательских классов являются хешируемыми по умолчанию. Но практического смысла чаще всего в этом мало из-за того, что сравнение таких объектов выполняется по их адресу в памяти, т.е. невозможно создать два «равных» объекта.
class City: def __init__(self, name: str): self.name = name def __repr__(self) -> str: """ Определим метод __repr__ для наглядности следующих примеров """ return f'City("")' print(City("Moscow") == City("Moscow")) # Вывод: False cities = print(cities) # ВыводСкорее всего мы предполагаем, что объекты City(«Moscow») должны быть равными, и следовательно в множестве cities должен находиться один объект.
Этого можно добиться, если определить семантику равенства для объектов класса City :class City: def __init__(self, name: str): # Атрибут name не должен изменяться, пока объект существует # Для простоты пометим этот атрибут как внутренний self._name = name def __hash__(self) -> int: """ Хеш от объекта """ return hash((self._name, self.__class__)) def __eq__(self, other) -> bool: """ Определяем семантику равентсва (оператор ==) """ if not isinstance(other, self.__class__): return False return self._name == other._name def __repr__(self) -> str: """ Определим метод __repr__ для наглядности следующих примеров """ return f'City("")'Чтобы протокол хеширования работал без явных и неявных логических ошибок, должны выполняться следующие условия:
- Хеш объекта не должен изменяться, пока этот объект существует
- Равные объекты должны возвращать одинаковый хеш
moscow = City("Moscow") moscow_again = City("Moscow") print(moscow == moscow_again and hash(moscow) == hash(moscow_again)) # Вывод: True # Теперь множество городов работает более логично и интуитивно cities = print(cities) # Вывод (порядок может быть другим):Свойства множеств
Тип set в Python является подтипом Collection (про коллекции), из данного факта есть три важных следствия:
- Определена операция проверки принадлежности элемента множеству
- Можно получить количество элементов в множестве
- Множества являются iterable-объектами
Принадлежность множеству
Проверить принадлежит ли какой-либо объект множеству можно с помощью оператора in . Это один из самых распространённых вариантов использования множеств. Такая операция выполняется в среднем за O(1) с теми же оговорками, которые существуют для хеш-таблиц.
tremendously_huge_set = if "green" in tremendously_huge_set: print("Green is there!") else: print("Unfortunately, there is no green. ") # Вывод: Green is there! if "purple" in tremendously_huge_set: print("Purple is there!") else: print("Unfortunately, there is no purple. ") # Вывод: Unfortunately, there is no purple.Мощность множества
Мощность множества – это характеристика множества, которая для конечных множеств просто означает количество элементов в данном множестве. Для бесконечных множеств всё несколько сложнее.
even_numbers = # Мощность множества cardinality = len(even_numbers) print(cardinality) # Вывод: 50Перебор элементов множества
Как уже было отмечено выше, множества поддерживают протокол итераторов, таким образом любое множество можно использовать там, где ожидается iterable-объект.
colors = # Элементы множества можно перебрать с помощью цикла for for color in colors: print(color) # Вывод (порядок может быть другим): red green blue # Множества можно использовать там, где ожидается iterable-объект color_counter = dict.fromkeys(colors, 1) print(color_counter) # Вывод (порядок может быть другим):
Отношения между множествами
Между множествами существуют несколько видов отношений, или другими словами взаимосвязей. Давайте рассмотрим возможные отношения между множествами в этом разделе.
Равные множества

Тут всё довольно просто – два множества называются равными, если они состоят из одних и тех же элементов. Как следует из определения множества, порядок этих элементов не важен.
my_fruits = your_fruits = print(my_fruits == your_fruits) # Вывод: TrueНепересекающиеся множества

Если два множества не имеют общих элементов, то говорят, что эти множества не пересекаются. Или другими словами, пересечение этих множеств является пустым множеством.
even_numbers = odd_numbers = # Очевидно, что множества чётных и нечётных чисел не пересекаются if even_numbers.isdisjoint(odd_numbers): print("Множества не пересекаются!") # Вывод: Множества не пересекаются!Подмножество и надмножество

Подмножество множества S – это такое множество, каждый элемент которого является также и элементом множества S. Множество S в свою очередь является надмножеством исходного множества.
# Множество чисел Фибоначчи меньших 100 fibonacci_numbers = # Множество натуральных чисел меньших 100 natural_numbers = set(range(100)) # Множество чисел Фибоначчи является подмножеством множества # натуральных чисел if fibonacci_numbers.issubset(natural_numbers): print("Подмножество!") # Вывод: Подмножество! # В свою очередь множество натуральных чисел является # надмножеством множества чисел Фибоначчи if natural_numbers.issuperset(fibonacci_numbers): print("Надмножество!") # Вывод: Надмножество!Пустое множество является подмножеством абсолютно любого множества.
empty = set() # Методы issubset и issuperset могут принимать любой iterable-объект print( empty.issubset(range(100)) and empty.issubset(["red", "green", "blue"]) and empty.issubset(set()) ) # Вывод: TrueСамо множество является подмножеством самого себя.
natural_numbers = set(range(100)) if natural_numbers.issubset(natural_numbers): print("Подмножество!") # Вывод: Подмножество!Операции над множествами
Рассмотрим основные операции, опредяляемые над множествами.
Объединение множеств

Объединение множеств – это множество, которое содержит все элементы исходных множеств. В Python есть несколько способов объединить множества, давайте рассмотрим их на примерах.
my_fruits = your_fruits = # Для объединения множеств можно использовать оператор `|`, # оба операнда должны быть объектами типа set our_fruits = my_fruits | your_fruits print(our_fruits) # Вывод (порядок может быть другим): # Также можно использовать ментод union. # Отличие состоит в том, что метод union принимает не только # объект типа set, а любой iterable-объект you_fruit_list: list = list(your_fruits) our_fruits: set = my_fruits.union(you_fruit_list) print(our_fruits) # Вывод (порядок может быть другим):
Добавление элементов в множество
Добавление элементов в множество можно рассматривать как частный случай объединения множеств за тем исключением, что добавление элементов изменяет исходное множество, а не создает новый объект. Добавление одного элемента в множество работает за O(1) .
colors = # Метод add добаляет новый элемент в множество colors.add("purple") # Добавление элемента, который уже есть в множестве, не изменяет # это множество colors.add("red") print(colors) # Вывод (порядок может быть другим): # Метод update принимает iterable-объект (список, словарь, генератор и т.п.) # и добавляет все элементы в множество numbers = numbers.update(i**2 for i in [1, 2, 3]) print(numbers) # Вывод (порядок может быть другим):Пересечение множеств

Пересечение множеств – это множество, в котором находятся только те элементы, которые принадлежат исходным множествам одновременно.
def is_prime(number: int) -> bool: """ Возвращает True, если number - это простое число """ assert number > 1 return all(number % i for i in range(2, int(number**0.5) + 1)) def is_fibonacci(number: int) -> bool: """ Возвращает True, если number - это число Фибоначчи """ assert number > 1 a, b = 0, 1 while a + b
При использовании оператора & необходимо, чтобы оба операнда были объектами типа set . Метод intersection , в свою очередь, принимает любой iterable-объект. Если необходимо изменить исходное множество, а не возращать новое, то можно использовать метод intersection_update , который работает подобно методу intersection , но изменяет исходный объект-множество.
Разность множеств

Разность двух множеств – это множество, в которое входят все элементы первого множества, не входящие во второе множество.
i_know: set = you_know: dict = < "Go": 0.4, "C++": 0.6, "Rust": 0.2, "Java": 0.9 ># Обратите внимание, что оператор `-` работает только # для объектов типа set you_know_but_i_dont = set(you_know) - i_know print(you_know_but_i_dont) # Вывод (порядок может быть другим): # Метод difference может работать с любым iterable-объектом, # каким является dict, например i_know_but_you_dont = i_know.difference(you_know) print(i_know_but_you_dont) # Вывод:
Удаление элементов из множества
Удаление элемента из множества можно рассматривать как частный случай разности, где удаляемый элемент – это одноэлементное множество. Следует отметить, что удаление элемента, как и в аналогичном случае с добавлением элементов, изменяет исходное множество. Удаление одного элемента из множества имеет вычислительную сложность O(1) .
fruits = # Удаление элемента из множества. Если удаляемого элемента # нет в множестве, то ничего не происходит fruits.discard("orange") fruits.discard("pineapple") print(fruits) # Вывод (порядок может быть другим): # Метод remove работает аналогично discard, но генерирует исключение, # если удаляемого элемента нет в множестве fruits.remove("pineapple") # KeyError: "pineapple"Также у множеств есть метод differenсe_update , который принимает iterable-объект и удаляет из исходного множества все элементы iterable-объекта. Этот метод работает аналогично методу difference , но изменяет исходное множество, а не возвращает новое.
numbers = even_numbers_under_100 = (i for i in range(1, 101) if i % 2 == 0) numbers.difference_update(even_numbers_under_100) print(numbers) # Вывод (порядок может быть другим):
Симметрическая разность множеств

Симметрическая разность множеств – это множество, включающее все элементы исходных множеств, не принадлежащие одновременно обоим исходным множествам. Также симметрическую разность можно рассматривать как разность между объединением и пересечением исходных множеств.
non_positive = non_negative = # Обратите внимание, что оператор `^` может применяться # только для объектов типа set non_zero = non_positive ^ non_negative print(non_zero) # Вывод (порядок может быть другим):
Как видно из примера выше, число 0 принадлежит обоим исходным множествам, и поэтому оно не входит в результирующее множество. Для операции симметрической разности, помимо оператора ^ , также существует два специальных метода – symmetric_difference и symmetric_difference_update . Оба этих метода принимают iterable-объект в качестве аргумента, отличие же состоит в том, что symmetric_difference возвращает новый объект-множество, в то время как symmetric_difference_update изменяет исходное множество.
non_positive = non_negative = range(4) non_zero = non_positive.symmetric_difference(non_negative) print(non_zero) # Вывод (порядок может быть другим): # Метод symmetric_difference_update изменяет исходное множество colors = colors.symmetric_difference_update(["green", "blue", "yellow"]) print(colors) # Вывод (порядок может быть другим):
Заключение
Я надеюсь, мне удалось показать, что Python имеет очень удобные встроенные средства для работы с множествами. На практике это часто позволяет сократить количество кода, сделать его выразительнее и легче для восприятия, а следовательно и более поддерживаемым. Я буду рад, если у вас есть какие-либо конструктивные замечания и дополнения.
Так что же означает «хэшируемый»
Я хочу что бы вершины моего графа в networkx были элементы моего класса. Согласно документации это должен быть какой то хэшируемый объект. Так. Хэшируемый? Что это значит?. Согласно офф документации он должен иметь магические методы hash и eq. Вот мой класс:
class Task: def __init__(self, user_id, level, created_by_id): self.user_id = user_id self.level = level self.created_by_id = created_by_id def __eq__(self, other): if self.user_id == other.user_id: return True else: return False def __hash__(self): return hash(self.user_id)У него есть оба этих метода, но когда я пытаюсь:
t1 = Task(. ) graph = nx.Graph() graph.add_node(t1)Выводит ошибку
TypeError: unhashable type: 'Task'Отслеживать
задан 20 апр 2021 в 14:29
BlueScreen BlueScreen
1,295 6 6 серебряных знаков 12 12 бронзовых знаков
Если сделать методы приватными, то все вроде работает.
20 апр 2021 в 14:31Вы уверены, что user_id однозначно определяет ваши объекты, это уникальный идентификатор? Если нет, то при хэшировании и сравнении нужно учитывать все поля, которые делают объект уникальным, иначе могут быть проблемы с идентификацией объектов.
20 апр 2021 в 14:56
А так то в Google Colab не ругается на ваш код
20 апр 2021 в 14:58@CrazyElf обычно user_id (как и любой другой uid) подразумевает же уникальность. тут, скорее всего, id пользователя в бд, должен быть уникальным