Класове и обекти на Python: Урокът за 2026 г
Повечето уроци за ООП на Python преподават синтаксиса и пропускат умствения модел. Този прави точно обратното — ще си тръгнете с ясна картина какво е класът по време на изпълнение, кога да посегнете към @dataclass вместо да пишете __init__ на ръка и как self наистина работи (не е магия).
Ще разгледаме: четириредовия минимален жизнеспособен клас, защо self е параметър, class vs атрибути на екземпляр (бъг №1 за начинаещи), шестте метода dunder, които ще видите навсякъде, @dataclass като съвременен стандарт по подразбиране и строго правило кога да оставите класовете сами и да използвате обикновена функция.
Мисловният модел: класът е фабрика
Клас в Python е фабрика, която произвежда обекти. Всеки път, когато извикате Rect(3, 4), Python създава нов обект със собствено хранилище и ви го връща обратно.
Това е цялата механика. Класът е планът; всяко повикване Rect(...) е печат на чертежа в нова RAM памет.
self е само параметър
Объркването №1 за хората, идващи от JS или Ruby: self в Python е изрично. Първият параметър на всеки метод е препратка към извиквания екземпляр.
Така че „c.bump()“ е синтактична захар за „Counter.bump(c)“. Python предава екземпляра като първи аргумент. Бихте могли да го наречете „аз“, „това“ или „фу“ – „аз“ е конвенция, а не ключова дума. Придържайте се към self (PEP 8), така че другите разработчици на Python да могат да четат вашия код.
Прихващането на атрибута клас срещу екземпляр
Най-често срещаната OOP грешка на Python в кодова база за начинаещи:
items = [] в обхвата на класа създава ЕДИН списък, споделен от всеки екземпляр. a.add("milk") го променя, така че b.items вижда същия списък.
Правило: инициализиране на променливото състояние в `__init__`, а не в обхвата на класа.
Този бъг е изпращан до производство повече пъти, отколкото всеки разработчик на Python би признал. Следете за списъци, диктовки, набори в обхвата на класа.
Шестте метода, които ще видите навсякъде
Методите на Dunder (__name__) са кукички за претоварване на оператора на Python. Тези шест покриват 95% от това, което ще срещнете:
1. __init__(self, ...) — конструктор
Изпълнява се, когато обектът е създаден. Задайте атрибути на екземпляр тук. Не връщайте нищо (Python налага Няма).
2. __repr__(self) — низ към разработчиците
Извиква се от print(x), ако __str__ липсва, и винаги от REPL. Стремете се към нещо, което можете да поставите обратно в Python, за да пресъздадете обекта:
3. __eq__(self, other) — равенство
Без него „Rect(3, 4) == Rect(3, 4)“ връща „False“ (по подразбиране е идентичност). Сравнете поле по поле:
4. __hash__(self) — възможност за хеширане
Ако замените __eq__, ТРЯБВА също така да замените __hash__ или Python задава __hash__ = None (обектът става нехеширан). Най-простият: върни хеш((self.width, self.height)).
5. __len__(self) — за len(obj)
Прави работа на len(cart) == 5.
6. __iter__(self) — за за x в obj:
Връща итератор. yield вътре прави класа итерируем:
@dataclass — модерното по подразбиране
Ако вашият клас е предимно държател на данни (три полета, __init__, __repr__, __eq__), спрете да пишете този шаблон. @dataclass генерира всичко от анотациите ви за тип:
Това са генерирани __init__, __repr__ и __eq__. Добавете @dataclass(frozen=True), за да направите обекта неизменен И да получите __hash__ безплатно.
Правило: първо посегнете към `@dataclass`. Напишете ръчно разработен клас само когато имате нужда от методи, които не са средства за достъп до атрибути.
__slots__ — оптимизиране на паметта (разширено)
По подразбиране всеки екземпляр носи __dict__, така че можете да добавяте произволни атрибути. __slots__ суапове, които диктуват за фиксирана структура от C-ниво — по-малък RAM отпечатък и по-бърз достъп до атрибути:
Използвайте __slots__, когато: (a) създавате милиони екземпляри и RAM има значение, или (b) искате да хванете присвояване на атрибут с печатна грешка по време на изпълнение. В противен случай го пропуснете - той деактивира pickle по подразбиране и прекъсва множественото наследяване по фини начини.
Кога да оставите часовете сами
Твърдо правило: ако вашият клас има ЕДИН метод плюс `__init__`, той вероятно трябва да е функция.
Класовете печелят своята тежест, когато държат state в множество извиквания на метод или когато прилагат добре дефиниран протокол (__iter__, __enter__, __eq__ и т.н.). Еднократната логика е функция.
Наследяване — използвайте го пестеливо
Наследството е реално, но се използва прекалено много. В 2026 Python предпочитайте composition — задръжте друг обект като атрибут, вместо да наследявате от него:
Наследявайте само когато имате истинска връзка „е-а“ И искате да отмените поведението. NamedRect, който наследява от Rect, за да добави поле name, е добре. Изграждането на йерархия на наследяване на пет нива за клас бизнес логика е код, който ще разплитате след 6 месеца.
Класовете са една от най-мощните функции на Python и една от най-лесните за прекомерна употреба. Мисловният модел, който помага: класът е фабрика за обекти, self е обикновен параметър, променливото състояние преминава в __init__, посегнете първо към @dataclass и се върнете обратно към функция, когато класът има по-малко от два метода, пренасящи състояние.
Следваща стъпка: 30 практически ООП урока, обхващащи класове, @dataclass, наследяване срещу композиция и методите dunder — с истински Python, работещ във вашия браузър и AI наставник на всяка стъпка.