Классы и объекты Python
Узнайте, как работают классы и объекты Python: определение классов, __init__, создание экземпляров, атрибуты, методы и наследование.
Python — это язык объектно-ориентированного программирования (ООП), что означает организацию кода вокруг объектов, а не только функций. Класс — это шаблон, описывающий, какие данные хранит объект и что он умеет делать. Объект — это конкретный экземпляр, созданный на основе этого шаблона.
В этой главе рассматривается:
- Что такое классы и объекты и почему они важны
- Определение класса с
__init__, атрибутами экземпляра и методами - Атрибуты класса в сравнении с атрибутами экземпляра
- Создание и использование объектов
- Изменение и удаление атрибутов
- Проверка типов объектов с помощью
isinstance() - Первое знакомство с наследованием
Для более глубокого изучения наследования смотрите Python Inheritance. Более сложные паттерны ООП описаны в Python Abstract Base Classes.
Что такое класс?
Представьте класс как форму для печенья, а объект — как само печенье. Вы один раз определяете форму (класс), а затем штампуете столько печений (объектов), сколько нужно. Каждый объект имеет одну структуру, но хранит собственные данные.
class Dog:
pass # an empty class — valid but not very useful yetКлючевое слово class, за которым следуют имя и двоеточие, создаёт новый класс. По соглашению имена классов используют CapWords (также называемый PascalCase): MyClass, BankAccount, HttpRequest.
Метод __init__
Метод __init__ (сокращение от initialise) запускается автоматически каждый раз при создании нового объекта. Он устанавливает начальное состояние объекта, присваивая значения его атрибутам экземпляра.
class Dog:
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attributeПервый параметр всегда self — он ссылается на создаваемый объект. Python передаёт его автоматически; вам никогда не нужно указывать его самостоятельно.
Атрибуты экземпляра и атрибуты класса
| Вид | Определяется внутри | Принадлежит | Общий? |
|---|---|---|---|
| Атрибут экземпляра | __init__ (через self) | Каждому объекту | Нет — у каждого объекта своя копия |
| Атрибут класса | Тело класса (вне любого метода) | Самому классу | Да — все объекты используют одну копию |
class Dog:
species = "Canis familiaris" # class attribute — shared by all Dogs
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attribute
fido = Dog("Fido", 3)
bella = Dog("Bella", 5)
print(fido.species) # Canis familiaris
print(bella.species) # Canis familiaris (same class attribute)
print(fido.name) # Fido
print(bella.name) # Bella (different instance attributes)Используйте атрибут класса, когда значение одинаково для каждого объекта данного типа (например, вид для всех Dogs). Используйте атрибуты экземпляра для данных, которые различаются для каждого объекта.
Определение методов
Метод — это функция, определённая внутри класса. Как и __init__, он всегда принимает self в качестве первого параметра, чтобы иметь доступ к атрибутам объекта.
class Dog:
species = "Canis familiaris"
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
return f"{self.name} says: Woof!"
def description(self):
return f"{self.name} is {self.age} years old."Метод вызывается на объекте через точечную нотацию — Python автоматически передаёт объект как self:
fido = Dog("Fido", 3)
print(fido.bark()) # Fido says: Woof!
print(fido.description()) # Fido is 3 years old.Метод __str__
Python вызывает __str__, когда вы передаёте объект в print() или str(). Без него вы получаете бесполезный адрес вроде <__main__.Dog object at 0x...>. Определение этого метода значительно упрощает отладку.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Dog(name={self.name!r}, age={self.age})"
fido = Dog("Fido", 3)
print(fido) # Dog(name='Fido', age=3)Создание и использование объектов
Создание объекта называется инстанциированием. Вы вызываете класс как функцию, передавая аргументы, которые ожидает __init__ (кроме self):
Из одного класса можно создать сколько угодно объектов — каждый из них независим:
class Rectangle:
def __init__(self, width, height=1):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
r1 = Rectangle(4, 3)
r2 = Rectangle(5) # height defaults to 1
print(r1.area()) # 12
print(r1.perimeter()) # 14
print(r2.area()) # 5Обратите внимание, что height=1 задаёт значение по умолчанию: если второй аргумент не указан, Python автоматически использует 1.
Изменение и удаление атрибутов
Атрибуты объекта можно изменять или добавлять после его создания, а также удалять с помощью del:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(p.age) # 30
p.age = 31 # modify an existing attribute
print(p.age) # 31
p.email = "[email protected]" # add a new attribute at runtime
print(p.email) # [email protected]
del p.email # delete the attribute
# print(p.email) # would raise AttributeErrorХотя Python позволяет свободно добавлять атрибуты, чище объявлять все атрибуты внутри __init__, чтобы структура класса была понятна с первого взгляда.
Проверка типов объектов
Используйте isinstance(), чтобы проверить, является ли объект экземпляром определённого класса. Функция возвращает True как для самого класса, так и для любого из его родительских классов:
class Animal:
pass
class Dog(Animal):
pass
rex = Dog()
print(isinstance(rex, Dog)) # True
print(isinstance(rex, Animal)) # True — Dog is a subclass of Animal
print(type(rex) is Dog) # True
print(type(rex) is Animal) # False — type() does not climb the hierarchyВ большинстве случаев предпочитайте isinstance() вместо type() is, поскольку первая корректно обрабатывает подклассы.
Первое знакомство с наследованием
Наследование позволяет новому классу повторно использовать и расширять поведение существующего. Новый класс (дочерний, или подкласс) автоматически получает все атрибуты и методы родителя:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound."
class Dog(Animal):
def speak(self): # override the parent method
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
dog = Dog("Rex")
cat = Cat("Whiskers")
print(dog.speak()) # Rex says: Woof!
print(cat.speak()) # Whiskers says: Meow!
print(isinstance(dog, Animal)) # TrueИ Dog, и Cat наследуют __init__ от Animal, поэтому им не нужно повторять его. Они только переопределяют speak(), чтобы задать поведение, специфичное для каждого вида.
Полное описание наследования — включая super(), множественное наследование и порядок разрешения методов — смотрите в Python Inheritance.
Когда следует использовать класс?
Классы наиболее уместны, когда:
- У вас есть данные и поведение, которые принадлежат вместе (банковский счёт, умеющий пополняться и списывать средства).
- Вам нужно несколько независимых объектов одного типа (много объектов
Dog, каждый с разными именами и возрастом). - Вы хотите смоделировать реальные сущности с чёткой идентичностью.
Простые вспомогательные функции, которые лишь преобразуют входные данные в выходные, зачастую лучше оставить обычными функциями. Python не требует помещать всё в класс.
Итоги
| Концепция | Что делает |
|---|---|
class | Определяет новый тип |
__init__ | Инициализирует атрибуты экземпляра при создании объекта |
self | Ссылается на текущий объект внутри метода |
| Атрибут экземпляра | Данные, принадлежащие одному конкретному объекту |
| Атрибут класса | Данные, общие для всех объектов класса |
| Метод | Функция, определённая внутри класса; всегда принимает self |
__str__ | Управляет тем, как print() отображает объект |
isinstance() | Проверяет, является ли объект экземпляром класса или его подклассов |
| Наследование | Позволяет дочернему классу повторно использовать и расширять родительский класс |