💻 Практические примеры

⚡ Минимальный рабочий пример

class Book:
    library_name: str = "Central Library"

    def __init__(self, title: str) -> None:
        self.title = title


book = Book("1984")
print(book.library_name)  # Central Library
Book.library_name = "City Library"
print(book.library_name)  # City Library

Пример 1. Атрибут класса общий для всех объектов

У всех книг одна и та же библиотека, поэтому library_name хранится в классе.

class Book:
    library_name: str = "Central Library"

    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author


book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")

print(Book.library_name)
print(book1.library_name)
print(book2.library_name)

Что происходит: атрибут library_name принадлежит классу Book. Оба объекта читают его из класса, потому что у самих объектов такого поля нет.

Пример 2. Изменение атрибута класса через класс

Если переименовать библиотеку через Book.library_name, изменение увидят все экземпляры.

class Book:
    library_name: str = "Central Library"

    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author


book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")

Book.library_name = "City Library"
print(book1.library_name)  # City Library
print(book2.library_name)  # City Library

Пример 3. Присваивание через объект создаёт собственный атрибут

Это самый частый источник путаницы. Запись через объект не меняет класс, а добавляет поле в конкретный экземпляр.

class Book:
    library_name: str = "Central Library"

    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author


book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")

book1.library_name = "Private Shelf"
print(book1.library_name)  # Private Shelf
print(book2.library_name)  # Central Library
print(Book.library_name)   # Central Library

print(book1.__dict__)
print(book2.__dict__)

Пример 4. Поле объекта по умолчанию

Параметр language имеет значение по умолчанию, но его можно переопределить при создании объекта.

class Book:
    def __init__(self, title: str, author: str, language: str = "English") -> None:
        self.title = title
        self.author = author
        self.language = language


book1 = Book("1984", "George Orwell")
book2 = Book("Cien años de soledad", "Gabriel García Márquez", "Spanish")

print(book1.language)  # English
print(book2.language)  # Spanish

Пример 5. Классовый метод как счётчик объектов

Каждое создание книги увеличивает общий счётчик. Классовый метод show_total читает атрибут класса.

class Book:
    total_books: int = 0

    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author
        Book.total_books += 1

    @classmethod
    def show_total(cls) -> None:
        print(f"Total books: {cls.total_books}")


Book.show_total()                          # Total books: 0
book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")
Book.show_total()                          # Total books: 2
book1.show_total()                         # Total books: 2

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

Классовый метод удобен, когда объект нужно создать не из отдельных аргументов, а из строки.

class Book:
    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author

    @classmethod
    def from_string(cls, data: str) -> "Book":
        title, author = data.split(" - ")
        return cls(title, author)


book = Book.from_string("1984 - George Orwell")
print(book.title)
print(book.author)

Пример 7. Статический метод для валидации

Метод не использует self и cls, но логически относится к классу Book.

class Book:
    def __init__(self, title: str) -> None:
        self.title = title

    @staticmethod
    def is_title_valid(title: str) -> bool:
        return isinstance(title, str) and len(title) > 0


print(Book.is_title_valid("1984"))  # True
print(Book.is_title_valid(""))      # False
print(Book.is_title_valid(123))     # False

Пример 8. __str__ и __repr__

Два метода отвечают за строковое представление объекта: один для пользователя, другой для разработчика.

class Book:
    def __init__(self, title: str, author: str) -> None:
        self.title = title
        self.author = author

    def __str__(self) -> str:
        return f"{self.title} by {self.author}"

    def __repr__(self) -> str:
        return f"Book(title={self.title!r}, author={self.author!r})"


book = Book("1984", "George Orwell")
print(book)        # 1984 by George Orwell
print(str(book))   # 1984 by George Orwell
print(repr(book))  # Book(title='1984', author='George Orwell')

books = [book]
print(books)       # [Book(title='1984', author='George Orwell')]

Как запустить в VS Code

  1. Создайте папку python-fundamentals-practice/lesson-68/.
  2. Создайте файл attributes.py и скопируйте в него любой пример.
  3. Откройте терминал в VS Code (Ctrl + `).
  4. Активируйте виртуальное окружение: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  5. Запустите: python attributes.py
Проверить по документации: примеры дополнены аннотациями типов, которых не было в исходном материале. Подробнее о типах см. typing и PEP 484.