
Объектно-ориентированное программирование на Python
Python — фантастический язык программирования, который позволяет использовать как функциональные, так и объектно-ориентированные парадигмы программирования.
Программисты на Python должны иметь возможность использовать фундаментальные концепции объектно-ориентированного программирования, будь то разработчики программного обеспечения, инженеры по машинному обучению или кто-то еще.
Все четыре основных аспекта общей структуры ООП поддерживаются системой объектно-ориентированного программирования Python: инкапсуляция, абстракция, наследование и полиморфизм.
В этом уроке мы кратко рассмотрим эти функции и попрактикуемся с ними.
Концепции объектно-ориентированного программирования в Python
Что такое классы и объекты?
Python, как и любой другой объектно-ориентированный язык, позволяет вам определять классы для создания объектов. Встроенные классы Python — это наиболее распространенные типы данных в Python, такие как строки, списки, словари и т. д.
Класс — это набор переменных экземпляра и связанных методов, определяющих конкретный тип объекта. Вы можете думать о классе как о чертеже или шаблоне объекта. Атрибуты — это имена, данные переменным, которые составляют класс.
Экземпляр класса с определенным набором свойств называется объектом. В результате один и тот же класс можно использовать для создания любого количества объектов.
Давайте определим класс с именем Book для программы продаж книг.
class Book:
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.price = price
Специальный метод __init__, также известный как конструктор, используется для инициализации класса Book с такими атрибутами, как название, количество, автор и цена.
В Python встроенные классы именуются строчными буквами, а пользовательские классы именуются в Camel case или Snake case с заглавной первой буквой.
Этот класс может быть создан для любого количества объектов. В следующем примере кода создаются экземпляры трех книг:
book1 = Book('Book 1', 12, 'Author 1', 120)
book2 = Book('Book 2', 18, 'Author 2', 220)
book3 = Book('Book 3', 28, 'Author 3', 320)
book1, book2 и book3 — разные объекты класса Book. Термин self в атрибутах относится к соответствующим экземплярам (объектам).
print(book1)
print(book2)
print(book3)
Выход:
<__main__.Book object at 0x00000156EE59A9D0>
<__main__.Book object at 0x00000156EE59A8B0>
<__main__.Book object at 0x00000156EE59ADF0>
Класс и место в памяти объектов печатаются при их печати (вызове функции print). Мы не можем ожидать, что они предоставят конкретную информацию о свойствах объекта, таких как название, имя автора и так далее.
Но для этого мы можем использовать специальный метод __repr__.
В Python специальный метод — это определенная функция, которая начинается и заканчивается двумя знаками подчеркивания и вызывается автоматически при выполнении определенных условий.
class Book:
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.price = price
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.price}"
book1 = Book('Book 1', 12, 'Author 1', 120)
book2 = Book('Book 2', 18, 'Author 2', 220)
book3 = Book('Book 3', 28, 'Author 3', 320)
print(book1)
print(book2)
print(book3)
Выход:
Book: Book 1, Quantity: 12, Author: Author 1, Price: 120
Book: Book 2, Quantity: 18, Author: Author 2, Price: 220
Book: Book 3, Quantity: 28, Author: Author 3, Price: 320
Что такое инкапсуляция?
Инкапсуляция — это процесс предотвращения доступа клиентов к определенным свойствам, доступ к которым можно получить только с помощью определенных методов.
Приватные атрибуты — это недоступные атрибуты, а сокрытие информации — это процесс превращения определенных атрибутов в приватные. Вы используете два символа подчеркивания для объявления частных характеристик.
Давайте введем закрытый атрибут с именем __discount в классе Book.
class Book:
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.price = price
self.__discount = 0.10
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.price}"
book1 = Book('Book 1', 12, 'Author 1', 120)
print(book1.title)
print(book1.quantity)
print(book1.author)
print(book1.price)
print(book1.__discount)
Book 1
12
Author 1
120
Traceback (most recent call last):
File "C:\Users\ashut\Desktop\Test\hello\test.py", line 19, in
print(book1.__discount)
AttributeError: 'Book' object has no attribute '__discount'
Мы видим, что печатаются все атрибуты, кроме приватного атрибута __discount. Для получения доступа к таким свойствам обычно создаются дополнительные методы
В следующем примере кода мы сделаем свойство price приватным и создадим дополнительный метод для установки для присвоения скидки и функцию получения для получения цены.
class Book:
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.__price = price
self.__discount = None
def set_discount(self, discount):
self.__discount = discount
def get_price(self):
if self.__discount:
return self.__price * (1-self.__discount)
return self.__price
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
На этот раз мы создадим два объекта: один для покупки одной книги, а другой для покупки книг в большом количестве. При покупке книг оптом мы получаем скидку 20%, поэтому в этом случае мы будем использовать метод set_discount(), чтобы установить скидку 20%.
single_book = Book('Two States', 1, 'Chetan Bhagat', 200)
bulk_books = Book('Two States', 25, 'Chetan Bhagat', 200)
bulk_books.set_discount(0.20)
print(single_book.get_price())
print(bulk_books.get_price())
print(single_book)
print(bulk_books)
Выход:
200
160.0
Book: Two States, Quantity: 1, Author: Chetan Bhagat, Price: 200
Book: Two States, Quantity: 25, Author: Chetan Bhagat, Price: 160.0
Что такое наследование в Python?
Наследование считается наиболее важной характеристикой ООП. Способность класса наследовать методы и/или характеристики от другого класса называется наследованием.
Подкласс или дочерний класс — это класс, который наследуется. Суперкласс или родительский класс — это класс, от которого наследуются методы и/или атрибуты.
В программное обеспечение для продаж нашего книготорговца добавлены два новых класса: Novel класс и Academic класс.
Мы видим, что независимо от того, классифицируется ли книга как новела или академическая, она может иметь некоторые схожие атрибуты, такие как название и автор, а также общие методы, такие как get_price() и set_discount(). Переписывать весь этот код для каждого нового класса — пустая трата времени, усилий и памяти.
class Book:
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.__price = price
self.__discount = None
def set_discount(self, discount):
self.__discount = discount
def get_price(self):
if self.__discount:
return self.__price * (1-self.__discount)
return self.__price
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
class Novel(Book):
def __init__(self, title, quantity, author, price, pages):
super().__init__(title, quantity, author, price)
self.pages = pages
class Academic(Book):
def __init__(self, title, quantity, author, price, branch):
super().__init__(title, quantity, author, price)
self.branch = branch
Давайте создадим объекты для этих классов, чтобы визуализировать их.
novel1 = Novel('Two States', 20, 'Chetan Bhagat', 200, 187)
novel1.set_discount(0.20)
academic1 = Academic('Python Foundations', 12, 'PSF', 655, 'IT')
print(novel1)
print(academic1)
Вывод:
Book: Two States, Quantity: 20, Author: Chetan Bhagat, Price: 160.0
Book: Python Foundations, Quantity: 12, Author: PSF, Price: 655
Что такое Полиморфизм?
Термин «полиморфизм» происходит из греческого языка и означает «нечто, что принимает несколько форм».
Полиморфизм относится к способности подкласса адаптировать метод, который уже существует в его суперклассе, для удовлетворения своих потребностей. Другими словами, подкласс может использовать метод своего суперкласса как есть или модифицировать его по мере необходимости.
class Academic(Book):
def __init__(self, title, quantity, author, price, branch):
super().__init__(title, quantity, author, price)
self.branch = branch
def __repr__(self):
return f"Book: {self.title}, Branch: {self.branch}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
Суперкласс Book имеет специальный метод, который называется __repr__. Этот метод может использоваться подклассом Novel, чтобы он вызывался всякий раз, когда объект печатается.
С другой стороны, подкласс Academic определяется собственной специальной метод __repr__ в приведенном выше примере кода. Подкласс Academic будет вызывать свой собственный метод, подавляя тот же метод, что и в его суперклассе, благодаря полиморфизму.
novel1 = Novel('Two States', 20, 'Chetan Bhagat', 200, 187)
novel1.set_discount(0.20)
academic1 = Academic('Python Foundations', 12, 'PSF', 655, 'IT')
print(novel1)
print(academic1)
Вывод:
Book: Two States, Quantity: 20, Author: Chetan Bhagat, Price: 160.0
Book: Python Foundations, Branch: IT, Quantity: 12, Author: PSF, Price: 655
Что такое Абстракция?
Абстракция не поддерживается напрямую в Python. С другой стороны, вызов магического метода допускает абстракцию.
Если абстрактный метод объявлен в суперклассе, подклассы, наследуемые от суперкласса, должны иметь собственную реализацию этого метода.
Абстрактный метод суперкласса никогда не будет вызываться его подклассами. Но абстракция помогает поддерживать одинаковую структуру во всех подклассах.
В нашем родительском классе Book мы определили метод __repr__. Давайте сделаем этот метод абстрактным, заставив каждый подкласс обязательно иметь свой собственный метод __repr__.
from abc import ABC, abstractmethod
class Book(ABC):
def __init__(self, title, quantity, author, price):
self.title = title
self.quantity = quantity
self.author = author
self.__price = price
self.__discount = None
def set_discount(self, discount):
self.__discount = discount
def get_price(self):
if self.__discount:
return self.__price * (1-self.__discount)
return self.__price
@abstractmethod
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
class Novel(Book):
def __init__(self, title, quantity, author, price, pages):
super().__init__(title, quantity, author, price)
self.pages = pages
class Academic(Book):
def __init__(self, title, quantity, author, price, branch):
super().__init__(title, quantity, author, price)
self.branch = branch
def __repr__(self):
return f"Book: {self.title}, Branch: {self.branch}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
novel1 = Novel('Two States', 20, 'Chetan Bhagat', 200, 187)
novel1.set_discount(0.20)
academic1 = Academic('Python Foundations', 12, 'PSF', 655, 'IT')
print(novel1)
print(academic1)
В приведенном выше коде мы унаследовали класс ABC для создания класса Book. Мы сделали метод __repr__ абстрактным, добавив декоратор @abstractmethod.
При создании класса Novel мы намеренно пропустили реализацию метода __repr__, чтобы посмотреть, что получится:
Traceback (most recent call last):
File "C:\Users\ashut\Desktop\Test\hello\test.py", line 40, in
novel1 = Novel('Two States', 20, 'Chetan Bhagat', 200, 187)
TypeError: Can't instantiate abstract class Novel with abstract method __repr__
Мы получаем TypeError о том, что мы не можем создать экземпляр объекта класса Novel. Давайте добавим реализацию метода __repr__ и посмотрим, что теперь получится.
class Novel(Book):
def __init__(self, title, quantity, author, price, pages):
super().__init__(title, quantity, author, price)
self.pages = pages
def __repr__(self):
return f"Book: {self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()}"
Вывод:
Book: Two States, Quantity: 20, Author: Chetan Bhagat, Price: 160.0
Book: Python Foundations, Branch: IT, Quantity: 12, Author: PSF, Price: 655
Теперь он работает нормально.
Перегрузка метода
Концепция перегрузки методов встречается почти во всех известных языках программирования, которые следуют концепциям объектно-ориентированного программирования. Это просто относится к использованию множества методов с одним и тем же именем, которые принимают различное количество аргументов в пределах одного класса.
Давайте теперь разберемся с перегрузкой методов с помощью следующего кода:
class OverloadingDemo:
def add(self, x, y):
print(x+y)
def add(self, x, y, z):
print(x+y+z)
obj = OverloadingDemo()
obj.add(2, 3)
Вывод:
Traceback (most recent call last):
File "C:\Users\ashut\Desktop\Test\hello\setup.py", line 10, in <module>
obj.add(2, 3)
TypeError: add() missing 1 required positional argument: 'z'
Вам, наверное, интересно, почему это произошло. В результате отображается ошибка, поскольку Python запоминает только самое последнее определение метода add(self, x, y, z) который принимает три параметра в дополнение к self. В результате при вызове метода add() необходимо передать три аргумента. Другими словами, он игнорирует предыдущее определение add().
Таким образом, Python по умолчанию не поддерживает перегрузку методов.
Переопределение метода
Когда метод с тем же именем и аргументами используется как в производном классе, так и в базовом или суперклассе, мы говорим, что метод производного класса «переопределяет» метод, предоставленный в базовом классе.
При вызове переопределенного метода всегда вызывается метод производного класса. Метод, который использовался в базовом классе, теперь скрыт.
class ParentClass:
def call_me(self):
print("I am parent class")
class ChildClass(ParentClass):
def call_me(self):
print("I am child class")
pobj = ParentClass()
pobj.call_me()
cobj = ChildClass()
cobj.call_me()
Вывод:
I am parent class
I am child class
В приведенном выше коде, когда метод call_me() вызывается для дочернего объекта, вызывается call_me() из дочернего класса. Мы можем вызвать метод call_me() родительского класса из дочернего класса, используя super(), например так:
class ParentClass:
def call_me(self):
print("I am parent class")
class ChildClass(ParentClass):
def call_me(self):
print("I am child class")
super().call_me()
pobj = ParentClass()
pobj.call_me()
cobj = ChildClass()
cobj.call_me()
Вывод:
I am parent class
I am child class
I am parent class
Оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи