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

Какие методы нужно переопределить, чтобы объект себя вел как словарь?

2.2 Middle🔥 141 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Методы для создания словаря-подобного объекта

В Python существует протокол для объектов, которые ведут себя как словари. Это называется Mapping Protocol. Расскажу о всех необходимых методах и примерах использования.

1. Основные методы: getitem, setitem, delitem

class SimpleDict:
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        """obj[key] - получение значения"""
        return self._data[key]
    
    def __setitem__(self, key, value):
        """obj[key] = value - установка значения"""
        self._data[key] = value
    
    def __delitem__(self, key):
        """del obj[key] - удаление значения"""
        del self._data[key]

obj = SimpleDict()
obj['name'] = 'Alice'  # __setitem__
print(obj['name'])     # __getitem__: Alice
del obj['name']        # __delitem__

2. len и contains

class DictLike:
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        """len(obj) - количество элементов"""
        return len(self._data)
    
    def __contains__(self, key):
        """key in obj - проверка наличия ключа"""
        return key in self._data

obj = DictLike()
obj['a'] = 1
obj['b'] = 2

print(len(obj))        # 2
print('a' in obj)      # True
print('c' in obj)      # False

3. iter для итерирования по ключам

class IterableDict:
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        """Итерирование по ключам"""
        return iter(self._data)

obj = IterableDict()
obj['x'] = 10
obj['y'] = 20

for key in obj:  # Использует __iter__
    print(f"{key}: {obj[key]}")

# x: 10
# y: 20

4. .keys(), .values(), .items()

from collections.abc import MutableMapping

class FullDict(MutableMapping):
    """Наследуем от MutableMapping для автоматических методов"""
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)

obj = FullDict()
obj['a'] = 1
obj['b'] = 2

print(list(obj.keys()))    # ['a', 'b']
print(list(obj.values()))  # [1, 2]
print(list(obj.items()))   # [('a', 1), ('b', 2)]

5. .get(), .pop(), .update() через MutableMapping

from collections.abc import MutableMapping

class SmartDict(MutableMapping):
    def __init__(self, **kwargs):
        self._data = kwargs
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)

obj = SmartDict(name='Alice', age=30)

print(obj.get('name'))           # Alice
print(obj.get('email', 'N/A'))  # N/A (дефолт)

obj.update({'email': 'alice@example.com'})  # Автоматический метод
print(obj._data)  # {'name': 'Alice', 'age': 30, 'email': '...'}

value = obj.pop('age')  # Удаляет и возвращает
print(value)  # 30

6. Пример: OrderDict-подобный класс

from collections.abc import MutableMapping
from collections import OrderedDict

class OrderedDictLike(MutableMapping):
    """Словарь, сохраняющий порядок вставки"""
    def __init__(self):
        self._data = OrderedDict()
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)
    
    def __repr__(self):
        return f"OrderedDictLike({dict(self._data)})"

obj = OrderedDictLike()
obj['z'] = 1
obj['a'] = 2
obj['m'] = 3

print(list(obj.keys()))  # ['z', 'a', 'm'] - порядок сохранён

7. Расширенная версия с валидацией

from collections.abc import MutableMapping

class ValidatedDict(MutableMapping):
    """Словарь с валидацией типов"""
    def __init__(self, **schema):
        self._data = {}
        self.schema = schema  # {'name': str, 'age': int}
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        if key in self.schema:
            expected_type = self.schema[key]
            if not isinstance(value, expected_type):
                raise TypeError(f"{key} must be {expected_type}")
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)

config = ValidatedDict(name=str, port=int, debug=bool)
config['name'] = 'myapp'  # OK
config['port'] = 8080     # OK
# config['port'] = 'abc'  # TypeError: port must be <class 'int'>

8. Case-insensitive Dictionary

from collections.abc import MutableMapping

class CaseInsensitiveDict(MutableMapping):
    """Словарь, нечувствительный к регистру ключей"""
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        return self._data[key.lower()]
    
    def __setitem__(self, key, value):
        self._data[key.lower()] = value
    
    def __delitem__(self, key):
        del self._data[key.lower()]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)

obj = CaseInsensitiveDict()
obj['Name'] = 'Alice'
print(obj['name'])    # Alice
print(obj['NAME'])    # Alice

9. Nested Dictionary (вложенный доступ)

from collections.abc import MutableMapping

class NestedDict(MutableMapping):
    """Словарь с вложенным доступом через точку"""
    def __init__(self, data=None):
        self._data = data or {}
    
    def __getitem__(self, key):
        value = self._data[key]
        if isinstance(value, dict):
            return NestedDict(value)
        return value
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)
    
    def __getattr__(self, key):
        if key.startswith('_'):
            return super().__getattribute__(key)
        try:
            return self[key]
        except KeyError:
            raise AttributeError(f"No attribute {key}")

data = {'user': {'name': 'Alice', 'age': 30}}
obj = NestedDict(data)
print(obj['user']['name'])  # Alice
print(obj.user.name)        # Alice

10. Сравнение подходов

ПодходСложностьПреимуществаКогда использовать
Базовые методыНизкаяПолный контрольПростые случаи
MutableMappingСредняяГотовые методыБольшинство случаев
РасширенныеВысокаяКастомное поведениеСпециальные требования

Минимальный набор методов

class MinimalDict:
    """Минимум для словаря-подобного поведения"""
    def __init__(self):
        self._data = {}
    
    def __getitem__(self, key):
        return self._data[key]
    
    def __setitem__(self, key, value):
        self._data[key] = value
    
    def __delitem__(self, key):
        del self._data[key]
    
    def __len__(self):
        return len(self._data)
    
    def __iter__(self):
        return iter(self._data)
    
    def __contains__(self, key):
        return key in self._data

Лучшие практики

  1. Используй collections.abc.MutableMapping если нужна полная функциональность
  2. Переопредели только необходимые методы — остальные унаследуются
  3. Добавляй валидацию в setitem для защиты данных
  4. Реализуй repr для красивого вывода
  5. Документируй поведение отличное от обычного dict

Профессиональные примеры использования: ORM объекты, конфиги, кеши, API клиенты.