← Назад к вопросам

В чем разница между id объекта и хэшем?

1.7 Middle🔥 131 комментариев
#Python Core

Комментарии (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()