Pull to refresh

Comments 8

Формально нужна хэшируемость и сравнимость объектов, но по факту проще запомнить не изменяемость. Проблема с изменяемыми объектами в том, что у них при изменении меняется либо хэш, либо значение и вы уже не сможете нормально найти ключ в словаре, который ранее был задан таким объектом. Они могут и не измениться, но гарантии нет. Поэтому, от греха подальше, изменяемым объектам стараются просто не делать доступной функцию хэширования, чтобы не было возможности их использовать в качестве ключа для словаря и собирать грабли после этого.

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

все верно, для упрощения говорим, что ключи - неизменяемые типы данных, вряд ли ключами словаря будут объекты функций и экземпляры классов, но нужно упор делать на хешируемость

Вы относите слово "неизменяемый" к содержимому структуры данных, но применительно к ключам ассоциативного массива (в Python - "словаря") смотреть надо не на неизменяемость содержимого, а на неизменяемость того, что хэшируется и/или сравнивается операциями == и !=.

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

"зачастую на интернет-ресурсах про ключи говорят как про объекты неизменяемых типов данных,"

Не знаю где так говорят, если читать книги по Rust и С++ там сказано если ты можешь захешировать свой тип - вперёд

dct = {obj_1: 1, obj_2: 2}

Хм, интересная конструкция: мы присваивает каждому объекту значение, например, рейтинг, при этом не внутри объекта, т.е. ни объект, ни класс об этом "не знают". Где бы это могло пригодиться? [Дай 10 вариантов] - строка, отделяющая теперешнее время от времени ещё несколько лет назад...

Ответ deepseek, который ещё несколько лет назад был бы немыслимо ждать от машины:

Отличный вопрос! Хранение внешних атрибутов объектов в словаре — мощный паттерн, особенно когда нельзя или нежелательно модифицировать сами классы объектов. Вот 10 практических применений:

  1. Кэширование результатов вычислений:

    • cache = {obj: expensive_calculation(obj) for obj in objects}

    • Пригодится: Для тяжёлых функций (рендеринг, сложные математические операции). Избегаем повторных вычислений для одного объекта.

  2. Временные состояния/флаги:

    • selected_objects = {obj_1: True, obj_2: False}

    • Пригодится: UI (выделение элементов), обработка событий ("уже обработан"), временные отметки в алгоритмах (обход графа - visited[node] = True).

  3. Связывание метаданных:

    • object_metadata = {obj: {"last_accessed": timestamp, "owner": user_id}}

    • Пригодится: Системы кеширования (время доступа), аудит (кто последний менял), контекстная информация для обработки.

  4. Подсчёт частоты / Агрегация:

    • word_count = {}; for word in words: word_count[word] = word_count.get(word, 0) + 1

    • Пригодится: Анализ текста (частотность слов), подсчёт событий (сколько раз пользователь выполнил действие), поиск дубликатов.

  5. Сопоставление объектов с внешними ресурсами:

    • texture_mapping = {game_object: texture_id}

    • Пригодится: Графика (связь модели с текстурой), базы данных (связь объекта ORM с ID записи), сетевые соединения (связь пользователя с сокетом).

  6. Приоритизация / Очереди:

    • priority_queue = []; heapq.heappush(priority_queue, (priority_dict[task], task))

    • Пригодится: Планировщики задач (приоритет задачи не свойство самой задачи, а определяется контекстом), алгоритмы поиска пути (A* - f_score[node] = ...).

  7. Вес рёбер / Сила связей (Графы):

    • graph[from_node] = {to_node: weight, ...}

    • Пригодится: Представление взвешенных графов (социальные сети - сила связи, карты - расстояние/время проезда). Вес - атрибут связи, а не узла.

  8. Внешняя конфигурация / Настройки:

    • object_settings = {widget: {"color": "red", "size": 10}}

    • Пригодится: GUI (стилизация элементов, которые не хранят стиль внутри), управление поведением плагинов. Настройки задаются извне.

  9. Группировка / Классификация:

    • groups = {"admin": [user1, user2], "user": [user3]}

    • Пригодится: Системы прав доступа (группы пользователей), категоризация элементов (товары в категориях). Группа - внешний контейнер.

  10. Хранение сессионных данных:

    • session_store = {session_id: {"user": user_obj, "cart": cart_items}}

    • Пригодится: Веб-приложения. Данные сессии (корзина покупок, состояние) привязаны к идентификатору сессии, а не к объекту пользователя напрямую (особенно если пользователь не залогинен).

Ключевые преимущества такого подхода:

  • Неинвазивность: Не нужно изменять исходные классы объектов.

  • Гибкость: Легко добавлять, изменять и удалять атрибуты для объектов динамически.

  • Разделение ответственности: Данные, специфичные для контекста использования, хранятся отдельно от базовых данных объекта.

  • Эффективность: Быстрый (O(1) в среднем) доступ к данным по ключу-объекту.

  • Работа с "чужими" объектами: Можно прикреплять данные к объектам из библиотек, код которых вы не контролируете.

Важные замечания:

  • Хешируемость: Ключи словаря (ваши объекты) должны быть хешируемыми (в Python обычно реализуют hash и eq).

  • Сравнение по ссылке: По умолчанию в Python объекты сравниваются по идентификатору (адресу в памяти). Это часто ожидаемо в таких сценариях.

  • Жизненный цикл: Словарь хранит сильные ссылки на объекты-ключи. Это может препятствовать сборке мусора, если объект больше нигде не используется, но остался в словаре. Для временных данных используйте weakref.WeakKeyDictionary.

Этот паттерн — отличный инструмент для случаев, когда данные контекстно-зависимы или временны, и их не стоит "зашивать" в саму сущность объекта.

Хз откуда здесь "чудеса" и "открытия". Это буквально база работы со словарями о которой написано в любой нормальной книге по питону. Или любом нормальном курсе. Да и статей где бы все детально разжёвывалось (real python тот же) предостаточно...

но на эти нюансы не все обращают внимание, не лишним будет еще раз повторить

Sign up to leave a comment.

Articles