Классы, методы Python 3. Примеры ООП (объектно ориентированное программирование)

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

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

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

Краткий обзор ооп python

Программа/скрипт/код, написанные с использованием парадигмы объектно-ориентированного программирования, должны состоять из

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

Что такое класс в ооп python?

Класс = данные + методы

Класс — это тип данных, состоящий из набора атрибутов (свойств) и методов — функций для работы с этими атрибутами.

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

Для создания классов предусмотрена инструкция class. Тело класса состоит из блока различных инструкций.

class ИмяКласса:
    ПеременнаяКласса = Значение
    …
    def ИмяМетода(self, ...):
        self.ПеременнаяКласса = Значение
        …
    …

Методы в классах — это те же функции, которые принимают один обязательный параметр — self (с англ. можно перевести как «собственная личность»). Он нужен для связи с конкретным объектом.

Атрибуты класса — это имена переменных вне функций и имена функций. Эти атрибуты наследуются всеми объектами, созданными на основе данного класса. Атрибуты обеспечивают свойства и поведение объекта. Объекты могут иметь атрибуты, которые создаются в теле метода, если данный метод будет вызван для конкретного объекта.

Пример класса (ООП) на Python 3:

class Second:
    color = "red"
    form = "circle"
    def changecolor(self,newcolor):
        self.color = newcolor
    def changeform(self,newform):
        self.form = newform

obj1 = Second()
obj2 = Second()

print (obj1.color, obj1.form) # вывод на экран "red circle"
print (obj2.color, obj2.form) # вывод на экран "red circle"

obj1.changecolor("green") # изменение цвета первого объекта
obj2.changecolor("blue") # изменение цвет второго объекта
obj2.changeform("oval") # изменение формы второго объекта

print (obj1.color, obj1.form) # вывод на экран "green circle"
print (obj2.color, obj2.form) # вывод на экран "blue oval"

Результат выполнения скрипта Python 3:

red circle
red circle
green circle
blue oval

Конструктор класса — метод __init__

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

Такой метод называется конструктором класса и в языке программирования Python носит имя __init__. (В начале и конце по два знака подчеркивания.)

Первым параметром, как и у любого другого метода, у __init__ является self, на место которого подставляется объект в момент его создания. Второй и последующие (если есть) параметры заменяются аргументами, переданными в конструктор при вызове класса. Рассмотрим два класса: в одном будет использоваться конструктор, а в другом нет. Требуется создать два атрибута объекта.

Рассмотрим два класса: в одном будет использоваться конструктор, а в другом нет. Требуется создать два атрибута объекта.

Пример 1:

class YesInit:
    def __init__(self,one,two):
        self.fname = one
        self.sname = two

obj1 = YesInit("Peter","Ok")
print (obj1.fname, obj1.sname)

Пример 2:

class NoInit:
    def names(self,one,two):
        self.fname = one
        self.sname = two

obj2 = NoInit()
obj2.names("Peter","Ok")

print (obj2.fname, obj2.sname)

Результат выполнения двух скриптов:

Peter Ok

Что значит аргумент self в Python 3 в методе класса

Аргумент self — это ссылка на создаваемый в памяти компьютера объект.

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

Методу необходимо «знать», данные какого объекта ему предстоит обрабатывать. Для этого ему в качестве первого (а иногда и единственного) аргумента передается имя переменной, связанной с объектом (можно сказать, передается сам объект). Чтобы в описании класса указать передаваемый в дальнейшем объект, используется параметр self.

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

Объект.ИмяМетода(…)

Здесь под словом Объект имеется в виду переменная, связанная с ним. Это выражение преобразуется в классе, к которому относится объект, в

ИмяМетода(Объект, …)

Т.е. конкретный объект подставляется вместо параметра self

Объектно-ориентированное программирование Python 3. Классы, объекты, экземпляры, методы

Принципы ООП

Объектно-ориентированный язык работает по следующим принципам:

  • Все данные представляются объектами
  • Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
  • Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
  • Каждый объект имеет тип
  • Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)

