📖 Теория: классы, даты и методы

⚡ Кратко

Класс Student хранит имя и дату рождения, автоматически нумерует студентов, проверяет возраст и умеет создаваться из строки. Ключевые механизмы: атрибут класса current_id, парсинг datetime.date, методы __str__/__repr__, @classmethod и @staticmethod.

  • datetime.strptime(text, "%Y-%m-%d").date() превращает строку в дату.
  • Атрибут класса current_id общий для всех объектов и служит счётчиком.
  • Возраст считаем через разницу лет с поправкой на месяц и день рождения.
  • @classmethod получает cls и может быть альтернативным конструктором.
  • @staticmethod не зависит от экземпляра и класса, но логически относится к классу.

Класс как шаблон объекта

Класс Student описывает, какие данные хранит студент и какие операции с ним можно выполнять. Поля объекта создаются в __init__, поля класса — в теле класса.

class Student:
    current_id: int = 1  # атрибут класса — общий счётчик

    def __init__(self, name: str, birth_date: str) -> None:
        self.name = name          # атрибут объекта
        self.student_id = Student.current_id
        Student.current_id += 1   # увеличиваем счётчик

Парсинг даты из строки

Дата рождения приходит в виде строки "YYYY-MM-DD". Модуль datetime умеет превращать такую строку в объект date.

from datetime import datetime, date

birth_date = datetime.strptime("2005-05-10", "%Y-%m-%d").date()
print(birth_date)          # 2005-05-10
print(type(birth_date))    # <class 'datetime.date'>
💡 На заметку: в Python 3.7+ для формата YYYY-MM-DD можно также использовать date.fromisoformat("2005-05-10"). В исходном материале используется datetime.strptime.

Проверка возраста

Перед созданием объекта убеждаемся, что студенту не менее 16 лет. Если нет — выбрасываем ValueError.

from datetime import date

class Student:
    MIN_AGE: int = 16

    def is_valid_birthdate(self) -> bool:
        return self.calculate_age() >= self.MIN_AGE

    def calculate_age(self) -> int:
        today = date.today()
        age = today.year - self.birth_date.year
        if (today.month, today.day) < (self.birth_date.month, self.birth_date.day):
            age -= 1
        return age

Почему важна поправка: если сегодня 10 марта 2026 года, а студент родился 15 марта 2009 года, простое вычитание даст 17, хотя ему ещё исполнилось только 16. Сравнение месяца и дня исправляет это.

Строковые представления

Метод __str__ вызывается функцией print и должен вернуть удобную для человека строку. __repr__ используется в отладке и коллекциях; его результат желательно показывать «как код».

class Student:
    def __init__(self, name: str, birth_date: str) -> None:
        self.name = name
        self.birth_date = birth_date

    def __str__(self) -> str:
        return f"Student: {self.name}, birth_date: {self.birth_date}, ID: {self.student_id}"

    def __repr__(self) -> str:
        return (
            f"Student(name={self.name!r}, birth_date={self.birth_date!r}, "
            f"student_id={self.student_id!r})"
        )

Альтернативный конструктор

Классовый метод from_string создаёт объект из строки вида "Bob, 2001-12-03". Первым аргументом он получает cls — ссылку на класс, поэтому можно вызвать cls(name, date_string).

class Student:
    def __init__(self, name: str, birth_date: str) -> None:
        self.name = name
        self.birth_date = birth_date

    @classmethod
    def from_string(cls, data: str) -> "Student":
        name, date_string = map(str.strip, data.split(","))
        return cls(name, date_string)


student = Student.from_string("Bob, 2001-12-03")
print(student.name)         # Bob
print(student.birth_date)   # 2001-12-03

Статический метод для фильтрации

Статический метод filter_by_min_age не использует self и cls, но логически принадлежит классу Student. Он получает список студентов и возраст, возвращает отфильтрованный список.

class Student:
    # ... остальные методы ...

    @staticmethod
    def filter_by_min_age(students: list["Student"], min_age: int) -> list["Student"]:
        return [s for s in students if s.calculate_age() >= min_age]
Проверить по документации: аннотации типов в примерах выходят за рамки исходного материала. Подробнее см. typing и PEP 484.