ООП предлагает отличающийся и часто более эффективный способ программирования, который предусматривает разложение кода на составляющие с целью минимизации избыточности и написания новых программ путем настройки существующего кода, а не его изменения на месте.
Классы предназначены для создания и управления новыми объектами и поддерживают наследование — механизм настройки и многократного применения кода.
### Для чего используются классы?
**Наследование**
Cвойства должны быть реализованы только один раз для общего случая и могут многократно применяться частично или полностью всеми типами объектов, которые возможно придется создавать в будущем.
**Композиция**
Обхъекто содержит другие объекты и активизирует их для выполнения соответствующих распоряжений. Каждый компонент может быть реализован в виде класса, который определяет собственное поведение и взаимосвязи.
**Множество экземпляров**
Классы по существу представляют собой фабрики для генерирования одного и более объектов. При каждом обращении к классу мы генерируем новый объект с отдельным пространством имен. Каждый объект, сгенерированный из класса, имеет доступ к атрибутам класса и получает собственное пространство имен для данных, которые варьируются от объекта к объекту.
**Настройка через наследование**
Классы также поддерживают понятие наследования, принятое в ООП; мы мо жем расширить класс за счет переопределения его атрибутов вне самого клас са в новых программных компонентах, реализованных как подклассы.
**Перегрузка операций**
За счет предоставления специальных методов протокола классы могут опреде лять объекты, реагирующие на всевозможные операции, которые мы видели в работе со встроенными типами.
1. @classmethod - это обычный метод класса, имеющий доступ ко всем атрибутам класса, через который он был вызван.
2. @staticmethod — это вроде обычной функции, определенной внутри класса, которая не имеет доступа к экземпляру, поэтому ее можно вызывать без создания экземпляра класса.
3. __getattr__ Python будет вызывать метод __getattr__ всякий раз, когда вы запросите атрибут, который еще не был определен.
4. __getattribute__ python вызывает этот метод для каждого атрибута независимо от того, существует он или нет.
5. __setattr__ перехватывает все попытки присваивания зачений атрибутам. Если этот метод определен, выражение self.attr = value будет преобразовано в вызов метода self.__setattr__(‘attr’, value).