Python-атрибуты: разбираемся, как они работают 🐍
Атрибуты — это фундамент объектной модели Python, но большинство новичков (и даже многие мидлы) используют их интуитивно, не понимая, что происходит "под капотом". В итоге — непредсказуемое поведение кода, пожирание оперативы и спагетти-API.
Давайте разбираться в нюансах 👇 1️⃣ Порядок поиска (MRO) Python живет по принципу: сначала объект, потом класс.
Когда вы обращаетесь к obj.attr, интерпретатор лезет сначала в obj.dict. Если не нашел — идет в Class.dict. Если вы случайно запишете что-то в obj.attr, вы создадите атрибут экземпляра, который скроет атрибут класса.
class Hero: weapon = "Sword"
h = Hero() h.weapon = "Gun" # Теперь у h свое оружие, а класс по-прежнему с мечом Это не баг, это фича, но контролируйте это. Если хотите изменить атрибут для всех — стучитесь в класс.
2️⃣ dict vs slots По умолчанию каждый объектdictn имеет dict — хэш-таблицу (словарь), где хранятся атрибуты. Это гибко, но дорого по памяти.
Если вы создаете миллионы объектов одного тиslotsьзуйте slots. 🔵Что это дает: Мы запdictоздание dict для экземпляра, жестко фиксируя набор атрибутов в памяти (как массив). 🔵Профит: Минус 25-30% потребления памяти и чуть более быстрый доступ. 🔵Цена: Вы теряете динамическую возможность добавлять новые атрибуты на лету.
3️⃣ Иллюзия инкапсуляции В Python нет "private" полей. Совсем. Конструкции вида __attribute — это не защита данных, а name mangling (искажение имен). Python просто переименовывает переменную в _ClassName__attribute, чтобы случайно не перетереть её в наследниках. Если вы пишете __ с надеждой, что никто не долезет до ваших данных, то любой, кто умеет пользоваться dir(), увидит всё. Пишете _private (один подчерк) — значит, "не трогай, это внутренний API". Это вопрос культуры, а не компилятора.
Хотите скрыть данные? Используйте один нижний подчерк _ только как сигнал коллегам: "Не трогай это, это внутренняя кухня".
4️⃣@property****: этичный геттер Никогда не делайте в Python Java-style геттеры (get_value(), set_value()). Если вам нужна логика при доступе к атрибуту — используйте @property. @property позволяет превратить метод в атрибут. Вы сохраняете чистый API (доступ через точку), но под капотом можете валидировать данные, логировать доступ или вычислять значения на лету. class DataStorage: def init(self, value): self._value = value
@property def value(self): return self._value
@value.setter def value(self, new_val): if not isinstance(new_val, int): raise ValueError("Только int, детка") self._value = new_val
Так вы сохраняете чистый интерфейс: obj.value = 10 выглядит так же просто, как работа с обычным полем, но вы контролируете, что именно туда записывается.
Какие еще "магические" места в Python вызывают у вас вопросы? Накидайте в комменты, разберем. 👇