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