Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
id() vs hash() в Python: глубокое погружение
Это две абсолютно разные операции, решающие разные задачи. Часто разработчики путают их.
1. id() — уникальный идентификатор объекта в памяти
id() возвращает память адрес объекта в данный момент времени.
class Person:
def __init__(self, name):
self.name = name
alice1 = Person("Alice")
alice2 = Person("Alice")
alice3 = alice1
print(id(alice1)) # 140234567890000
print(id(alice2)) # 140234567890128 (другой объект!)
print(id(alice3)) # 140234567890000 (тот же объект как alice1)
print(alice1 is alice3) # True — один объект
print(alice1 is alice2) # False — разные объекты
Свойства id():
- Уникален для каждого объекта в памяти
- Меняется если объект перемещается (в CPython обычно не перемещается)
- Используется для проверки
is(идентичность, не равенство) - Возвращается целое число (адрес памяти)
2. hash() — криптографический отпечаток объекта
hash() возвращает числовой хэш на основе содержимого объекта.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
return hash((self.name, self.age))
def __eq__(self, other):
if not isinstance(other, Person):
return False
return self.name == other.name and self.age == other.age
alice1 = Person("Alice", 30)
alice2 = Person("Alice", 30)
alice3 = Person("Alice", 31)
print(id(alice1)) # 140234567890000
print(id(alice2)) # 140234567890128 (другой объект)
print(hash(alice1)) # 12345678
print(hash(alice2)) # 12345678 (одинаковый хэш!)
print(hash(alice3)) # 87654321 (другой хэш)
print(alice1 == alice2) # True — одинаковое содержимое
print(alice1 is alice2) # False — разные объекты
3. Основные различия
| Аспект | id() | hash() |
|---|---|---|
| Что возвращает | Адрес в памяти | Число на основе содержимого |
| Меняется ли | Редко (в CPython не меняется) | Не должна меняться для одного объекта |
| Для чего | Проверка идентичности (is) | Поиск в словарях и множествах |
| Требует ли eq | Нет | Да, должны быть вместе |
| Два объекта == означают | Разные в памяти | Возможно одинаковый хэш |
4. Практический пример: использование в dict и set
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
return hash((self.name, self.age))
def __eq__(self, other):
return self.name == other.name and self.age == other.age
def __repr__(self):
return f"Person({self.name}, {self.age})"
alice1 = Person("Alice", 30)
alice2 = Person("Alice", 30) # Копия alice1
bob = Person("Bob", 25)
# SET — использует hash() и ==
people_set = {alice1, alice2, bob}
print(len(people_set)) # 2, не 3! alice1 и alice2 считаются дубликатом
# DICT — использует hash() для ключей
people_ages = {alice1: 30, bob: 25}
print(people_ages[alice2]) # 30 — находит по хэшу и ==
print(people_ages[Person("Alice", 30)]) # 30 — новый объект, но находит!
5. Почему hash() и eq должны быть вместе
# НЕПРАВИЛЬНО — нарушает инвариант
class BadPerson:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
# Забыли реализовать __hash__!
p1 = BadPerson("Alice")
p2 = BadPerson("Alice")
print(p1 == p2) # True
print(hash(p1) == hash(p2)) # False! (используется id())
# Это ломает invariant: если a == b, то hash(a) == hash(b)
people = {p1: 1, p2: 2} # Разные ключи в словаре!
print(len(people)) # 2, а не 1
Правильно:
class GoodPerson:
def __init__(self, name):
self.name = name
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return self.name == other.name
p1 = GoodPerson("Alice")
p2 = GoodPerson("Alice")
print(p1 == p2) # True
print(hash(p1) == hash(p2)) # True
people = {p1: 1, p2: 2} # Второй перезапишет первый
print(len(people)) # 1 ✓
6. Примеры с встроенными типами
# int
num1 = 42
num2 = 42
print(id(num1) == id(num2)) # True (Python кэширует малые числа)
print(hash(num1) == hash(num2)) # True
print(num1 is num2) # True (в CPython)
# str
s1 = "hello"
s2 = "hello"
print(id(s1) == id(s2)) # True (интернирование строк)
print(hash(s1) == hash(s2)) # True
# list — UNHASHABLE
my_list = [1, 2, 3]
print(hash(my_list)) # TypeError: unhashable type: 'list'
print(id(my_list)) # 140234567890000 — работает
# dict — UNHASHABLE
my_dict = {"a": 1}
print(hash(my_dict)) # TypeError: unhashable type: 'dict'
print(id(my_dict)) # 140234567890000 — работает
# tuple — HASHABLE
my_tuple = (1, 2, 3)
print(hash(my_tuple)) # 529344067295497451 ✓
print(id(my_tuple)) # 140234567890000 — работает
7. Когда что использовать
Используй id() когда:
- Проверяешь идентичность:
obj1 is obj2 - Нужен уникальный идентификатор объекта в памяти
- Отлаживаешь и смотришь адреса
Используй hash() когда:
- Сохраняешь объект в set или как ключ dict
- Реализуешь кэш (например, @lru_cache)
- Проверяешь, что два объекта имеют одинаковое содержимое
Итоги
id()— это адрес объекта в памяти, уникален для каждого объектаhash()— это число на основе содержимого, одинаково для объектов с одинаковым содержимым- Правило золотое: если переопределяешь
__eq__, обязательно переопределяй__hash__ - В хэш-таблицах (dict, set) используется
hash()+__eq__, неid()