Методы классов
Содержание
- Что такое декоратор
- Методы класса
@classmethod - Создание объектов через
cls - Статические методы
@staticmethod - Защищенные и приватные атрибуты
@property, getter и setter- Типичные ошибки
- Практика
Что такое декоратор
Декоратор в Python - это способ изменить поведение функции или метода, не переписывая его тело напрямую. Декоратор пишется над функцией через @. Если функции подзабылись — вернись к 03.01 - Функции.
В этой теме нам важны встроенные декораторы:
@classmethod- делает метод методом класса;@staticmethod- делает метод статическим;@property- позволяет обращаться к методу как к атрибуту.
Пока не нужно глубоко разбирать внутреннее устройство декораторов. Достаточно понимать, что они меняют способ вызова метода.
Методы класса @classmethod
Метод класса связан не с конкретным объектом, а с самим классом. Первый параметр такого метода - cls. Он указывает на класс, через который метод вызвали.
class User:
role = "student"
@classmethod
def get_role(cls):
return cls.role
print(User.get_role())cls.role в этом примере примерно то же самое, что User.role, но cls гибче: если метод унаследует другой класс, cls будет указывать уже на дочерний класс.
Метод класса можно вызвать и через объект, но обычно его вызывают через имя класса, чтобы код читался понятнее.
user = User()
print(user.get_role()) # работает, но лучше User.get_role()Когда использовать методы класса
Методы класса удобны, когда нужно:
- работать с атрибутами класса;
- вести общий счетчик объектов;
- создать альтернативный конструктор;
- вернуть объект класса через
cls(...); - написать логику, которая относится ко всему классу, а не к одному экземпляру.
Пример со счетчиком:
class Course:
created_count = 0
def __init__(self, title):
self.title = title
Course.created_count += 1
@classmethod
def get_created_count(cls):
return cls.created_count
course1 = Course("Python")
course2 = Course("AQA")
print(Course.get_created_count())Создание объектов через cls
Метод класса может создавать объект. Это часто называют альтернативным конструктором.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
current_year = 2026
age = current_year - birth_year
return cls(name, age)
person = Person.from_birth_year("Ирина", 1998)
print(person.name, person.age)В строке return cls(name, age) вызывается конструктор класса. Если этот метод унаследует дочерний класс, cls позволит создать объект именно дочернего класса.
Статические методы @staticmethod
Статический метод находится внутри класса, но не получает ни self, ни cls. По сути, это обычная функция, которую положили внутрь класса для логической группировки.
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def is_positive(value):
return value > 0
print(MathUtils.add(6, 9))
print(MathUtils.is_positive(-3))Такой метод не использует данные объекта и не обращается к атрибутам класса. Он просто логически относится к классу.
Использовать staticmethod удобно для утилитных действий: расчеты, форматирование, проверки, конвертация значений.
Защищенные и приватные атрибуты
В Python нет жестких модификаторов доступа как в некоторых других языках. Вместо этого используются соглашения по именам.
Одно подчеркивание _value
Одно подчеркивание означает: атрибут предназначен для внутреннего использования.
class Account:
def __init__(self, balance):
self._balance = balancePython не запрещает обратиться к _balance снаружи, но разработчик таким именем предупреждает: напрямую менять это значение не стоит.
Два подчеркивания __value
Двойное подчеркивание включает name mangling: Python изменяет имя атрибута внутри класса.
class Account:
def __init__(self, balance):
self.__balance = balanceСнаружи account.__balance не будет доступен напрямую. Это помогает защитить внутренние данные от случайного изменения.
Но важно помнить: в Python это не абсолютная безопасность, а механизм для контроля интерфейса класса.
@property, getter и setter
@property позволяет сделать метод похожим на обычный атрибут при чтении.
class Product:
def __init__(self, name, price):
self.name = name
self._price = price
@property
def price(self):
return self._price
product = Product("Клавиатура", 3500)
print(product.price)price вызывается без скобок, хотя внутри это метод.
Чтобы контролировать запись значения, добавляют setter.
class Product:
def __init__(self, name, price):
self.name = name
self._price = price
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if value < 0:
raise ValueError("Цена не может быть отрицательной")
self._price = value
product = Product("Монитор", 18000)
product.price = 17000
print(product.price)Getter должен возвращать значение через return. Setter обычно ничего не возвращает: его задача - проверить и записать данные.
Когда использовать property
@property полезен, когда нужно:
- проверить значение перед записью;
- сделать вычисляемое свойство;
- скрыть внутреннюю реализацию;
- сохранить простой интерфейс
obj.attr, но добавить логику внутри; - не ломать внешний код, если внутренняя структура класса изменилась.
Типичные ошибки
Неправильное имя setter
class WrongExample:
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value
@value.setter
def set_value(self, new_value): # ошибка: имя должно быть value
self._value = new_valueПравильно:
class CorrectExample:
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value
@value.setter
def value(self, new_value):
self._value = new_valueСлишком много параметров в setter
Setter принимает только self и одно новое значение.
class Point:
def __init__(self, x, y):
self._x = x
self._y = y
@property
def coordinates(self):
return self._x, self._y
@coordinates.setter
def coordinates(self, value):
x, y = value
self._x = x
self._y = ySetter без getter
Сначала создается getter через @property, и только потом можно добавить setter через @property_name.setter.
Практика
-
Задание 1: реестр курсов
Создайте класс
CourseRegistryс атрибутом классаcourses. Реализуйте метод классаadd_course, который добавляет новый курс, если его еще нет, иshow_courses, который выводит список курсов.Подсказка: проверку на дубликаты лучше делать внутри classmethod.
-
Задание 2: альтернативный конструктор
Создайте класс
UserProfile, который принимаетnameиage. Добавьте метод классаfrom_birth_year(cls, name, birth_year), который вычисляет возраст и возвращает объект.Проверьте на значениях
birth_year=1997иbirth_year=2003. -
Задание 3: статические методы
Создайте класс
CurrencyConverterсо статическими методами:usd_to_eur(amount)по курсу0.92;eur_to_usd(amount)по курсу1.09;rub_to_usd(amount)по курсу0.011.
Методы не должны использовать
selfилиcls. -
Задание 4: скорость автомобиля
Создайте класс
Carсо свойствомspeed.Требования:
- скорость не может быть меньше 0;
- скорость не может быть больше 280;
- корректные значения сохраняются;
- некорректные значения вызывают
ValueError.
car = Car(90) print(car.speed) car.speed = 140 # car.speed = -20 # car.speed = 320 -
Задание 5: исправить сломанный код
Найдите и исправьте ошибки.
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): print(f"Текущая температура: {self._celsius}") @celsius.setter def set_celsius(self, value): if value < -273.15: raise ValueError("Ниже абсолютного нуля") self._celsius = value @property.setter def fahrenheit(self, value): self.celsius = (value - 32) * 5 / 9Что нужно проверить:
- getter должен возвращать значение;
- имя setter должно совпадать с именем свойства;
- нельзя создать setter без getter;
- для
fahrenheitнужен корректный getter и setter.
-
Задание 6: вычисляемые свойства
Создайте класс
Rectangleс атрибутамиwidthиheight.Свойства:
area- только чтение;perimeter- только чтение;diagonal- только чтение;dimensions- чтение и запись кортежа(width, height).
При записи новых размеров проверяйте, что оба значения положительные.
-
Задание 7: счет с историей
Создайте класс
Counter.Требования:
- свойство
valueхранит текущее значение; - свойство
historyтолько для чтения и хранит историю значений; - свойство
change_countпоказывает количество изменений; - одинаковое значение подряд не должно добавляться в историю.
- свойство
-
Задание 8: банковский счет
Создайте класс
BankAccountс продвинутой валидацией.Атрибуты:
account_number- только для чтения после создания;balance- не может быть отрицательным;owner_name- непустая строка без цифр;is_active- операции возможны только при активном счете.
Дополнительно:
formatted_balance;transaction_history;account_info;- методы
deposit(amount)иwithdraw(amount).
Короткий итог
@classmethod работает с классом через cls, @staticmethod хранит связанную по смыслу утилитную функцию внутри класса, а @property позволяет добавить контроль чтения и записи атрибутов без усложнения внешнего интерфейса. Одинарное подчеркивание предупреждает о внутреннем атрибуте, двойное подчеркивание сильнее скрывает имя внутри класса.
⬅️ Назад: 03.05 - Наследование | Далее: 03.07 - Что такое импорт в Python ➡️ Модуль: 03 - MOC