Python — объектно-ориентированный язык с начала его существования. Поэтому, создание и использование классов и объектов в Python просто и легко. Эта статья поможет разобраться на примерах в области поддержки объектно-ориентированного программирования Python. Если у вас нет опыта работы с объектно-ориентированным программированием (OOП), ознакомьтесь с вводным курсом или учебным пособием, чтобы понять основные понятия.
Создание классов
Оператор class
создает новое определение класса. Имя класса сразу следует за ключевым словом class
, после которого ставиться двоеточие:
class ClassName:
"""Необязательная строка документации класса"""
class_suite
- У класса есть строка документации, к которой можно получить доступ через
ClassName.__doc__
. class_suite
состоит из частей класса, атрибутов данных и функции.
Пример создания класса на Python:
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.emp_count += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.empCount)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
- Переменная
emp_count
— переменная класса, значение которой разделяется между экземплярами этого класса. Получить доступ к этой переменной можно черезEmployee.emp_count
из класса или за его пределами. - Первый метод
__init__()
— специальный метод, который называют конструктором класса или методом инициализации. Его вызывает Python при создании нового экземпляра этого класса. - Объявляйте другие методы класса, как обычные функции, за исключением того, что первый аргумент для каждого метода
self
. Python добавляет аргументself
в список для вас; и тогда вам не нужно включать его при вызове этих методов.
Создание экземпляров класса
Чтобы создать экземпляры классов, нужно вызвать класс с использованием его имени и передать аргументы, которые принимает метод __init__
.
# Это создаст первый объект класса Employee
emp1 = Employee("Андрей", 2000)
# Это создаст второй объект класса Employee
emp2 = Employee("Мария", 5000)
Доступ к атрибутам
Получите доступ к атрибутам класса, используя оператор .
после объекта класса. Доступ к классу можно получить используя имя переменой класса:
emp1.display_employee()
emp2.display_employee()
print("Всего сотрудников: %d" % Employee.emp_count)
Теперь, систематизируем все.
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.emp_count += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.emp_count)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
# Это создаст первый объект класса Employee
emp1 = Employee("Андрей", 2000)
# Это создаст второй объект класса Employee
emp2 = Employee("Мария", 5000)
emp1.display_employee()
emp2.display_employee()
print("Всего сотрудников: %d" % Employee.emp_count)
При выполнении этого кода, мы получаем следующий результат:
Имя: Андрей. Зарплата: 2000
Имя: Мария. Зарплата: 5000
Всего сотрудников: 2
Вы можете добавлять, удалять или изменять атрибуты классов и объектов в любой момент.
emp1.age = 7 # Добавит атрибут 'age'
emp1.age = 8 # Изменит атрибут 'age'
del emp1.age # Удалит атрибут 'age'
Вместо использования привычных операторов для доступа к атрибутам вы можете использовать эти функции:
getattr(obj, name [, default])
— для доступа к атрибуту объекта.hasattr(obj, name)
— проверить, есть ли вobj
атрибутname
.setattr(obj, name, value)
— задать атрибут. Если атрибут не существует, он будет создан.delattr(obj, name)
— удалить атрибут.
hasattr(emp1, 'age') # возвращает true если атрибут 'age' существует
getattr(emp1, 'age') # возвращает значение атрибута 'age'
setattr(emp1, 'age', 8) #устанавливает атрибут 'age' на 8
delattr(empl, 'age') # удаляет атрибут 'age'
Встроенные атрибуты класса
Каждый класс Python хранит встроенные атрибуты, и предоставляет к ним доступ через оператор .
, как и любой другой атрибут:
__dict__
— словарь, содержащий пространство имен класса.__doc__
— строка документации класса.None
если, документация отсутствует.__name__
— имя класса.__module__
— имя модуля, в котором определяется класс. Этот атрибут__main__
в интерактивном режиме.__bases__
— могут быть пустые tuple, содержащие базовые классы, в порядке их появления в списке базового класса.
Для вышеуказанного класса давайте попробуем получить доступ ко всем этим атрибутам:
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.empCount)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
Когда этот код выполняется, он возвращает такой результат:
Employee.__doc__: Базовый класс для всех сотрудников
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<class 'object'>,)
Employee.__dict__: {'__module__': '__main__', '__doc__': 'Базовый класс для всех сотрудников', 'emp_count': 0, '__init__': <function Employee.__init__ at 0x03C7D7C8>, 'display_count': <function Employee.display_count at 0x03FA6AE0>, 'display_employee': <function Employee.display_employee at 0x03FA6B28>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
Удаление объектов (сбор мусора)
Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. С помощью процесса ‘Garbage Collection’ Python периодически восстанавливает блоки памяти, которые больше не используются.
Сборщик мусора Python запускается во время выполнения программы и тогда, когда количество ссылок на объект достигает нуля. С изменением количества обращений к нему, меняется количество ссылок.
Когда объект присваивают новой переменной или добавляют в контейнер (список, кортеж, словарь), количество ссылок объекта увеличивается. Количество ссылок на объект уменьшается, когда он удаляется с помощью del
, или его ссылка выходит за пределы видимости. Когда количество ссылок достигает нуля, Python автоматически собирает его.
a = 40 # создали объект <40>
b = a # увеличивает количество ссылок <40>
c = [b] # увеличивает количество ссылок <40>
del a # уменьшает количество ссылок <40>
b = 100 # уменьшает количество ссылок <40>
c[0] = -1 # уменьшает количество ссылок <40>
Обычно вы не заметите, когда сборщик мусора уничтожает экземпляр и очищает свое пространство. Но классом можно реализовать специальный метод __del__()
, называемый деструктором. Он вызывается, перед уничтожением экземпляра. Этот метод может использоваться для очистки любых ресурсов памяти.
Пример работы __del__()
Деструктор __del__()
выводит имя класса того экземпляра, который должен быть уничтожен:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print('{} уничтожен'.format(class_name))
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3)) # выведите id объектов
del pt1
del pt2
del pt3
Когда вышеуказанный код выполняется и выводит следующее:
17692784 17692784 17692784
Point уничтожен
В идеале вы должны создавать свои классы в отдельном модуле. Затем импортировать их в основной модуль программы с помощью
import SomeClass
.
Наследование класса в python
Наследование — это процесс, когда один класс наследует атрибуты и методы другого. Класс, чьи свойства и методы наследуются, называют Родителем или Суперклассом. А класс, свойства которого наследуются — класс-потомок или Подкласс.
Вместо того, чтобы начинать с нуля, вы можете создать класс, на основе уже существующего. Укажите родительский класс в круглых скобках после имени нового класса.
Класс наследник наследует атрибуты своего родительского класса. Вы можете использовать эти атрибуты так, как будто они определены в классе наследнике. Он может переопределять элементы данных и методы родителя.
Синтаксис наследования класса
Классы наследники объявляются так, как и родительские классы. Только, список наследуемых классов, указан после имени класса.
class SubClassName(ParentClass1[, ParentClass2, ...]):
"""Необязательная строка документации класса"""
class_suite
Пример наследования класса в Python
class Parent: # объявляем родительский класс
parent_attr = 100
def __init__(self):
print('Вызов родительского конструктора')
def parent_method(self):
print('Вызов родительского метода')
def set_attr(self, attr):
Parent.parent_attr = attr
def get_attr(self):
print('Атрибут родителя: {}'.format(Parent.parent_attr))
class Child(Parent): # объявляем класс наследник
def __init__(self):
print('Вызов конструктора класса наследника')
def child_method(self):
print('Вызов метода класса наследника')
c = Child() # экземпляр класса Child
c.child_method() # вызов метода child_method
c.parent_method() # вызов родительского метода parent_method
c.set_attr(200) # еще раз вызов родительского метода
c.get_attr() # снова вызов родительского метода
Когда этот код выполняется, он выводит следующий результат:
Вызов конструктора класса наследника
Вызов метода класса наследника
Вызов родительского метода
Атрибут родителя: 200
Аналогичным образом вы можете управлять классом с помощью нескольких родительских классов:
class A: # объявите класс A
...
class B: # объявите класс B
...
class C(A, B): # C наследуется от A и B
...
Вы можете использовать функции issubclass()
или isinstance()
для проверки отношений двух классов и экземпляров.
- Логическая функция
issubclass(sub, sup)
возвращает значениеTrue
, если данный подклассsub
действительно является подклассомsup
. - Логическая функция
isinstance(obj, Class)
возвращаетTrue
, еслиobj
является экземпляром классаClass
или является экземпляром подкласса класса.
Переопределение методов
Вы всегда можете переопределить методы родительского класса. В вашем подклассе могут понадобиться специальные функции. Это одна из причин переопределения родительских методов.
Пример переопределения методов:
class Parent: # объявите родительский класс
def my_method(self):
print('Вызов родительского метода')
class Child(Parent): # объявите класс наследник
def my_method(self):
print('Вызов метода наследника')
c = Child() # экземпляр класса Child
c.my_method() # метод переопределен классом наследником
Когда этот код выполняется, он производит следующий результат:
Вызов метода наследника
Популярные базовые методы
В данной таблице перечислены некоторые общие функции. Вы можете переопределить их в своих собственных классах.
№ | Метод, описание и пример вызова |
---|---|
1 | __init__(self [, args...]) — конструктор (с любыми необязательными аргументами)obj = className(args) |
2 | __del__(self) — деструктор, удаляет объектdel obj |
3 | __repr__(self) — программное представление объектаrepr(obj) |
4 | __str__(self) — строковое представление объектаstr(obj) |
Пример использования __add__
Предположим, вы создали класс Vector
для представления двумерных векторов. Что происходит, когда вы используете дополнительный оператор для их добавления? Скорее всего, Python будет против.
Однако вы можете определить метод __add__
в своем классе для добавления векторов и оператор +
будет вести себя так как нужно.
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector ({}, {})'.format(self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
При выполнении этого кода, мы получим:
Vector(7, 8)
Приватные методы и атрибуты
Атрибуты класса могут быть не видимыми вне определения класса. Вам нужно указать атрибуты с __
вначале, и эти атрибуты не будут вызваны вне класса.
Пример приватного атрибута:
class JustCounter:
__secret_count = 0
def count(self):
self.__secret_count += 1
print(self.__secret_count)
counter = JustCounter()
counter.count()
counter.count()
print(counter.__secret_count)
При выполнении данного кода, имеем следующий результат:
1
2
Traceback (most recent call last):
File "test.py", line 12, in <module>
print(counter.__secret_count)
AttributeError: 'JustCounter' object has no attribute '__secret_count'
Вы можете получить доступ к таким атрибутам, так object._className__attrName
. Если вы замените свою последнюю строку следующим образом, то она будет работать.
...
print(counter._JustCounter__secret_count)
При выполнении кода, получаем результат:
1
2
2