← Назад к вопросам
Как определить что объект хешируемый в Python?
2.0 Middle🔥 151 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Определение хешируемости объекта в Python
Хешируемость — критическое свойство объекта, которое определяет, можно ли его использовать как ключ в словаре или элемент множества (set). Это важный концепт для Python-разработчика.
Что такое hashable?
Объект считается хешируемым, если:
- Имеет метод
__hash__()(возвращает целое число) - Имеет метод
__eq__()(для проверки равенства) - Объект неизменяемый (immutable) — если равные объекты, то одинаковые хеши
- Хеш стабилен на протяжении жизни объекта
Способ 1: Проверка наличия hash
# Проверяем наличие метода __hash__
objects_to_test = [
42, # int
"hello", # str
(1, 2, 3), # tuple
[1, 2, 3], # list
{1, 2, 3}, # set
{"a": 1}, # dict
]
for obj in objects_to_test:
is_hashable = hasattr(obj, '__hash__') and obj.__hash__ is not None
print(f"{type(obj).__name__}: hashable={is_hashable}")
# int: hashable=True
# str: hashable=True
# tuple: hashable=True
# list: hashable=False
# set: hashable=False
# dict: hashable=False
Способ 2: Попытка использовать hash()
def is_hashable(obj):
"""Попытаться вычислить хеш объекта"""
try:
hash(obj)
return True
except TypeError:
return False
# Тестирование
test_objects = [
123,
"string",
(1, 2),
[1, 2],
{1: 2},
{1, 2},
lambda x: x,
]
for obj in test_objects:
result = is_hashable(obj)
print(f"{repr(obj):30} -> {result}")
# 123 -> True
# 'string' -> True
# (1, 2) -> True
# [1, 2] -> False
# {1: 2} -> False
# {1, 2} -> False
# <function <lambda>> -> True
Это самый надёжный способ — практический, а не теоретический.
Способ 3: Проверка типа
from collections.abc import Hashable
object_list = [1, "test", [1, 2], {1, 2}]
for obj in object_list:
# Проверка через ABC (Abstract Base Class)
if isinstance(obj, Hashable):
print(f"{type(obj).__name__} is hashable")
else:
print(f"{type(obj).__name__} is NOT hashable")
Но это может быть ненадёжно! Некоторые объекты могут заявлять о хешируемости, но hash() всё равно упадёт.
Способ 4: Проверка hash == None
def check_hashable(obj):
"""Явная проверка через __hash__"""
# Если __hash__ == None, объект точно не хешируемый
if hasattr(obj, '__hash__'):
return obj.__hash__ is not None
return False
# Классы с явно отключённой хешируемостью
class NonHashable:
__hash__ = None
class HasEquality:
def __eq__(self, other):
return True
# При определении __eq__, __hash__ автоматически становится None!
print(check_hashable(NonHashable())) # False
print(check_hashable(HasEquality())) # False
Практические примеры
Пример 1: Класс с хешем
class Point:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __repr__(self):
return f"Point({self.x}, {self.y})"
# Проверяем
point = Point(3, 4)
print(f"Hashable: {is_hashable(point)}") # True
# Можем использовать в set
points_set = {Point(1, 2), Point(3, 4), Point(1, 2)}
print(f"Set size: {len(points_set)}") # 2 (дубликат удален)
# Можем использовать как ключ в dict
points_dict = {Point(1, 2): "origin", Point(3, 4): "other"}
print(points_dict[Point(1, 2)]) # origin
Пример 2: Класс БЕЗ хеша (когда есть eq)
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
def __eq__(self, other):
if not isinstance(other, User):
return NotImplemented
return self.id == other.id
# __hash__ автоматически становится None!
# Проверяем
user = User(1, "John")
print(f"Hashable: {is_hashable(user)}") # False
# Не можем использовать в set
try:
users_set = {user}
except TypeError as e:
print(f"Ошибка: {e}") # unhashable type: 'User'
Пример 3: Класс с явным hash
class UserHashable:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
def __eq__(self, other):
if not isinstance(other, UserHashable):
return NotImplemented
return self.id == other.id
def __hash__(self):
# Хешируем только неизменяемые поля
return hash(self.id)
# Теперь хешируемый
user = UserHashable(1, "John")
print(f"Hashable: {is_hashable(user)}") # True
users_set = {UserHashable(1, "John"), UserHashable(2, "Jane")}
print(f"Set size: {len(users_set)}") # 2
Хешируемые встроенные типы
hashable_types = [
42, # int
3.14, # float
"hello", # str
b"bytes", # bytes
(1, 2, 3), # tuple (если содержит только hashable)
None, # NoneType
True, # bool
frozenset([1, 2]), # frozenset
lambda x: x, # function
type, # type
]
for obj in hashable_types:
print(f"{type(obj).__name__:15} hash={hash(obj)}")
Не хешируемые типы
unhashable_types = [
[1, 2, 3], # list (изменяемый)
{1, 2, 3}, # set (изменяемый)
{"a": 1}, # dict (изменяемый)
bytearray(b"test"), # bytearray (изменяемый)
]
for obj in unhashable_types:
try:
hash(obj)
except TypeError as e:
print(f"{type(obj).__name__:15} -> TypeError: {e}")
Tuple с изменяемыми элементами
# Кортеж сам по себе хешируемый
immutable_tuple = (1, 2, "hello")
print(f"Hashable: {is_hashable(immutable_tuple)}") # True
# Но если содержит изменяемые объекты...
mixed_tuple = (1, 2, [3, 4]) # содержит list
print(f"Hashable: {is_hashable(mixed_tuple)}") # False
# Потому что
try:
hash(mixed_tuple)
except TypeError as e:
print(f"Ошибка: {e}") # unhashable type: 'list'
Рекомендация: Всегда тестируйте
def safe_use_in_set(obj):
"""Безопасно ли использовать объект в set?"""
try:
test_set = {obj}
return True
except TypeError:
return False
print(safe_use_in_set(42)) # True
print(safe_use_in_set([1, 2])) # False
print(safe_use_in_set((1, 2))) # True
print(safe_use_in_set((1, [2]))) # False
Вывод: Самый надёжный способ определить хешируемость — попытаться вычислить hash(obj) и перехватить TypeError. Это практичнее, чем проверять наличие методов.