Идеи/принципы объектно-ориентированного программирования:

  1. Наследование. Возможность выделять общие свойства и методы классов в один класс верхнего уровня (родительский). Классы, имеющие общего родителя, различаются между собой за счет включения в них различных дополнительных свойств и методов.
  2. Инкапсуляция. Свойства и методы класса делятся на доступные из вне (опубликованные) и недоступные (защищенные). Защищенные атрибуты нельзя изменить, находясь вне класса. Опубликованные же атрибуты также называют интерфейсом объекта, т. к. с их помощью с объектом можно взаимодействовать. По идеи, инкапсуляция призвана обеспечить надежность программы, т.к. изменить существенные для существования объекта атрибуты становится невозможно.
  3. Полиморфизм. Полиморфизм подразумевает замещение атрибутов, описанных ранее в других классах: имя атрибута остается прежним, а реализация уже другой. Полиморфизм позволяет специализировать (адаптировать) классы, оставляя при этом единый интерфейс взаимодействия.

Преимущества ООП

В связи со своими особенностями объектно-ориентированное программирование имеет ряд преимуществ перед структурным (и др.) программированием. Выделим некоторые из них:

  1. Использование одного и того же программного кода с разными данными. Классы позволяют создавать множество объектов, каждый из которых имеет собственные значения атрибутов. Нет потребности вводить множество переменных, т.к объекты получают в свое распоряжение индивидуальные так называемые пространства имен. Пространство имен конкретного объекта формируется на основе класса, от которого он был создан, а также от всех родительских классов данного класса. Объект можно представить как некую упаковку данных.
  2. Наследование и полиморфизм позволяют не писать новый код, а настраивать уже существующий, за счет добавления и переопределения атрибутов. Это ведет к сокращению объема исходного кода.

Особенность ООП

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

Особенности ООП в Python

По сравнению с другими распространенными языками программирования у Python можно выделить следующие особенности, связанные с объектно-ориентированным программированием:

  1. Любое данное (значение) — это объект. Число, строка, список, массив и др. — все является объектом. Бываю объекты встроенных классов (как те,что перечисленные в предыдущем предложении), а бывают объекты пользовательских классов (тех, что создает программист). Для единого механизма взаимодействия предусмотрены методы перегрузки операторов.
  2. Класс — это тоже объект с собственным пространством имен. Это нигде не было указано в данном цикле уроков. Однако это так. Поэтому правильнее было употреблять вместо слова «объект», слово «экземпляр». И говорить «экземпляр объекта», подразумевая под этим созданный на основе класса именно объект, и «экземпляр класса», имея ввиду сам класс как объект.
  3. Инкапсуляции в Python не уделяется особого внимания. В других языках программирования обычно нельзя получить напрямую доступ к свойству, описанному в классе. Для его изменения может быть предусмотрен специальный метод. В Python же это легко сделать, просто обратившись к свойству класса из вне. Несмотря на это в Python все-таки предусмотрены специальные способы ограничения доступа к переменным в классе.

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

  • Класс — определенный пользователем прототип для объекта, который определяет набор атрибутов, которые характеризуют любой объект класса. Атрибутами являются члены данных (переменные класса и переменные экземпляра) и методы, доступ к которым осуществляется через точечную запись.
  • Переменная класса — переменная, которая используется всеми экземплярами класса. Переменные класса определены внутри класса, но вне любого из методов класса. Переменные класса используются не так часто, как переменные экземпляра.
  • Член данных — переменная класса или переменная экземпляра, которая содержит данные, связанные с классом и его объектами.
  • Перегрузка функций — назначение более чем одного поведения определенной функции. Выполняемая операция варьируется в зависимости от типов объектов или аргументов.
  • Переменная экземпляра — переменная, которая определена внутри метода и принадлежит только текущему экземпляру класса.
  • Наследование — передача характеристик класса другим классам, которые являются его производными.
  • Экземпляр — индивидуальный объект определенного класса. Например, объект obj, принадлежащий классу Circle, является экземпляром класса Circle.
  • Instantiation — создание экземпляра класса.
  • Метод — особый вид функции, который определен в определении класса.
  • Объект — уникальный экземпляр структуры данных, который определяется его классом. Объект включает в себя как члены данных (переменные класса и переменные экземпляра), так и методы.
  • Перегрузка оператора — назначение более чем одной функции определенному оператору.

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

