Skip Navigation
Telegram
Python-атрибуты: разбираемся, как они работают 🐍


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 вызывают у вас вопросы? Накидайте в комменты, разберем. 👇

#анатомия_питона
Telegram
Рекурсия и почему Python её (не) любят 🌀


Рекурсия и почему Python её (не) любят 🌀

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

Разбираем, что происходит под капотом и почему Python, в отличие от функциональных языков, рекурсию недолюбливает.

1️⃣ Цена вопроса: стек вызовов
Каждый вызов функции — это не бесплатно. Это создание нового стек-фрейма в памяти. В этом фрейме хранятся локальные переменные и адрес возврата.
Представьте матрешку, где каждая следующая кукла весит столько же, сколько предыдущая. В Python этот "вес" ложится на оперативку.
Цикл while или for использует один блок памяти. Рекурсия жрет память линейно (или экспоненциально) пропорционально глубине вызова.

2️⃣ Почему нет хвостовой оптимизации
В языках типа Haskell или C++ компилятор умный. Если рекурсивный вызов — последнее действие в функции (хвостовая рекурсия), он подменяет его на обычный прыжок (GOTO), не создавая новый фрейм. Это называется Tail Call Optimization (TCO).

В Python TCO нет и не будет. Гвидо ван Россум (BDFL) принципиально против TCO в Python. Аргумент простой: «Мы хотим видеть полные трейсбеки ошибок». Если схлопнуть стек, вы никогда не узнаете, на каком именно витке рекурсии всё упало.
Поэтому лимит в 1000 вызовов (дефолтный) — это не баг, а предохранитель от переполнения стека. Можно ли его увеличить через sys.setrecursionlimit? Можно. Но если вам это понадобилось, 99% вероятность, что вы ошиблись с архитектурой.

3️⃣ Когда рекурсия — зло, а когда — необходимость?
Линейные структуры (например, списки): Вычисление факториала, чисел Фибоначчи или обход плоского списка. Итеративный цикл while/for всегда будет быстрее и экономичнее по памяти (O(1) памяти против O(N)).
Разветвленные структуры (деревья, графы, вложенные dict): Парсинг JSON, обход файловой системы, DOM-дерева или алгоритмы типа Divide and Conquer (QuickSort, MergeSort). Здесь рекурсия снижает когнитивную нагрузку и делает код читаемым.

4️⃣ Лекарство от тормозов: @lru_cache
Классический пример глупости — вычисление Фибоначчи "в лоб". Сложность O(2^n). Это значит, что для вычисления 50-го числа программа умрет раньше, чем закончит.
Решение — Мемоизация.
Декоратор @functools.lru_cache кэширует результаты вызовов. Если функция уже считалась с этим аргументом, Python просто достанет готовое значение из хэш-таблицы. Это превращает экспоненциальный ужас в линейную сложность O(N).

Итого: Python — это не Haskell и не Lisp. Здесь рекурсия — это гражданин второго сорта. Используйте её для деревьев и графов, но для всего остального есть циклы.

#анатомия_питона
Telegram
MRO в Python 🧬


MRO в Python 🧬

Если вас спросят, как Python ищет методы в «ромбовидной» иерархии, и вы ответите «сначала в глубину» (DFS) или «сначала в ширину» (BFS) — вы ошиблись. Оба ответа технически неверны для Python 3.

Разбираем анатомию Method Resolution Order:
💎 Diamond Problem: Почему порядок наследования может быть критически важен.
💎 Как работает алгоритм C3 Linearization.
💎 Миф о super(): почему это не всегда «вызов родителя».

Листайте, чтобы понять, почему ваши миксины работают не так, как вы планировали 👉

#анатомия_питона
Telegram
#️⃣ Комментарии в Python


#️⃣ Комментарии в Python

«Код должен объяснять себя сам» — скажут одни. И будут непоняты.

«Я оставлю комменты у каждой строки кода, чтобы всё было понятно!» — скажут другие. И их код будет выглядеть как свалка никому не нужных очевидных фактов.

В каком лагере вы?
👨‍💻 — Лучше буду комментировать всё!
👨‍💻 — Код, который требует комментариев — плохой код!

#анатомия_питона