Учебники

Python 3 – Объектно-ориентированный

Python был объектно-ориентированным языком со времени его существования. Благодаря этому создавать и использовать классы и объекты очень просто. Эта глава поможет вам стать экспертом в использовании поддержки объектно-ориентированного программирования в Python.

Если у вас нет опыта работы с объектно-ориентированным (ОО) программированием, вы можете обратиться к вводному курсу по нему или хотя бы к какому-то учебнику, чтобы иметь представление об основных понятиях.

Тем не менее, вот небольшое введение объектно-ориентированного программирования (ООП), чтобы помочь вам –

Обзор терминологии ООП

  • Класс – определенный пользователем прототип для объекта, который определяет набор атрибутов, которые характеризуют любой объект класса. Атрибутами являются члены данных (переменные класса и переменные экземпляра) и методы, доступ к которым осуществляется через точечную запись.

  • Переменная класса – переменная, которая используется всеми экземплярами класса. Переменные класса определены внутри класса, но вне любого из методов класса. Переменные класса используются не так часто, как переменные экземпляра.

  • Член данных – переменная класса или переменная экземпляра, которая содержит данные, связанные с классом и его объектами.

  • Перегрузка функций – назначение более чем одного поведения определенной функции. Выполняемая операция варьируется в зависимости от типов объектов или аргументов.

  • Переменная экземпляра – переменная, которая определена внутри метода и принадлежит только текущему экземпляру класса.

  • Наследование – передача характеристик класса другим классам, которые являются его производными.

  • Экземпляр – индивидуальный объект определенного класса. Например, объект obj, принадлежащий классу Circle, является экземпляром класса Circle.

  • Instantiation – создание экземпляра класса.

  • Метод – особый вид функции, который определен в определении класса.

  • Объект – уникальный экземпляр структуры данных, который определяется ее классом. Объект включает в себя как члены данных (переменные класса и переменные экземпляра), так и методы.

  • Перегрузка оператора – назначение более чем одной функции определенному оператору.

Класс – определенный пользователем прототип для объекта, который определяет набор атрибутов, которые характеризуют любой объект класса. Атрибутами являются члены данных (переменные класса и переменные экземпляра) и методы, доступ к которым осуществляется через точечную запись.

Переменная класса – переменная, которая используется всеми экземплярами класса. Переменные класса определены внутри класса, но вне любого из методов класса. Переменные класса используются не так часто, как переменные экземпляра.

Член данных – переменная класса или переменная экземпляра, которая содержит данные, связанные с классом и его объектами.

Перегрузка функций – назначение более чем одного поведения определенной функции. Выполняемая операция варьируется в зависимости от типов объектов или аргументов.

Переменная экземпляра – переменная, которая определена внутри метода и принадлежит только текущему экземпляру класса.

Наследование – передача характеристик класса другим классам, которые являются его производными.

Экземпляр – индивидуальный объект определенного класса. Например, объект obj, принадлежащий классу Circle, является экземпляром класса Circle.

Instantiation – создание экземпляра класса.

Метод – особый вид функции, который определен в определении класса.

Объект – уникальный экземпляр структуры данных, который определяется ее классом. Объект включает в себя как члены данных (переменные класса и переменные экземпляра), так и методы.

Перегрузка оператора – назначение более чем одной функции определенному оператору.

Создание классов

Оператор класса создает новое определение класса. Имя класса следует сразу за ключевым словом class, за которым следует двоеточие:

class ClassName:
   'Optional class documentation string'
   class_suite
  • Класс имеет строку документации, к которой можно получить доступ через ClassName .__ doc__ .

  • 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 – это переменная класса, значение которой является общим для всех экземпляров a в этом классе. Доступ к нему можно получить как Employee.empCount внутри класса или за его пределами.

  • Первый метод __init __ () – это специальный метод, который называется конструктором класса или методом инициализации, который Python вызывает при создании нового экземпляра этого класса.

  • Вы объявляете другие методы класса, как обычные функции, за исключением того, что первый аргумент каждого метода – это self . Python добавляет аргумент self в список для вас; вам не нужно включать его при вызове методов.

Переменная empCount – это переменная класса, значение которой является общим для всех экземпляров a в этом классе. Доступ к нему можно получить как Employee.empCount внутри класса или за его пределами.

Первый метод __init __ () – это специальный метод, который называется конструктором класса или методом инициализации, который Python вызывает при создании нового экземпляра этого класса.

Вы объявляете другие методы класса, как обычные функции, за исключением того, что первый аргумент каждого метода – это self . Python добавляет аргумент self в список для вас; вам не нужно включать его при вызове методов.

Создание объектов экземпляра

Чтобы создать экземпляры класса, вы вызываете класс, используя имя класса, и передаете любые аргументы, которые принимает его метод __init__ .

This would create first object of Employee class
emp1 = Employee("Zara", 2000)
This would create second object of Employee class
emp2 = Employee("Manni", 5000)