Оператор класса создает новое определение класса. Имя класса следует сразу за ключевым словом 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__ .

"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

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

#!/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

"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.age = 7  # Add an 'age' attribute.
emp1.age = 8  # Modify 'age' attribute.
del emp1.age  # Delete 'age' attribute.

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

  • GetAttr (объект, имя [, по умолчанию]) — для доступа к атрибуту объекта.
  • Hasattr (объект, имя) — проверить , если атрибут существует или нет.
  • SetAttr (объект, имя, значение) — установить атрибут. Если атрибут не существует, он будет создан.
  • Delattr (объект, имя) — для удаления атрибута.
hasattr(emp1, 'age')    # Returns true if 'age' attribute exists
getattr(emp1, 'age')    # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age')    # Delete attribute 'age'

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

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

  • __dict__ — словарь, содержащий пространство имен класса.
  • __doc__ — Строка документации класса или нет, если она не определена.
  • __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':
<function displayCount at 0xb7c84994>, 'empCount': 2, 
'displayEmployee': <function displayEmployee at 0xb7c8441c>, 
'__doc__': 'Common base class for all employees', 
'__init__': <function __init__ at 0xb7c846bc>}

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

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 __ () печатает имя класса экземпляра, который должен быть уничтожен

#!/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) # prints the ids of the obejcts
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:        # 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 class B
.....

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

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

  • Issubclass ( к югу, вир) функция булева возвращает истину , если данный подкласс суб действительно подкласс суперкласса вир .
  • Isinstance (объект, класс) Функция булева возвращает истину , если OBJ является экземпляром класса Class или является экземпляром подкласса класса

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

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

Пример

#!/usr/bin/python

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)

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

Предположим, что вы создали класс 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 <module>
      print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

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

.........................
print counter._JustCounter__secretCount

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

1
2
2

Сквозной пример по классам

####################################################
## 5. Классы
####################################################

# Чтобы получить класс, мы наследуемся от object.
class Human(object):

    # Атрибут класса. Он разделяется всеми экземплярами этого класса
    species = "H. sapiens"

    # Обычный конструктор, вызывается при инициализации экземпляра класса
    # Обратите внимание, что двойное подчёркивание в начале и в конце имени
    # означает объекты и атрибуты, которые используются Python, но находятся
    # в пространствах имён, управляемых пользователем.
    # Не придумывайте им имена самостоятельно.
    def __init__(self, name):
        # Присваивание значения аргумента атрибуту класса name
        self.name = name

    # Метод экземпляра. Все методы принимают self в качестве первого аргумента
    def say(self, msg):
        return "{name}: {message}".format(name=self.name, message=msg)

    # Метод класса разделяется между всеми экземплярами
    # Они вызываются с указыванием вызывающего класса в качестве первого аргумента
    @classmethod
    def get_species(cls):
        return cls.species

    # Статический метод вызывается без ссылки на класс или экземпляр
    @staticmethod
    def grunt():
        return "*grunt*"


# Инициализация экземпляра класса
i = Human(name="Иван")
print(i.say("привет"))     # Выводит: «Иван: привет»

j = Human("Пётр")
print(j.say("Привет"))  # Выводит: «Пётр: привет»

# Вызов метода класса
i.get_species() #=> "H. sapiens"

# Изменение разделяемого атрибута
Human.species = "H. neanderthalensis"
i.get_species() #=> "H. neanderthalensis"
j.get_species() #=> "H. neanderthalensis"

# Вызов статического метода
Human.grunt() #=> "*grunt*"
4.5 14 голоса
Рейтинг статьи
Шамаев Иван
Разработчик аналитических решений QlikView/Qlik Sense/Power BI; Python Data Scientist; Разработчик интеграционных решений на PHP, Python, JavaScript.
3
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x