Объектно-ориентированное программирование в Python 3
Объективно-ориентирное программирование –это парадигма программирования, подразумевающая моделирования разных компьютерных программ на основе реальных объектов. Объектом называют что-либо, у чего есть характеристики или то, что выполняет определенную функцию.
Например, объектом может выступать человек, свойства которого «возраст», «имя» и методы (поведение) – «дыхание», «разговор», «ходьба» и «бег». Даже электронное письмо описывается свойствами «тема», «список получателей» и «сообщение», а также методами «добавить вложения» и «отправить». Другими словами, ООП – это подход для моделирования вещей и отношений между этими вещами.
Вторая распространенная парадигма – процедурное программирование, структурирующее программу словно рецепт. Эта программа состоит из набора шагов в виде блоков и функций кода, которые поэтапно выполняются для завершения поставленной задачи.
Плюсы и минусы ООП Python
Приведем некоторые главные преимущества ООП, о которых должен знать каждый:
- ООП подразумевает повторное использование. Программа, написанная в форме классов и объектов может применяться повторно в других проектах без необходимости повторения кода.
- Применение модулярного подхода в ООП позволяет получить гибкий и читаемый код.
- Каждый класс в объектно-ориентировочном программировании отвечает за определенную задачу. Если ошибка произошла в одной части кода, то ее можно исправить локально, не затрагивая остальные части кода.
- Инкапсуляция данных вносит дополнительную безопасность в создаваемую программу с применением ООП.
Несмотря на все преимущества объективно-ориентированного программирования, важно знать и о его недостатках, которые мы представили в виде списка:
- При создании объектов невозможно обойтись без детального представления о создаваемом программном обеспечении.
- Не все аспекты программного обеспечения можно считать подходящим решением для реализации в качестве объекта. Начинающим может быть сложно прочертить линию в золотой середине.
- Сложность и размер программы будет расти одновременно с внесением новых классов вход.
Как вы уже поняли, объектно-ориентированное программирование строится на объектах. Но, перед тем как приступить к созданию объекта, важно научиться определять его класс.
Основные термины ООП
- Класс – прототип для объекта, определенный пользователем, который определяет набор атрибутов, характеризующих любой объект класса. Атрибутами выступают члены данных (переменные класса и экземпляра) и методы, доступ к которым происходит через точечную запись.
- Член данных – переменная экземпляра или переменная класса, содержащая данные, которые связаны с классом, а также его объектами.
- Переменная класса – переменная, использующаяся всеми экземплярами класса. Они используются не настолько часто, как переменные экземпляра.
- Перегрузка функций – назначение больше чем одного поведения конкретной функции. При этом выполняемая операция зависит от типов аргументов или объектов.
- Экземпляр – индивидуальный объект, относящийся к определенному классу. К примеру, объект obj принадлежит классу Circle и выступает экземпляром класса Circle.
- Переменная экземпляра – переменная, определенная внутри метода и относится только к текущему экземпляру класса.
- Перезагрузка оператора – назначение определенному оператору больше чем одной функции.
- Instantiation – это создание экземпляра класса.
- Объект – уникальный вид функции, определен в определении класса.
- Объект – особый экземпляр структуры данных, определяющийся его классом. Объект содержит, как члены данных (переменные класса и экземпляра) и методы.
Создание классов Python
Оператор класса создает новое определение класса. Сразу за ключевым словом class следует имя класса и двоеточие:
classClassName:
‘Optionalclassdocumentationstring’
class_suite
- Класс имеет строку документации. Получить доступ к ней можно через ClassName .__ doc__ .
- Class_suite включает все компоненты утверждений, которые определяют член класса, атрибуты данных и функцию.
Приведем пример простого класса:
classEmployee:
‘Commonbaseclassforallemployees’
empCount = 0
def__init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
defdisplayCount(self):
print»TotalEmployee %d» % Employee.empCount
defdisplayEmployee(self):
print»Name : «, self.name, «, Salary: «, self.salary
Переменная empCount – переменная класса, значение которой общее для всех экземпляров данного класса. К нему можно получить доступ как Employee.empCount внутри класса, а также за его пределами.
Первый метод __init __ () представляет собой специальный метод, который называют методом инициализации или конструктором класса, который Питон вызывает во время создания нового экземпляра данного класса.
Другие методы класса вы объявляете, как обычные функциями, кроме того, что self является первым аргументом каждого метода. Для вас Python добавляет этот аргумент, чтобы вам не пришлось его включать при вызове методов.
Создание объектов экземпляра
Для того чтобы создать экземпляры класса, нужно вызвать класс, применяя его имя, и передать любые аргументы, принимающие его методом __init__ .
«ThiswouldcreatefirstobjectofEmployeeclass»
emp1 = Employee(«Zara», 2000)
«ThiswouldcreatesecondobjectofEmployeeclass»
emp2 = Employee(«Manni», 5000)
Доступ к атрибутам
Чтобы получить доступ к атрибутам объекта, нужно использовать оператор точки с объектом. Переменная класса станет доступной с использованием имени класса:
emp1.displayEmployee()
emp2.displayEmployee()
print»TotalEmployee %d» % Employee.empCount
Теперь, соединяя все концепции
#!/usr/bin/python
classEmployee:
‘Commonbaseclassforallemployees’
empCount = 0
def__init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
defdisplayCount(self):
print»TotalEmployee %d» % Employee.empCount
defdisplayEmployee(self):
print»Name : «, self.name, «, Salary: «, self.salary
«ThiswouldcreatefirstobjectofEmployeeclass»
emp1 = Employee(«Zara», 2000)
«ThiswouldcreatesecondobjectofEmployeeclass»
emp2 = Employee(«Manni», 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print»TotalEmployee %d» % Employee.empCount
Результат приведенного выше кода:
Name :Zara ,Salary: 2000
Name :Manni ,Salary: 5000
TotalEmployee2
Можно удалять, изменять или добавлять атрибуты объектов и классов когда угодно:
emp1.age = 7# Addan ‘age’ attribute.
emp1.age = 8# Modify ‘age’ attribute.
del emp1.age # Delete ‘age’ attribute.
Альтернативные функции, которые можно использовать вместо обычных операторов для доступа к атрибутам:
Для удаления атрибута — Delattr (объект, имя).
Для доступа к атрибуту объекта — GetAttr (объект, имя [, по умолчанию]).
Проверить, существует атрибут или нет — Hasattr (объект, имя).
Установить атрибут — • SetAttr (объект, имя, значение). Если атрибута нет, то он будет создан.
hasattr(emp1, ‘age’)# Returnstrueif ‘age’ attributeexists
getattr(emp1, ‘age’)# Returnsvalueof ‘age’ attribute
setattr(emp1, ‘age’, 8) # Setattribute ‘age’ at 8
delattr(empl, ‘age’)# Deleteattribute ‘age’
Встроенные атрибуты класса
Классы Python поддерживают такие встроенные атрибуты (получить доступ к ним можно, используя оператор точки, так собственно и любой другой атрибут):
- Имя класса — __name__ .
- Словарь, которыйсодержитпространствоименикласса — __dict__ .
- Строка документации класса или нет, в том случае если она не определена -__doc__.
- Имя модуля, в котором определяется класс __module__ . В интерактивном режиме этот атрибут «__main__».
- Пустой кортеж, включающий базовые классы, в порядке их появления в списке базовых классов — __bases__.
Давайте попробуем получить доступ к данным атрибутам для приведенного выше класса:
#!/usr/bin/python
classEmployee:
‘Commonbaseclassforallemployees’
empCount = 0
def__init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
defdisplayCount(self):
print»TotalEmployee %d» % Employee.empCount
defdisplayEmployee(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__: Commonbaseclassforallemployees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {‘__module__’: ‘__main__’, ‘displayCount’:
<functiondisplayCountat0xb7c84994>, ’empCount’: 2,
‘displayEmployee’: <functiondisplayEmployeeat0xb7c8441c>,
‘__doc__’: ‘Commonbaseclassforallemployees’,
‘__init__’: <function__init__at0xb7c846bc>}
Уничтожение объектов или сборка мусора
Чтобы освободить пространство, Питон удаляет ненужные объекты (экземпляры классов или встроенные типы) в автоматическом режиме. Процесс, посредством которого Питон периодически восстанавливает не использующиеся ранее блоки памяти, называется сборкой мусора.
Во время выполнения программы запускается сборщик мусора в Питон, когда счетчик ссылок на объект равен нулю. По мере изменения количества псевдонимов, указывающих на него, изменяется количество ссылок объекта.
Когда счетчик ссылок на объект помещается в контейнер (кортеж, словарь или список) или ему присваивается новое имя, он увеличивается. А уменьшается он тогда, когда удаляется с помощью del. Его ссылка выходит за пределы области видимости или предназначается. Когда счетчик достигает нуля, Питон собирает его автоматически.
a = 40# Createobject<40>
b = a # Increaseref. countof<40>
c = [b]# Increaseref. countof<40>
del a # Decreaseref. countof<40>
b = 100# Decreaseref. countof<40>
c[0] = -1# Decreaseref. countof<40>
Сборщик мусора уничтожает потерянный экземпляр и освобождает его пространством незаметно. Однако класс может реализовать метод __del __ () (деструктор). Он вызывается тогда, когда экземпляр собирается быть уничтоженным. Данный метод может применяться для очистки всех ресурсов памяти, которые используются экземпляром.
Пример
Данный деструктор __del __ () печатает имя класса экземплятора, идущего на уничтожение:
#!/usr/bin/python
classPoint:
def__init__(self, x=0, y=0):
self.x = x
self.y = y
def__del__(self):
class_name = self.__class__.__name__
printclass_name, «destroyed»
pt1 = Point()
pt2 = pt1
pt3 = pt1
printid(pt1), id(pt2), id(pt3) # printstheidsoftheobejcts
del pt1
del pt2
del pt3
Результат приведенного выше кода:
3083401324 30834013243083401324
Pointdestroyed
Внимание! В идеале нужно определять классы в отдельном файле, после чего перенести их в основной, воспользовавшись оператором import .
Наследование классов
Вы можете создать класс, выведя его уже из существующего, а не создавать с нуля. Для этого после имени нового класса нужно перечислить родительский класс в скобках. В таком случае, дочерний класс наследует атрибуты родительского класса, и вы можете применять их, если бы они были определены в дочернем классе. Также дочерний класс способен переопределять методы и элементы данных родительского класса.
Синтаксис
Производные классы объявляются так, как и их родительский класс, но список базовых классов для наследования дается после имени класса.
classSubClassName(ParentClass1[, ParentClass2, …]):
‘Optionalclassdocumentationstring’
class_suite
Пример
#!/usr/bin/python
classParent: # defineparentclass
parentAttr = 100
def__init__(self):
print»Callingparentconstructor»
defparentMethod(self):
print’Callingparentmethod’
defsetAttr(self, attr):
Parent.parentAttr = attr
defgetAttr(self):
print»Parentattribute :», Parent.parentAttr
classChild(Parent): # definechildclass
def__init__(self):
print»Callingchildconstructor»
defchildMethod(self):
print’Callingchildmethod’
c = Child()# instanceofchild
c.childMethod()# childcallsitsmethod
c.parentMethod()# callsparent’smethod
c.setAttr(200)# againcallparent’smethod
c.getAttr()# againcallparent’smethod
Результат приведенного выше кода:
Callingchildconstructor
Callingchildmethod
Callingparentmethod
Parentattribute :200
Точно так же можно управлять классом, состоящим из нескольких родительских классов:
class A: # defineyourclass A
…..
class B: # defineyourclass B
…..
classC(A, B): # subclassof A and B
…..
Чтобы проверить отношение классов и экземпляров можно воспользоваться функциями issubclass () или isinstance ().
Isinstance (объект, класс). Функция булева возвращает истину, если OBJ — экземпляр класса Class или выступает экземпляром подкласса класса.
Issubclass ( к югу, вир). Функция булева возвращает истину, если этот подкласс суб является подклассом суперкласса вир.
Переопределяющие методы
Всегда можно переопределить родительские методы класса. Одна из причин этого состоит в том, что вам может понадобится особая или иная функциональность в подклассе.
Пример
#!/usr/bin/python
classParent: # defineparentclass
defmyMethod(self):
print’Callingparentmethod’
classChild(Parent): # definechildclass
defmyMethod(self):
print’Callingchildmethod’
c = Child()# instanceofchild
c.myMethod()# childcallsoverriddenmethod
Результат приведенного выше кода:
Callingchildmethod
Базовые методы перегрузки
Для наглядности мы приведем некоторые общие функции, которые можно переопределить в собственных классах в виде таблицы:
Sr.No. | Метод, описание и пример вызова |
1 | __init__ (self [, args …]) Конструктор (с любыми необязательными аргументами) Образец вызова: obj = className (args) |
2 | __del __ (самостоятельно) Деструктор, удаляет объект Пример звонка: delobj |
3 | __repr __ (самостоятельно) Оцениваемое строковое представление Пример вызова: repr (obj) |
4 | __str __ (самостоятельно) Печатное представление строки Образец вызова: str (obj) |
5 | __cmp__ (self, x) Сравнение объектов Образец вызова: cmp (obj, x) |
Операторы перегрузки
Представьте, что вы создали класс Vector для двумерных векторов. Каким будет результат, если добавить оператор «плюс»? Вероятней всего, Питон будет «ругаться». Но решение есть – определите метод метод __add__ в вашем классе для сложения векторов. В таком случае, будет ожидаемый результат:
пример
#!/usr/bin/python
classVector:
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):
returnVector(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
classJustCounter:
__secretCount = 0
defcount(self):
self.__secretCount += 1
printself.__secretCount
counter = JustCounter()
counter.count()
counter.count()
printcounter.__secretCount
Результат выше приведенного кода:
1
2
Traceback(mostrecentcalllast):
File»test.py», line12, in<module>
printcounter.__secretCount
AttributeError: JustCounterinstancehasnoattribute’__secretCount’
Чтобы выключить имя класса, Питон защищает данных членов, внутреннее изменяя имя. Можно получить доступ к атрибутам object._className__attrName. Если вы замените последнюю строку, то она будет работать следующим образом:
…………………….
printcounter._JustCounter__secretCount
Результат приведенного выше кода:
1
2
2
Сквозной пример по классам
####################################################
## 5. Классы
####################################################
# Чтобы получить класс, мы наследуемся от object.
classHuman(object):
# Атрибут класса. Он разделяется всеми экземплярами данного класса
species = «H. sapiens»
# Обычный конструктор, вызывается при инициализации экземпляра класса
# Учтите, что двойное подчёркивание в начале и в конце имени
# означает объекты и атрибуты, которые применяютсяPython, но находятся
# в пространствах имён, управляемых пользователем.
# Не придумывайте им имена сами.
def__init__(self, name):
# Присваивание значения аргумента атрибуту класса name
self.name = name
# Метод экземпляра. Все методы принимают self в качестве первого аргумента
defsay(self, msg):
return»{name}: {message}».format(name=self.name, message=msg)
# Метод класса разделяется между всеми экземплярами
# Они вызываются с указыванием вызывающего класса в качестве первого аргумента
@classmethod
defget_species(cls):
returncls.species
# Статический метод вызывается без ссылки на класс или экземпляр
@staticmethod
defgrunt():
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*»
Насколько сложным для вас оказалось объектно-ориентированное программирование в Python 3? Поделитесь опытом в комментариях.