Доступ к атрибутам

Вы получаете доступ к атрибутам объекта, используя оператор точки с объектом. Переменная класса будет доступна с использованием имени класса следующим образом:

emp1.displayEmployee()
emp2.displayEmployee()
print ("Total Employee %d" % Employee.empCount)

Теперь, объединяя все концепции –

Live Demo

#!/usr/bin/python3

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)


#This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
#This would create second object of Employee class"
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.salary = 7000  # Add an 'salary' attribute.
emp1.name = 'xyz'  # Modify 'age' attribute.
del emp1.salary  # Delete 'age' attribute.

Вместо использования обычных операторов для доступа к атрибутам, вы можете использовать следующие функции:

  • Getattr (obj, name [, default]) – для доступа к атрибуту объекта.

  • Hasattr (obj, name) – проверить, существует атрибут или нет.

  • Setattr (obj, name, value) – установить атрибут. Если атрибут не существует, он будет создан.

  • Delattr (obj, name) – удалить атрибут.

Getattr (obj, name [, default]) – для доступа к атрибуту объекта.

Hasattr (obj, name) – проверить, существует атрибут или нет.

Setattr (obj, name, value) – установить атрибут. Если атрибут не существует, он будет создан.

Delattr (obj, name) – удалить атрибут.

hasattr(emp1, 'salary')    # Returns true if 'salary' attribute exists
getattr(emp1, 'salary')    # Returns value of 'salary' attribute
setattr(emp1, 'salary', 7000) # Set attribute 'salary' at 7000
delattr(emp1, 'salary')    # Delete attribute 'salary'

Встроенные атрибуты класса

Каждый класс Python поддерживает следующие встроенные атрибуты, и к ним можно получить доступ, используя оператор точки, как и любой другой атрибут –

  • __dict__ – словарь, содержащий пространство имен класса.

  • __doc__ – Строка документации класса или нет, если она не определена.

  • __name__ – Имя класса.

  • __module__ – Имя модуля, в котором определяется класс. Этот атрибут «__main__» в интерактивном режиме.

  • __bases__ – возможно пустой кортеж, содержащий базовые классы, в порядке их появления в списке базовых классов.

__dict__ – словарь, содержащий пространство имен класса.

__doc__ – Строка документации класса или нет, если она не определена.

__name__ – Имя класса.

__module__ – Имя модуля, в котором определяется класс. Этот атрибут «__main__» в интерактивном режиме.

__bases__ – возможно пустой кортеж, содержащий базовые классы, в порядке их появления в списке базовых классов.

Для приведенного выше класса давайте попробуем получить доступ ко всем этим атрибутам –

Live Demo

#!/usr/bin/python3

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)
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__: (<class 'object'>,)
Employee.__dict__: {
   'displayCount': <function Employee.displayCount at 0x0160D2B8>, 
   '__module__': '__main__', '__doc__': 'Common base class for all employees', 
   'empCount': 2, '__init__': 
   <function Employee.__init__ at 0x0124F810>, 'displayEmployee': 
   <function Employee.displayEmployee at 0x0160D300>,
   '__weakref__': 
   <attribute '__weakref__' of 'Employee' objects>, '__dict__': 
   <attribute '__dict__' of 'Employee' objects>
}

Уничтожение объектов (Сборка мусора)

Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. Процесс, посредством которого Python периодически восстанавливает блоки памяти, которые больше не используются, называется сборкой мусора.

Сборщик мусора в Python запускается во время выполнения программы и запускается, когда счетчик ссылок на объект достигает нуля. Количество ссылок объекта изменяется по мере изменения количества псевдонимов, которые на него указывают.

Счетчик ссылок на объект увеличивается, когда ему присваивается новое имя или он помещается в контейнер (список, кортеж или словарь). Количество ссылок объекта уменьшается, когда он удаляется с помощью del , его ссылка переназначается или его ссылка выходит за пределы области видимости. Когда счетчик ссылок объекта достигает нуля, Python собирает его автоматически.

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c = [b]     # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c[0] = -1   # Decrease ref. count  of <40> 

Обычно вы не замечаете, когда сборщик мусора уничтожает потерянный экземпляр и освобождает его пространство. Однако класс может реализовать специальный метод __del __ () , называемый деструктором, который вызывается, когда экземпляр собирается уничтожить. Этот метод может использоваться для очистки любых ресурсов, не связанных с памятью, используемых экземпляром.

пример

Этот деструктор __del __ () печатает имя класса экземпляра, который должен быть уничтожен –

Live Demo

#!/usr/bin/python3

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))   # prints the ids of the obejcts
del pt1
del pt2
del pt3

Когда приведенный выше код выполняется, он дает следующий результат –

140338326963984 140338326963984 140338326963984
Point destroyed

Примечание. В идеале вы должны определить свои классы в отдельном файле, а затем импортировать их в основной файл программы с помощью оператора import .

