Объектно-ориентированное программирование (ООП) — это парадигма программирования, где различные компоненты компьютерной программы моделируются на основе реальных объектов. Объект — это что-либо, у чего есть какие-либо характеристики и то, что может выполнить какую-либо функцию.
Содержание
- Преимущества и недостатки ООП Python
- Класс
- Объекты
- Атрибуты класса
- Атрибуты класса против атрибутов экземпляров
- Методы
- Статичные методы
- Возврат множественных значений из метода
- Метод str
- Конструкторы
- Локальные переменные против глобальных
- Локальные переменные
- Глобальная переменная
- Модификаторы доступа
- Наследование
- Множественное наследование Python
- Полиморфизм
- Перегрузка метода
- Переопределение метода
- Инкапсуляция
- Подведем итоги
Представьте сценарий, где вам нужно разработать болид Формулы-1 используя подход объектно-ориентированного программирования. Первое, что вам нужно сделать — это определить реальные объекты в настоящей гонке Формула-1. Какие аспекты в Формуле-1 обладают определенными характеристиками и могут выполнять ту или иную функцию?
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Один из очевидных ответов на этот вопрос — гоночный болид. Условный болид может обладать такими характеристиками как:
- мощность двигателя;
- марка;
- модель;
- производитель, и т. д.
Соответственно, болид можно запустить, остановить, ускорить, и так далее. Гонщик может быть еще одним объектом в Формуле-1. Гонщик имеет национальность, возраст, пол, и так далее, кроме этого, он обладает таким функционалом, как управление болидом, рулевое управление, переключение передач.
Как и в этом примере, в объектно-ориентированном программировании мы создадим объекты, которые будут соответствовать реальным аспектам.
Стоит обратить внимание на то, что объектно-ориентированное программирование — не зависящая от языка программирования концепция. Это общая концепция программирования и большинство современных языков, такие как Java, C#, C++ и Python поддерживают объектно-ориентированное программирование.
В этой статье мы разберем подробную инструкцию объектно-ориентированного программирования в Python, но перед этим, рассмотрим некоторые преимущества и недостатки объектно-ориентированного программирования.
Преимущества и недостатки ООП Python
Рассмотрим несколько основных преимуществ объектно-ориентированного программирования:
- Объектно-ориентированное программирование подразумевает повторное использование. Компьютерная программа написанная в форме объектов и классов может быть использована снова в других проектах без повторения кода;
- Использование модулярного подхода в объектно-ориентированном программировании позволяет получить читаемый и гибкий код;
- В объектно-ориентированном программировании каждый класс имеет определенную задачу. Если ошибка возникнет в одной части кода, вы можете исправить ее локально, без необходимости вмешиваться в другие части кода;
- Инкапсуляция данных (которую мы рассмотрим дальше в статье) вносит дополнительный уровень безопасности в разрабатываемую программу с использованием объектно-ориентированного подхода;
Хотя объектно-ориентированное программирование обладает рядом преимуществ, оно также содержит определенные недостатки, некоторые из них находятся в списке ниже:
- Для создания объектов необходимо иметь подробное представление о разрабатываемом программном обеспечении;
- Не каждый аспект программного обеспечения является лучшим решением для реализации в качестве объекта. Для новичков может быть тяжело прочертить линию в золотой середине;
- С тем, как вы вносите все новые и новые классы в код, размер и сложность программы растет в геометрической прогрессии;
В следующем разделе мы рассмотрим ряд самых важных концепций объектно-ориентированного программирования.
Как и следует из названия, объектно-ориентированное программирование — это речь об объектах. Однако, перед тем как создать объект, нам нужно определить его класс.
Класс
Класс в объектно-ориентированном программировании выступает в роли чертежа для объекта. Класс можно рассматривать как карту дома. Вы можете понять, как выглядит дом, просто взглянув на его карту.
Cам по себе класс не представляет ничего. К примеру, нельзя сказать что карта является домом, она только объясняет как настоящий дом должен выглядеть.
Отношение между классом и объектом можно представить более наглядно, взглянув на отношение между машиной и Audi. Да, Audi – это машина. Однако, нет такой вещи, как просто машина. Машина — это абстрактная концепция, которую также реализуют в Toyota, Honda, Ferrari, и других компаниях.
Ключевое слово class
используется для создания класса в Python. Название класса следует за ключом class
, за которым следует двоеточие. Тело класса начинается с новой строки, с отступом на одну вкладку влево.
Давайте рассмотрим, как мы можем создать самый простой класс в Python. Взглянем на следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Создаем класс Car class Car: # создаем атрибуты класса name = "c200" make = "mercedez" model = 2008 # создаем методы класса def start(self): print ("Заводим двигатель") def stop(self): print ("Отключаем двигатель") |
В примере выше мы создали класс под названием Car
с тремя атрибутами: имя name
, марка make
и модель model
. Наш класс также содержит два метода: start()
и stop()
.
Объекты
Раннее мы поняли, что класс предоставляет чертеж объекта. Однако, чтобы на самом деле использовать объекты и методы класса, вам нужно создать объект из этого класса. Существует несколько методов и атрибутов класса, которые можно использовать вне объекта, мы рассмотрим их в следующем разделе.
Сейчас просто запомните, что по умолчанию, нам нужно создать объект класса перед тем, как мы сможем начать использовать его методы и атрибуты.
Объект также называется экземпляром. Тем не менее, процесс создания объекта класса называется инициализация. В Python, чтобы создать объект класса, нам просто нужно вписать название класса, с последующими открывающимися и закрывающимися скобками.
Давайте создадим объект класса Car
, который мы создали в предыдущем разделе.
1 2 3 4 5 |
# Создаем объект класса Car под названием car_a car_a = Car() # Создаем объект класса Car под названием car_b car_b = Car() |
В этом скрипте мы создали два объекта класса Car: car_a
и car_b
. Чтобы узнать тип созданных нами объектов, мы можем использовать метод type
и передать ему названия наших объектов. Выполните следующий код:
1 |
print(type(car_b)) |
В выдаче вы увидите:
1 |
<class '__main__.Car'> |
Это говорит нам о том, что тип объекта car_b
– класс Car
.
На данный момент мы создали наш класс и соответствующие ему объекты. Теперь настало время получить доступ к атрибутам класса и вызвать метод класса при помощи объекта класса. Чтобы сделать это, вам нужно только вписать имя объекта, за которым следует оператор точка .
и название атрибута или метода, к которому вы хотите получить доступ или вызов, соответственно. Давайте рассмотрим следующий пример:
1 |
car_b.start() |
В этом скрипте мы вызываем метод start()
через объект car_b
. Выдача будет выглядеть следующим образом:
1 |
Заводим двигатель |
Аналогично, вы можете получить доступ к атрибуту, пользуясь следующим синтаксисом:
1 |
print(car_b.model) |
В выдаче вы увидите значение атрибута модели, как показано ниже:
1 |
2008 |
Атрибуты класса
В предыдущей секции мы разобрались, как создавать объекты класса и как мы можем использовать эти объекты для получения доступа к атрибутам класса
В Python, каждый объект содержит определенные атрибуты по умолчанию и методы в дополнение к определенным пользователем атрибутами. Чтобы посмотреть на все атрибуты и методы объекта, используйте встроенную функцию под названием dir()
. Попробуем взглянуть на все атрибуты объекта car_b
, который мы создали в предыдущем разделе. Выполните следующий скрипт:
1 |
print(dir(car_b)) |
В выдаче вы увидите следующие атрибуты:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'make', 'model', 'name', 'start', 'stop'] |
Эта встроенная функция очень полезна при изучении атрибутов и функций объекта, особенно при использовании через REPL.
Атрибуты класса против атрибутов экземпляров
Атрибуты могут быть наглядно отнесены к двум типам:
- атрибуты класса
- атрибуты экземпляров
Атрибуты класса делятся среди всех объектов класса, в то время как атрибуты экземпляров являются собственностью экземпляра.
Помните, что экземпляр — это просто альтернативное название объекта.
Атрибуты экземпляра объявляются внутри любого метода, в то время как атрибуты класса объявляются вне любого метода.
Следующий пример прояснит эту разницу:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Car: # создаем атрибуты класса car_count = 0 # создаем методы класса def start(self, name, make, model): print("Двигатель заведен") self.name = name self.make = make self.model = model Car.car_count += 1 |
В указанном выше скрипте мы создаем класс Car
с одним атрибутом класса под названием car_count
и три атрибута экземпляра под названием name
, make
и model
. Класс содержит один метод start()
, который содержит наши три атрибута экземпляров. Значения атрибутов экземпляров переданы в качестве аргументов методу start()
. Внутри метода start
, атрибут car_count
увеличен на один.
Стоит упомянуть, что внутри метода, атрибуты экземпляра ссылаются при помощи ключевого слова self
, в то время как атрибуты класса ссылаются при помощи названия класса.
Давайте создадим объект класса Car
и вызовем метод start()
.
1 2 3 4 |
car_a = Car() car_a.start("Corrola", "Toyota", 2015) print(car_a.name) print(car_a.car_count) |
В скрипте выше мы вывели название атрибута экземпляра и атрибута класса car_count
. В выдаче вы увидите, что атрибут car_count
будет иметь значение 1, как показано ниже:
1 2 3 |
Двигатель заведен Corrola 1 |
Теперь создадим еще один объект класса Car
и вызываем метод start()
.
1 2 3 4 |
car_b = Car() car_b.start("City", "Honda", 2013) print(car_b.name) print(car_b.car_count) |
Сейчас если вы выведите значение атрибута car_count
, вы увидите 2 в выдаче. Это связано с тем, что атрибут car_count
является атрибутом класса и таким образом он разделяется между экземплярами. Объект car_a
увеличил свое значение до 1, в то время как car_b
увеличил свое значение еще раз, так что итоговое значение равняется 2. Выдача выглядит следующим образом:
1 2 3 |
Двигатель заведен City 2 |
Методы
Как мы выяснили ранее, в объектно-ориентированном программировании, методы используются для реализации функционалов объекта. В предыдущем разделе мы создали методы start()
и stop()
для класса Car
. До этих пор, мы использовали объекты класса для вызова методов. Однако, есть тип методов, который может быть вызван напрямую при помощи имени класса. Такой метод называется статичным методом.
Статичные методы
Для объявления статического метода, вам нужно указать дескриптор @staticmethod
перед названием метода, как показано ниже:
1 2 3 4 5 6 7 |
class Car: @staticmethod def get_class_details(): print ("Это класс Car") Car.get_class_details() |
В коде выше мы создали класс Car
с одним статичным методом get_class_details()
. Давайте вызовем этот метод, используя название класса.
1 |
Car.get_class_details() |
Вы можете видеть что нам не нужно создавать экземпляр класса Car
для вызова метода get_class_details()
, вместо этого мы просто использовали название класса. Стоит упомянуть, что статические методы могут иметь доступ только к атрибутам класса в Python, вы не сможете обратиться к методам через self
.
Возврат множественных значений из метода
Одна из лучших особенностей языка Python заключается в том, что методы класса могут возвращать множественные значения. Взгляните на следующий пример:
1 2 3 4 5 6 7 |
class Square: @staticmethod def get_squares(a, b): return a*a, b*b print(Square.get_squares(3, 5)) |
В скрипте выше мы создали класс под названием Square
со статичным методом get_squares()
. Метод принимает два параметра. Он умножает каждый параметр на себя и возвращает оба результата при помощи оператора return
. В выдаче указанного выше скрипта вы увидите квадраты 3 и 5.
Метод str
До этого момента мы выводили атрибуты при помощи метода print()
. Посмотрим, что случится, если мы выведем объект класса.
Для этого нам нужно создать простой класс Car
с одним методом и попытаться вывести объект класса в консоль. Выполним следующий скрипт:
1 2 3 4 5 6 7 8 |
class Car: # создание методов класса def start(self): print ("Двигатель заведен") car_a = Car() print(car_a) |
В скрипте выше мы создали объект car_a
класса Car
и вывели его значение на экран. По сути мы относимся к объекту car_a
как к строке. Выдача выглядит следующим образом:
1 |
<__main__.Car object at 0x000001CCCF4335C0> |
Выдача показывает локацию памяти, где хранится наш объект. Каждый объект Python по умолчанию содержит метод __str__ .
Когда вы используете объект в качестве строки, вызывается метод __str__
, который по умолчанию выводит локацию памяти объекта. Однако, вы также можете предоставить собственное определение метода __str__
. Например, как в следующем примере:
1 2 3 4 5 6 7 8 9 10 11 12 |
# создание класса Car class Car: # создание методов класса def __str__(self): return "Car class Object" def start(self): print ("Двигатель заведен") car_a = Car() print(car_a) |
В скрипте выше, мы переопределили метод __str__
, предоставив наше собственное определение метода. Теперь, если вы выведите объект car_a, вы увидите сообщение «Car class Object» в консоли. Это сообщение, которое мы внесли в наш пользовательский метод __str__
.
Использование этого метода позволяет вам создавать пользовательские и более осмысленные описания, когда объект выводится. Вы можете даже отобразить кое-какие данные внутри класса, такие как название класса Car
.
Конструкторы
Конструктор — это специальный метод, который вызывается по умолчанию когда вы создаете объект класса.
Для создания конструктора вам нужно создать метод с ключевым словом __init__
. Взгляните на следующий пример:
1 2 3 4 5 6 7 8 9 |
class Car: # создание атрибутов класса car_count = 0 # создание методов класса def __init__(self): Car.car_count +=1 print(Car.car_count) |
В скрипте выше мы создали класс Car
с одним атрибутом класса car_count
. Класс содержит конструктор, который увеличивает значение car_count
и выводит итоговое значение на экран.
Теперь, когда объект класса Car
будет создан, конструктор также будет вызван, значение car_count
увеличится и отобразится на экране. Создадим простой объект и посмотрим, что выйдет:
1 2 3 |
car_a = Car() car_b = Car() car_c = Car() |
В выдаче вы увидите выведенное значение 1, 2 и 3, поскольку для каждого объекта значение переменной car_count
увеличивается и отображается на экране.
За исключением названия, конструктор может использоваться как обычный метод. Вы можете передавать и получать значения из конструктора. Он обычно используется таким образом, когда вам нужно инициализировать значения атрибута при создании экземпляра класса.
Локальные переменные против глобальных
Мы знаем, что есть два типа атрибутов Python: атрибуты экземпляра и атрибуты класса.
Атрибуты класса также называются переменными. В зависимости от области видимости, переменные также могут относиться к двум типам: локальные переменные и глобальные переменные.
Локальные переменные
Локальная переменная в классе — это переменная, доступ к которой возможен только внутри блока кода, в котором она определена. Например, если вы определите переменную внутри метода, к нему не удастся получить доступ откуда-либо вне метода. Посмотрим на следующий скрипт:
1 2 3 4 5 |
# создаем класс Car class Car: def start(self): message = "Двигатель заведен" return message |
В скрипте выше мы создали локальную переменную message
внутри метода start()
класса Car
. Теперь создадим объект класса Car
и попытаемся получить доступ к локальной переменной message
, как показано ниже:
1 2 |
car_a = Car() print(car_a.message) |
Скрипт выше приводит к следующей ошибке AttributeError:
1 |
AttributeError: 'Car' object has no attribute 'message' |
Это связано с тем, что мы не можем получить доступ к локальной переменной вне блока, где эта локальная переменная была определена.
Глобальная переменная
Глобальная переменная определяется вне любого блока, то есть метода, операторов-if, и тому подобное. Доступ к глобальной переменной может быть получен где угодно в классе. Рассмотрим следующий пример.
1 2 3 4 5 6 7 8 9 10 |
# создаем класс Car class Car: message1 = "Двигатель заведен" def start(self): message2 = "Автомобиль заведен" return message2 car_a = Car() print(car_a.message1) |
В этом скрипте мы создали глобальную переменную message1
и вывели ее значение на экран. В выдаче вы увидите значение переменной message1
, выведенной без ошибки.
Обратите внимание на то, что существует разница между атрибутами класса и экземпляра, а также между глобальными и локальными переменными.
Атрибуты экземпляра и класса отличаются способом получения доступа к ним. Другими словами, речь идет об использовании названия класса и использовании названия экземпляра. С другой стороны, глобальные и локальные переменные отличаются своими областями видимости, другими словами, местами, где к ним может быть получен доступ.
Доступ к локальной переменной может быть получен только внутри метода. Хотя в этой статье локальные переменные и атрибуты экземпляров определяются внутри метода, локальные переменные определяются собственным ключевым словом.
Модификаторы доступа
Модификаторы доступа в Python используются для модификации области видимости переменных по умолчанию. Есть три типа модификаторов доступов в Python ООП:
- публичный — public;
- приватный — private;
- защищенный — protected.
Доступ к переменным с модификаторами публичного доступа открыт из любой точки вне класса, доступ к приватным переменным открыт только внутри класса, и в случае с защищенными переменными, доступ открыт только внутри того же пакета.
Для создания приватной переменной, вам нужно проставить префикс двойного подчеркивание __
с названием переменной.
Для создания защищенной переменной, вам нужно проставить префикс из одного нижнего подчеркивания _
с названием переменной. Для публичных переменных, вам не нужно проставлять префиксы вообще.
Давайте взглянем на публичные, приватные и защищенные переменные в действии. Выполните следующий скрипт:
1 2 3 4 5 6 |
class Car: def __init__(self): print ("Двигатель заведен") self.name = "corolla" self.__make = "toyota" self._model = 1999 |
Здесь мы создали простой класс Car
с конструктором и тремя переменными: name
, make
, и model
(название, марка и модель). Переменная name
является публичной, в то время как переменные make
и model
являются приватными и защищенными, соответственно.
Давайте создадим объект класса Car
и попытаемся получить доступ к переменной name
. Выполним следующий скрипт:
1 2 |
car_a = Car() print(car_a.name) |
Так как name
является публичной переменной, мы можем получить к ней доступ не из класса. В выдаче вы увидите значение переменной name
, выведенное в консоли.
Теперь попробуем вывести значение переменной make
. Выполняем следующий скрипт:
1 |
print(car_a.make) |
В выдаче мы получим следующее уведомление об ошибке:
1 |
AttributeError: 'Car' object has no attribute 'make' |
Мы рассмотрели большую часть основных концепций объектно-ориентированного программирования в предыдущих двух секциях. Теперь, поговорим о столбах объектно-ориентированного программирования:
- Полиморфизм;
- Наследование;
- Инкапсуляция.
Наследование
Наследование в объектно-ориентированном программировании очень похоже на наследование в реальной жизни, где ребенок наследует те или иные характеристики его родителей в дополнение к его собственным характеристикам.
В объектно-ориентированном программировании, наследование означает отношение IS-A
. Например, болид — это транспорт. Наследование это одна из самых удивительных концепций объектно-ориентированного программирования, так как оно подразумевает повторное использование.
Основная идея наследования в объектно-ориентированном программировании заключается в том, что класс может наследовать характеристики другого класса. Класс, который наследует другой класс, называется дочерним классом или производным классом, и класс, который дает наследие, называется родительским, или основным.
Рассмотрим на очень простой пример наследования. Выполним следующий скрипт:
1 2 3 4 5 6 7 8 9 |
# Создание класса Vehicle class Vehicle: def vehicle_method(self): print("Это родительский метод из класса Vehicle") # Создание класса Car, который наследует Vehicle class Car(Vehicle): def car_method(self): print("Это метод из дочернего класса") |
В скрипте выше мы создаем два класса: Vehicle
и Car
, который наследует класс Vehicle
. Чтобы наследовать класс, вам нужно только вписать название родительского класса внутри скобок, которая следует за названием дочернего класса. Класс Vehicle
содержит метод vehicle_method()
, а дочерний класс содержит метод car_method()
. Однако, так как класс Car
наследует класс Vehicle
, он также наследует и метод vehicle_method()
.
Рассмотрим это на практике и выполним следующий скрипт:
1 2 |
car_a = Car() car_a.vehicle_method() # Вызываем метод родительского класса |
В этом скрипте мы создали объект класса Car
вызывали метод vehicle_method()
при помощи объекта класса Car
. Вы можете обратить внимание на то, что класс Car
не содержит ни одного метода vehicle_method()
, но так как он унаследовал класс Vehicle
, который содержит vehicle_method()
, класс Car
также будет использовать его. Выдача выглядит следующим образом:
1 |
Это родительский метод из класса Vehicle |
Множественное наследование Python
В Python, родительский класс может иметь несколько дочерних, и, аналогично, дочерний класс может иметь несколько родительских классов. Давайте рассмотрим первый сценарий. Выполним следующий скрипт:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# создаем класс Vehicle class Vehicle: def vehicle_method(self): print("Это родительский метод из класса Vehicle") # создаем класс Car, который наследует Vehicle class Car(Vehicle): def car_method(self): print("Это дочерний метод из класса Car") # создаем класс Cycle, который наследует Vehicle class Cycle(Vehicle): def cycleMethod(self): print("Это дочерний метод из класса Cycle") |
В этом скрипте, родительский класс Vehicle
наследуется двумя дочерними классами — Car
и Cycle
. Оба дочерних класса будут иметь доступ к vehicle_method()
родительского класса. Запустите следующий скрипт, чтобы увидеть это лично:
1 2 3 4 |
car_a = Car() car_a.vehicle_method() # вызов метода родительского класса car_b = Cycle() car_b.vehicle_method() # вызов метода родительского класса |
В выдаче вы увидите выдачу метода vehicle_method()
дважды, как показано ниже:
1 2 |
Это родительский метод из класса Vehicle Это родительский метод из класса Vehicle |
Вы можете видеть, как родительский класс наследуется двумя дочерними классами. Таким же образом, дочерний класс может иметь несколько родительских. Посмотрим на пример:
1 2 3 4 5 6 7 8 9 10 11 |
class Camera: def camera_method(self): print("Это родительский метод из класса Camera") class Radio: def radio_method(self): print("Это родительский метод из класса Radio") class CellPhone(Camera, Radio): def cell_phone_method(self): print("Это дочерний метод из класса CellPhone") |
В скрипте выше мы создали три класса: Camera
, Radio
, и CellPhone
. Классы Camera
и Radio
наследуются классом CellPhone
. Это значит, что класс CellPhone
будет иметь доступ к методам классов Camera
и Radio
. Следующий скрипт подтверждает это:
1 2 3 |
cell_phone_a = CellPhone() cell_phone_a.camera_method() cell_phone_a.radio_method() |
Выдача будет выглядеть следующим образом:
1 2 |
Это родительский метод из класса Camera Это родительский метод из класса Radio |
Полиморфизм
Термин полиморфизм буквально означает наличие нескольких форм. В контексте объектно-ориентированного программирования, полиморфизм означает способность объекта вести себя по-разному.
Полиморфизм в программировании реализуется через перегрузку метода, либо через его переопределение.
Перегрузка метода
Перегрузка метода относится к свойству метода вести себя по-разному, в зависимости от количества или типа параметров. Взглянем на очень простой пример перегрузки метода. Выполним следующий скрипт:
1 2 3 4 5 6 7 |
# создаем класс Car class Car: def start(self, a, b=None): if b is not None: print (a + b) else: print (a) |
В скрипте выше, если метод start()
вызывается передачей одного аргумента, параметр будет выведен на экран. Однако, если мы передадим 2 аргумента методу start()
, он внесет оба аргумента и выведет результат суммы.
Попробуем с одним аргументом для начала:
1 2 |
car_a = Car() car_a.start(10) |
В выдаче мы можем видеть 10. Теперь попробуем передать два аргумента:
1 |
car_a.start(10, 20) |
В выдаче вы увидите 30.
Переопределение метода
Переопределение метода относится к наличию метода с одинаковым названием в дочернем и родительском классах. Определение метода отличается в родительском и дочернем классах, но название остается тем же. Давайте посмотрим на простой пример переопределения метода в Python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# создание класса Vehicle class Vehicle: def print_details(self): print("Это родительский метод из класса Vehicle") # создание класса, который наследует Vehicle class Car(Vehicle): def print_details(self): print("Это дочерний метод из класса Car") # создание класса Cycle, который наследует Vehicle class Cycle(Vehicle): def print_details(self): print("Это дочерний метод из класса Cycle") |
В скрипте выше, классы Cycle
и Car
наследуют класс Vehicle
. Класс Vehicle
содержит метод print_details()
, который переопределен дочерним классом. Теперь, если вы вызовите метод print_details()
, выдача будет зависеть от объекта, через который вызывается метод. Выполните следующий скрипт, чтобы понять суть на деле:
1 2 3 4 5 6 7 8 |
car_a = Vehicle() car_a. print_details() car_b = Car() car_b.print_details() car_c = Cycle() car_c.print_details() |
Выдача будет выглядеть вот так:
1 2 3 |
Это родительский метод из класса Vehicle Это дочерний метод из класса Car Это дочерний метод из класса Cycle |
Как вы видите, выдача отличается, к тому же метод print_details()
вызывается через производные классы одного и того же базового класса. Однако, так как дочерние классы переопределены методом родительского класса, методы ведут себя по-разному.
Инкапсуляция
Инкапсуляция — это третий столп объектно-ориентированного программирования. Инкапсуляция просто означает скрытие данных. Как правило, в объектно-ориентированном программировании один класс не должен иметь прямого доступа к данным другого класса. Вместо этого, доступ должен контролироваться через методы класса.
Чтобы предоставить контролируемый доступ к данным класса в Python, используются модификаторы доступа и свойства. Мы уже ознакомились с тем, как действуют модификаторы доступа. В этом разделе мы посмотрим, как действуют свойства.
Предположим, что нам нужно убедиться в том, что модель автомобиля должна датироваться между 2000 и 2018 годом. Если пользователь пытается ввести значение меньше 2000 для модели автомобиля, значение автоматически установится как 2000, и если было введено значение выше 2018, оно должно установиться на 2018. Если значение находится между 2000 и 2018 — оно остается неизменным. Мы можем создать свойство атрибута модели, которое реализует эту логику. Взглянем на пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# создаем класс Car class Car: # создаем конструктор класса Car def __init__(self, model): # Инициализация свойств. self.model = model # создаем свойство модели. @property def model(self): return self.__model # Сеттер для создания свойств. @model.setter def model(self, model): if model < 2000: self.__model = 2000 elif model > 2018: self.__model = 2018 else: self.__model = model def getCarModel(self): return "Год выпуска модели " + str(self.model) carA = Car(2088) print(carA.getCarModel()) |
Свойство имеет три части. Вам нужно определить атрибут, который является моделью в скрипте выше. Затем, вам нужно определить свойство атрибута, используя декоратор @property
. Наконец, вам нужно создать установщик свойства, который является дескриптором @model.setter
в примере выше.
Теперь, если вы попробуете ввести значение выше 2018
в атрибуте модели, вы увидите, что значение установлено на 2018
. Давайте проверим это. Выполним следующий скрипт:
1 2 |
car_a = Car(2088) print(car_a.get_car_model()) |
Здесь мы передаем 2088
как значение для модели, однако, если вы введете значение для атрибута модели через функцию get_car_model()
, вы увидите 2018
в выдаче.
Подведем итоги
В этой статье мы освоили часть важнейших основ объектно-ориентированного программирования. Этот тип программирования — один из самых популярных и используемых парадигм.
Важность объектно-ориентированного программирования отображается в том факт, что большая часть современных языков программирования так или иначе объектно-ориентированы или, по крайней мере, поддерживают объектно-ориентированное программирование.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»