Атрибуты класса
Атрибут класса (class attribute, поле класса) — это переменная, принадлежащая самому классу, а не отдельному объекту. Все экземпляры могут получить к ней доступ, а значение хранится в одном месте.
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) # Central Library — через класс
print(book1.library_name) # Central Library — через объект
print(book2.library_name) # Central Library
Доступ к атрибутам
Поля класса можно читать и изменять как внутри класса, так и снаружи. Но поведение при изменении отличается в зависимости от того, обращаемся ли мы через класс или через объект.
Чтение поля класса
class Book:
library_name: str = "Central Library"
def __init__(self, title: str, author: str) -> None:
self.title = title
self.author = author
def show_library(self) -> None:
print(self.library_name) # доступ через self
print(Book.library_name) # доступ через имя класса
print(Book.library_name) # через класс
book = Book("1984", "George Orwell")
print(book.library_name) # через объект
book.show_library() # изнутри метода
Изменение через класс
Если присвоить новое значение через имя класса, оно изменится для всех экземпляров, у которых нет собственного атрибута с таким именем.
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
Изменение через объект создаёт атрибут объекта
При присваивании через объект Python создаёт новый атрибут в экземпляре. Атрибут класса при этом не меняется.
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
Механизм поиска атрибутов
Когда мы обращаемся к полю через объект (obj.attr), Python сначала ищет этот атрибут у самого объекта. Если не находит — продолжает поиск в классе, из которого объект был создан.
obj.attr = value всегда создаёт или изменяет атрибут объекта, если только attr не является дескриптором с методом __set__ (например, property). В рамках этого урока думайте так: запись — в объект, чтение — сначала объект, потом класс.
Магическое поле __dict__
__dict__ — это словарь, в котором хранятся явно заданные атрибуты объекта или класса. У объекта в нём только собственные атрибуты, у класса — атрибуты класса и методы.
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.__dict__) # {'title': '1984', 'author': 'George Orwell', 'library_name': 'Private Shelf'}
print(book2.__dict__) # {'title': 'Brave New World', 'author': 'Aldous Huxley'}
print(Book.__dict__.keys()) # ключи атрибутов и методов класса
__dict__ и встроенная функция vars() описаны в разделе object.__dict__ документации Python.
Добавление атрибутов извне
В Python можно добавить атрибут к классу или объекту в любой момент. Но так делать не рекомендуется: такие поля неочевидны, их сложно отслеживать, и они могут привести к ошибкам.
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.book_color = "black" # атрибут объекта извне
Book.default_language = "eng" # атрибут класса извне
print(book1.book_color) # black
print(book1.default_language) # eng
# print(book2.book_color) # AttributeError
Поля объекта по умолчанию
Метод __init__ — это обычная функция, поэтому у него могут быть параметры со значениями по умолчанию. Это удобно, когда у большинства объектов некоторое поле одно и то же, но иногда нужно задать другое.
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
Классовые методы
Классовый метод работает не с конкретным объектом, а с самим классом. Вместо self он получает cls — ссылку на класс, из которого вызван. Для объявления используется декоратор @classmethod.
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 — можно вызвать и через объект
Альтернативный конструктор
Классовые методы часто используют для создания объектов из других форматов данных.
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) # 1984
print(book.author) # George Orwell
Статические методы
Статический метод не зависит ни от объекта, ни от класса. Это обычная функция, логически связанная с классом. Для объявления используется декоратор @staticmethod.
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
Магические методы __str__ и __repr__
По умолчанию print(obj) выводит техническую информацию об объекте. Чтобы получить читаемый вывод, определяют метод __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')
__str__ должен вернуть строку, а не печатать её. Если __str__ не определён, Python использует __repr__.
__str__ и __repr__, а также рекомендации по их реализации, описаны в Data model.