Объектно-ориентированное программирование в Python: полное руководство
Обзор терминологии ООП
Класс (Class) — пользовательский прототип для объекта, определяющий набор атрибутов, характеризующих любой объект класса. Атрибуты — это члены данных (переменные класса и экземпляра) и методы, доступные через точечную нотацию.
Переменная класса (Class variable) — переменная, общая для всех экземпляров класса. Определяется внутри класса, но вне методов. Используется реже переменных экземпляра.
Член данных (Data member) — переменная класса или экземпляра, хранящая данные, связанные с классом и его объектами.
Перегрузка функции (Function overloading) — назначение более одного поведения определенной функции. Выполняемая операция варьируется в зависимости от типов объектов или аргументов.
Переменная экземпляра (Instance variable) — переменная, определенная внутри метода и принадлежащая только текущему экземпляру класса.
Наследование (Inheritance) — передача характеристик класса другим классам, производным от него.
Экземпляр (Instance) — отдельный объект определенного класса. Объект
obj, принадлежащий классуCircle, является экземпляром классаCircle.Создание экземпляра (Instantiation) — создание экземпляра класса.
Метод (Method) — специальный вид функции, определенной в определении класса.
Объект (Object) — уникальный экземпляр структуры данных, определенной своим классом. Объект включает члены данных (переменные класса и экземпляра) и методы.
Перегрузка операторов (Operator overloading) — назначение более одной функции определенному оператору.
Создание классов
Оператор class создает новое определение класса. Имя класса сразу следует за ключевым словом class и двоеточием:
class ClassName:
'Optional class documentation string'
class_suite
У класса есть строка документации, доступная через
ClassName.__doc__.class_suiteсостоит из всех операторов, определяющих члены класса, атрибуты данных и функции.
Пример
Простой класс Python:
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
Переменная
empCount— переменная класса, значение которой разделяется всеми экземплярами. Доступна какEmployee.empCountвнутри и вне класса.Первый метод
__init__()— специальный метод, конструктор класса или метод инициализации, который Python вызывает при создании нового экземпляра.Остальные методы объявляются как обычные функции, за исключением того, что первый аргумент каждого метода —
self. Python добавляет аргументselfавтоматически; не нужно включать его при вызове методов.
Создание экземпляров объектов
Для создания экземпляров класса вызывается класс по имени с передачей аргументов, принимаемых его методом __init__:
# Создание первого объекта класса Employee
emp1 = Employee("Zara", 2000)
# Создание второго объекта класса Employee
emp2 = Employee("Manni", 5000)
Доступ к атрибутам
Атрибуты объекта доступны через точечный оператор. Переменная класса доступна через имя класса:
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
Объединение всех концепций:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
emp1 = Employee("Zara", 2000)
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
Результат:
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
Можно добавлять, удалять или изменять атрибуты классов и объектов в любое время:
emp1.age = 7 # Добавить атрибут 'age'
emp1.age = 8 # Изменить атрибут 'age'
del emp1.age # Удалить атрибут 'age'
Вместо обычных операторов для доступа к атрибутам можно использовать функции:
getattr(obj, name[, default]) — доступ к атрибуту объекта.
hasattr(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(emp1, 'age') # Удалить атрибут 'age'
Встроенные атрибуты класса
Каждый класс Python хранит следующие встроенные атрибуты, доступные через точечный оператор:
__dict__ — словарь, содержащий пространство имен класса.
__doc__ — строка документации класса или None, если не определена.
__name__ — имя класса.
__module__ — имя модуля, в котором определен класс. В интерактивном режиме —
"__main__".__bases__ — возможно пустой кортеж, содержащий базовые классы в порядке их появления.
Пример доступа
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", 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__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': , 'empCount': 2, 'displayEmployee': , '__doc__': 'Common base class for all employees', '__init__': }
Уничтожение объектов (сборка мусора)
Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов) для освобождения памяти. Процесс периодического восстановления блоков памяти, которые больше не используются, называется сборкой мусора (Garbage Collection).
Сборщик мусора Python запускается во время выполнения программы и срабатывает, когда счетчик ссылок объекта достигает нуля. Счетчик ссылок меняется по мере изменения количества псевдонимов, указывающих на объект.
Счетчик увеличивается при назначении объекту нового имени или размещении в контейнере (список, кортеж, словарь). Уменьшается при удалении через del, переназначении ссылки или выходе ссылки из области видимости. Когда счетчик достигает нуля, Python автоматически собирает объект.
a = 40 # Создать объект <40>
b = a # Увеличить счетчик ссылок <40>
c = [b] # Увеличить счетчик ссылок <40>
del a # Уменьшить счетчик ссылок <40>
b = 100 # Уменьшить счетчик ссылок <40>
c[0] = -1 # Уменьшить счетчик ссылок <40>
Обычно вы не заметите, когда сборщик мусора уничтожает потерянный экземпляр и освобождает его память. Но класс может реализовать специальный метод __del__(), называемый деструктором, который вызывается перед уничтожением экземпляра. Этот метод может использоваться для очистки непамятных ресурсов, используемых экземпляром.
Пример деструктора
#!/usr/bin/python
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print(class_name, "destroyed")
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3)) # вывод идентификаторов объектов
del pt1
del pt2
del pt3
Результат:
3083401324 3083401324 3083401324
Point destroyed
Примечание: Идеально определять классы в отдельном файле, затем импортировать их в основной программный файл с помощью import.
Наследование классов
Вместо создания с нуля можно создать класс, производный от существующего, перечислив родительский класс в скобках после имени нового класса.
Дочерний класс наследует атрибуты родительского и может использовать их так, будто они определены в дочернем. Дочерний класс также может переопределять члены данных и методы родителя.
Синтаксис
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
Пример
#!/usr/bin/python
class Parent:
parentAttr = 100
def __init__(self):
print("Calling parent constructor")
def parentMethod(self):
print('Calling parent method')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("Parent attribute :", Parent.parentAttr)
class Child(Parent):
def __init__(self):
print("Calling child constructor")
def childMethod(self):
print('Calling child method')
c = Child()
c.childMethod()
c.parentMethod()
c.setAttr(200)
c.getAttr()
Результат:
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
Можно наследовать от нескольких родительских классов:
class A:
...
class B:
...
class C(A, B):
...
Функции issubclass() и isinstance() проверяют отношения классов и экземпляров:
issubclass(sub, sup) — возвращает True, если
subявляется подклассомsup.isinstance(obj, Class) — возвращает True, если
objявляется экземпляромClassили подкласса.
Переопределение методов
Можно всегда переопределять методы родительского класса. Причина — необходимость специальной или иной функциональности в подклассе.
Пример
#!/usr/bin/python
class Parent:
def myMethod(self):
print('Calling parent method')
class Child(Parent):
def myMethod(self):
print('Calling child method')
c = Child()
c.myMethod()
Результат:
Calling child method
Базовые методы перегрузки
Таблица обобщенной функциональности, которую можно переопределить в своих классах:
| № | Метод, описание и пример вызова |
|---|---|
| 1 | __init__(self[, args...]) — конструктор. Вызов: obj = ClassName(args) |
| 2 | __del__(self) — деструктор, удаляет объект. Вызов: del obj |
| 3 | __repr__(self) — вычислимое строковое представление. Вызов: repr(obj) |
| 4 | __str__(self) — печатаемое строковое представление. Вызов: str(obj) |
| 5 | __cmp__(self, x) — сравнение объектов. Вызов: cmp(obj, x) |
Перегрузка операторов
Предположим, вы создали класс Vector для представления двумерных векторов. Что происходит при использовании оператора плюс для их сложения? Скорее всего, Python выдаст ошибку.
Однако можно определить метод __add__ в классе для выполнения сложения векторов, и тогда оператор плюс будет работать как ожидается:
Пример
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (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)
Сокрытие данных
Атрибуты объекта могут быть видимы или невидимы вне определения класса. Нужно назвать атрибуты с префиксом двойного подчеркивания, и тогда они не будут напрямую доступны извне.
Пример
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print(counter.__secretCount)
Результат:
1
2
Traceback (most recent call last):
File "test.py", line 12, in
print(counter.__secretCount)
AttributeError: JustCounter instance has no attribute '__secretCount'
Python защищает такие члены, внутренне меняя имя для включения имени класса. Доступ к таким атрибутам возможен как object._className__attrName. Если заменить последнюю строку на:
print(counter._JustCounter__secretCount)
Результат:
1
2
2
Python
- Классы и объекты в C#: Фундаменты надёжного ООП
- Освоение типов данных в Python: практическое руководство
- Мастерство операторов Python: Полное руководство
- Оператор pass в Python: практическое руководство
- Словари в Python: создание, манипуляции и продвинутые техники
- Пользовательские исключения в Python: практическое руководство
- Объектно-ориентированное программирование в Python: классы, наследование, инкапсуляция и полиморфизм
- Наследование в Python: концепции, синтаксис и практические примеры
- Паттерн Singleton в Java: как реализовать и безопасно использовать
- Автоматизированное визуальное отслеживание объектов на Raspberry Pi и OpenCV