Объектно-ориентированное программирование на Python

Объектно-ориентированное программирование на 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

 

  • share:
Комментарии 0

Оставить комментарий


Комментарии могут оставлять только зарегистрированные пользователи

shape shape