В приведенном выше примере предполагается, что определение класса Point содержится в point.py, и в нем нет другого исполняемого кода.

#!/usr/bin/python3
import point

p1 = point.Point()

Наследование классов

Вместо того, чтобы начинать с нуля, вы можете создать класс, выведя его из ранее существовавшего класса, перечислив родительский класс в скобках после имени нового класса.

Дочерний класс наследует атрибуты своего родительского класса, и вы можете использовать эти атрибуты, как если бы они были определены в дочернем классе. Дочерний класс также может переопределять элементы данных и методы родительского класса.

Синтаксис

Производные классы объявляются так же, как их родительский класс; однако список базовых классов для наследования дается после имени класса –

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

пример

Live Demo

#!/usr/bin/python3

class Parent:        # define parent class
   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): # define child class
   def __init__(self):
      print ("Calling child constructor")

   def childMethod(self):
      print ('Calling child method')

c = Child()          # instance of child
c.childMethod()      # child calls its method
c.parentMethod()     # calls parent's method
c.setAttr(200)       # again call parent's method
c.getAttr()          # again call parent's method

Когда приведенный выше код выполняется, он дает следующий результат –

Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200

Аналогичным образом вы можете управлять классом из нескольких родительских классов следующим образом:

class A:        # define your class A
.....

class B:         # define your calss B
.....

class C(A, B):   # subclass of A and B
.....

Вы можете использовать функции issubclass () или isinstance (), чтобы проверить отношения двух классов и экземпляров.

  • Булева функция issubclass (sub, sup) возвращает True, если данный подкласс sub действительно является подклассом суперкласса sup .

  • Булева функция isinstance (obj, Class) возвращает True, если obj является экземпляром класса Class или экземпляром подкласса Class

Булева функция issubclass (sub, sup) возвращает True, если данный подкласс sub действительно является подклассом суперкласса sup .

Булева функция isinstance (obj, Class) возвращает True, если obj является экземпляром класса Class или экземпляром подкласса Class

Переопределяющие методы

Вы всегда можете переопределить ваши родительские методы класса. Одна из причин переопределения родительских методов заключается в том, что вам может потребоваться особая или другая функциональность в вашем подклассе.

пример

Live Demo

#!/usr/bin/python3

class Parent:        # define parent class
   def myMethod(self):
      print ('Calling parent method')

class Child(Parent): # define child class
   def myMethod(self):
      print ('Calling child method')

c = Child()          # instance of child
c.myMethod()         # child calls overridden method

Когда приведенный выше код выполняется, он дает следующий результат –

Calling child method

Базовые методы перегрузки

В следующей таблице перечислены некоторые общие функции, которые вы можете переопределить в своих собственных классах.

Sr.No. Метод, описание и пример вызова
1

__init__ (self [, args …])

Конструктор (с любыми необязательными аргументами)

Пример вызова: obj = className (args)

2

__del __ (самостоятельно)

Деструктор, удаляет объект

Образец звонка: del obj

3

__repr __ (самостоятельно)

Оцениваемое строковое представление

Пример вызова: repr (obj)

4

__str __ (самостоятельно)

Печатное представление строки

Пример вызова: str (obj)

5

__cmp__ (self, x)

Сравнение объектов

Пример вызова: cmp (obj, x)

__init__ (self [, args …])

Конструктор (с любыми необязательными аргументами)

Пример вызова: obj = className (args)

__del __ (самостоятельно)

Деструктор, удаляет объект

Образец звонка: del obj

__repr __ (самостоятельно)

Оцениваемое строковое представление

Пример вызова: repr (obj)

__str __ (самостоятельно)

Печатное представление строки

Пример вызова: str (obj)

__cmp__ (self, x)

Сравнение объектов

Пример вызова: cmp (obj, x)

Операторы перегрузки

Предположим, вы создали класс Vector для представления двумерных векторов. Что происходит, когда вы используете оператор плюс, чтобы добавить их? Скорее всего, Python будет кричать на вас.

Однако вы можете определить метод __add__ в вашем классе для выполнения сложения векторов, и тогда оператор плюс будет вести себя так, как ожидалось:

пример

Live Demo

#!/usr/bin/python3

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)

Скрытие данных

Атрибуты объекта могут или не могут быть видны вне определения класса. Вам нужно присвоить атрибутам имена с двойным префиксом подчеркивания, и тогда эти атрибуты не будут напрямую видны посторонним.

пример

Live Demo

#!/usr/bin/python3

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 <module>
      print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

Python защищает этих членов, внутренне изменяя имя, чтобы включить имя класса. Вы можете получить доступ к таким атрибутам как object._className__attrName . Если вы заменили свою последнюю строку следующим образом, то она работает для вас –

.........................
print (counter._JustCounter__secretCount)

Когда приведенный выше код выполняется, он дает следующий результат –