HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В объектно-ориентированном программировании встречаются различные типы методов и атрибутов, включая статические методы, методы класса и классовые атрибуты. Хотя эти элементы являются фундаментальными, они часто могут сбивать с толку, особенно, когда дело доходит до наследования и переопределения.</p>
1 <p>В объектно-ориентированном программировании встречаются различные типы методов и атрибутов, включая статические методы, методы класса и классовые атрибуты. Хотя эти элементы являются фундаментальными, они часто могут сбивать с толку, особенно, когда дело доходит до наследования и переопределения.</p>
2 <p>Как использовать статические методы, не нарушая при этом иерархию наследования? Как правильно применять атрибуты класса, и в чем их отличие от атрибутов экземпляра? В этом уроке мы разберем эти и другие вопросы, а также рассмотрим сложные моменты работы с методами и атрибутами.</p>
2 <p>Как использовать статические методы, не нарушая при этом иерархию наследования? Как правильно применять атрибуты класса, и в чем их отличие от атрибутов экземпляра? В этом уроке мы разберем эти и другие вопросы, а также рассмотрим сложные моменты работы с методами и атрибутами.</p>
3 <h2>Статические методы и их особенности</h2>
3 <h2>Статические методы и их особенности</h2>
4 <p>В отличие от экземпляров классов обращение к статическим методам в Python работает немного иначе. Когда мы объявляем метод как статический с помощью декоратора @staticmethod, он ведет себя как обычная функция, но она находится в пространстве имен класса:</p>
4 <p>В отличие от экземпляров классов обращение к статическим методам в Python работает немного иначе. Когда мы объявляем метод как статический с помощью декоратора @staticmethod, он ведет себя как обычная функция, но она находится в пространстве имен класса:</p>
5 <p>В этом примере статический метод test() вызывает A.who(), который находится внутри класса A. Поэтому A.who() указывает на сам A. Никакая иерархия наследования не может изменить это поведение.</p>
5 <p>В этом примере статический метод test() вызывает A.who(), который находится внутри класса A. Поэтому A.who() указывает на сам A. Никакая иерархия наследования не может изменить это поведение.</p>
6 <p>Подобное поведение можно рассматривать как ограничение. Фактически оно игнорирует факт наследования. Но перед тем, как вдаваться в детали, нужно немного разобраться с тем, какую роль играют статические методы в классах.</p>
6 <p>Подобное поведение можно рассматривать как ограничение. Фактически оно игнорирует факт наследования. Но перед тем, как вдаваться в детали, нужно немного разобраться с тем, какую роль играют статические методы в классах.</p>
7 <p>В этом примере мы просто объявляем класс A со статическим методом who(), который выводит 'A'. Когда мы вызываем A.who(), он корректно выводит 'A' и показывает, что статические методы можно вызывать напрямую от класса. Но если нам нужно хранить информацию, специфичную для класса, мы можем использовать классовые атрибуты.</p>
7 <p>В этом примере мы просто объявляем класс A со статическим методом who(), который выводит 'A'. Когда мы вызываем A.who(), он корректно выводит 'A' и показывает, что статические методы можно вызывать напрямую от класса. Но если нам нужно хранить информацию, специфичную для класса, мы можем использовать классовые атрибуты.</p>
8 <h2>Классовые атрибуты и их использование</h2>
8 <h2>Классовые атрибуты и их использование</h2>
9 <p>Данные в классовых атрибутах относятся к классу в целом. Они "описывают" его. В Python самый распространенный пример - это связь сущности с таблицей в базе данных, где она хранится:</p>
9 <p>Данные в классовых атрибутах относятся к классу в целом. Они "описывают" его. В Python самый распространенный пример - это связь сущности с таблицей в базе данных, где она хранится:</p>
10 <p>Почему эту информацию нужно хранить в классовом атрибуте? Потому что она не принадлежит конкретному объекту.</p>
10 <p>Почему эту информацию нужно хранить в классовом атрибуте? Потому что она не принадлежит конкретному объекту.</p>
11 <p>Представим, что мы хотим узнать из кода, в какую таблицу будут сохраняться пользователи. Если бы эта информация была связана с конкретным объектом, пришлось бы создавать объект только ради того, чтобы узнать ответ на наш вопрос. Это бессмысленно.</p>
11 <p>Представим, что мы хотим узнать из кода, в какую таблицу будут сохраняться пользователи. Если бы эта информация была связана с конкретным объектом, пришлось бы создавать объект только ради того, чтобы узнать ответ на наш вопрос. Это бессмысленно.</p>
12 <p>Для сохранения сущности в базу данных обычно используются библиотеки ORM (Object-Relationship Mapping), которые знают, как сохранять сущности в базу и как извлечь их. Большинство из них построено на наследовании. Любая сущность должна наследоваться от специального базового класса, который содержит в себе общую логику для работы с базой данных:</p>
12 <p>Для сохранения сущности в базу данных обычно используются библиотеки ORM (Object-Relationship Mapping), которые знают, как сохранять сущности в базу и как извлечь их. Большинство из них построено на наследовании. Любая сущность должна наследоваться от специального базового класса, который содержит в себе общую логику для работы с базой данных:</p>
13 <p>Метод save() закомментирован, потому что в Python нет встроенной функции сохранения объектов в базу данных. Вместо этого мы используем библиотеки ORM, такие, как SQLAlchemy или Django ORM.</p>
13 <p>Метод save() закомментирован, потому что в Python нет встроенной функции сохранения объектов в базу данных. Вместо этого мы используем библиотеки ORM, такие, как SQLAlchemy или Django ORM.</p>
14 <p>Для сохранения пользователя в базу недостаточно знать, что сохранять. Еще нужно понимать, куда сохранять. И эта информация записана в классовом атрибуте:</p>
14 <p>Для сохранения пользователя в базу недостаточно знать, что сохранять. Еще нужно понимать, куда сохранять. И эта информация записана в классовом атрибуте:</p>
15 <p>В данном случае класс User, который наследуется от BaseEntity, содержит классовый атрибут _table на 'users'. Это означает, что когда мы сохраняем объект User, мы знаем, что его следует сохранить в таблице 'users'. Но что делать, если мы хотим обратиться к этой информации из базового класса? Рассмотрим этот нюанс подробнее.</p>
15 <p>В данном случае класс User, который наследуется от BaseEntity, содержит классовый атрибут _table на 'users'. Это означает, что когда мы сохраняем объект User, мы знаем, что его следует сохранить в таблице 'users'. Но что делать, если мы хотим обратиться к этой информации из базового класса? Рассмотрим этот нюанс подробнее.</p>
16 <h2>Работа с наследованием и методы класса</h2>
16 <h2>Работа с наследованием и методы класса</h2>
17 <p>Возникает вопрос, можно ли добраться до нее из базового класса BaseEntity?</p>
17 <p>Возникает вопрос, можно ли добраться до нее из базового класса BaseEntity?</p>
18 <p>Рассмотрим следующий код:</p>
18 <p>Рассмотрим следующий код:</p>
19 <p>В этом примере мы пытаемся получить доступ к атрибуту _table через метод get_table(). Проблема в том, что такой код не сработает. User.get_table() ссылается на BaseEntity, и вызов get_table() завершится с ошибкой, так как в этом классе нет классового атрибута _table.</p>
19 <p>В этом примере мы пытаемся получить доступ к атрибуту _table через метод get_table(). Проблема в том, что такой код не сработает. User.get_table() ссылается на BaseEntity, и вызов get_table() завершится с ошибкой, так как в этом классе нет классового атрибута _table.</p>
20 <p>Из этой ситуации есть выход: использовать @classmethod:</p>
20 <p>Из этой ситуации есть выход: использовать @classmethod:</p>
21 <p>Изменив get_table() на метод класса с помощью декоратора @classmethod, мы можем вызвать метод для экземпляра класса и получить доступ к атрибуту _table текущего класса. Теперь вызов get_table() вернет значение классового атрибута _table, определенного в том классе, с объектом которого идет работа прямо сейчас.</p>
21 <p>Изменив get_table() на метод класса с помощью декоратора @classmethod, мы можем вызвать метод для экземпляра класса и получить доступ к атрибуту _table текущего класса. Теперь вызов get_table() вернет значение классового атрибута _table, определенного в том классе, с объектом которого идет работа прямо сейчас.</p>
22 <p>Обратите внимание, что мы используем cls вместо self. Ключевое слово cls используется в Python для обозначения текущего класса в контексте метода класса.</p>
22 <p>Обратите внимание, что мы используем cls вместо self. Ключевое слово cls используется в Python для обозначения текущего класса в контексте метода класса.</p>
23 <p>В Python метод класса, определенный с использованием декоратора @classmethod, автоматически получает ссылку на класс, а не на экземпляр. Это позволяет нам обращаться к атрибутам класса внутри метода класса, даже если этот атрибут переопределен в подклассе.</p>
23 <p>В Python метод класса, определенный с использованием декоратора @classmethod, автоматически получает ссылку на класс, а не на экземпляр. Это позволяет нам обращаться к атрибутам класса внутри метода класса, даже если этот атрибут переопределен в подклассе.</p>
24 <p>Например, определим метод get_table в базовом классе BaseEntity и переопределим атрибут _table в классе User:</p>
24 <p>Например, определим метод get_table в базовом классе BaseEntity и переопределим атрибут _table в классе User:</p>
25 <p>Можно заметить, что вызов get_table для класса User возвращает значение атрибута _table, определенное в классе User, а не в BaseEntity. Это показывает, что метод класса get_table работает с текущим классом (User), а не с классом, в котором он был определен (BaseEntity).</p>
25 <p>Можно заметить, что вызов get_table для класса User возвращает значение атрибута _table, определенное в классе User, а не в BaseEntity. Это показывает, что метод класса get_table работает с текущим классом (User), а не с классом, в котором он был определен (BaseEntity).</p>
26 <h2>Выводы</h2>
26 <h2>Выводы</h2>
27 <p>Использование методов класса с помощью декоратора @classmethod позволяет нам работать с атрибутами текущего класса, а не базового класса. Это особенно полезно при работе с наследованием, когда подклассы могут переопределить некоторые атрибуты класса.</p>
27 <p>Использование методов класса с помощью декоратора @classmethod позволяет нам работать с атрибутами текущего класса, а не базового класса. Это особенно полезно при работе с наследованием, когда подклассы могут переопределить некоторые атрибуты класса.</p>
28 <p>Ключевое слово cls используется для обозначения текущего класса в контексте метода класса. Помимо этого мы увидели, как классовые атрибуты могут быть использованы для хранения информации, специфичной для класса, такой как имя таблицы в базе данных для сохранения объектов этого класса.</p>
28 <p>Ключевое слово cls используется для обозначения текущего класса в контексте метода класса. Помимо этого мы увидели, как классовые атрибуты могут быть использованы для хранения информации, специфичной для класса, такой как имя таблицы в базе данных для сохранения объектов этого класса.</p>