Продолжаем разбираться в основных аспектах объектно-ориентированного программирования. В частности, необходимо разобрать методы.
- Методы в объектно-ориентированном программировании
- Статичные методы
- Возврат множественных значений
- Метод str
- Конструкторы
- Что такое локальные и глобальные переменные?
- Локальные
- Глобальная переменная
- Модификаторы доступа
- Столпы объектно-ориентированного программирования
- Наследование
- Полиморфизм
- Перегрузка метода
- Переопределение метода
- Инкапсуляция
- Выводы
Методы в объектно-ориентированном программировании
Как мы прежде разобрались, в объектно-ориентированном подходе к разработке приложений методы нужны для того, чтобы реализовать определенный функционал объектов. Ранее мы уже создавали методы start() и stop() для нашего класса Car. До этого момента, для вызова использовались объекты класса. Тем не менее есть разновидность методов, вызов которых может осуществляться прямо с участием имени класса. Такой метод именуется статичным.
Статичные методы
Чтобы объявить статический метод, надо использовать дескриптор @staticmethod перед его названием. Этот фрагмент кода наглядно демонстрирует этот принцип.
class Car: @staticmethod def get_class_details(): print ("Это класс Car") Car.get_class_details()
В приведенном коде нами был создан класс Car с одним методом get_class_details(). Давайте метод вызовем с помощью названия класса.
Car.get_class_details()
Как вы можете видеть, нет необходимости в создании объекта класса Car, чтобы вызвать метод get_class_details. Достаточно просто было использовать название класса. Необходимо учитывать то, что статичные методы лишь получают доступ к атрибутам класса. А вот обратиться к методам через self не получится.
Возврат множественных значений
Одна из главных характеристик Python в том, что методы класса возвращают множественные значения. Посмотрите на этот пример.
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, у которого есть один метод, и попробовать вывести его объект в консоль. Давайте выполним такой скрипт.
class Car: # создание методов класса def start(self): print ("Двигатель заведен") car_a = Car() print(car_a)
В этом скрипте был создан объект car_a класса Car, и его значение было выведено на экран. Фактически мы относимся к объекту car_a, как к строке. В выдаче будет что-то такого плана.
<__main__.Car object at 0x000001CCCF4335C0>
Выдача показывает локацию памяти, где хранится наш объект. Все объекты Python по умолчанию включают метод __str__. Когда объект используется в качестве строки, то вызывается и метод __str__, который по умолчанию выводит локацию памяти объекта. Тем не менее вы также можете предоставить свое определение этого метода. Приведем пример.
# создание класса 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__. Посмотрите на этот фрагмент кода.
class Car: # создание атрибутов класса car_count = 0 # создание методов класса def __init__(self): Car.car_count +=1 print(Car.car_count)
Здесь мы сгенерировали класс Car с единственным атрибутом класса car_count. Сам класс имеет конструктор, увеличивающий значение car_count и выводящий получившееся число на экран.
Когда объект класса Car будет сгенерирован, конструктор также будет вызван. Соответственно, значение car_count также увеличится и будет показано на экране.
Давайте создадим простой объект и посмотрим, что получится в итоге.
car_a = Car()
car_b = Car()
car_c = Car()
В выдаче будет видно выведенное значение 1,2 и 3. Ведь для каждого из объектов значение переменной car_count будет увеличиваться и отображаться на экране.
В целом, конструктор может использоваться как стандартный метод. Из него возможна как передача значения, так и прием. Он, как правило, применяется так, когда необходимо инициализировать значения атрибута в процессе создания экземпляра класса.
Что такое локальные и глобальные переменные?
Насколько мы помним, есть два типа атрибутов Python: атрибуты экземпляра и атрибуты класса. Последние нередко называют переменными. В зависимости от имеющейся области видимости, переменные атрибуты также нередко относятся к одному из двух типов: локальным и глобальным переменным.
Локальные
Под локальной переменной подразумевается такая переменная, к которой можно получить доступ с лишь внутри того блока кода, в которой она объявлена. Так, если вы определите переменную в рамках метода, к нему не получится получить доступ за его пределами. Посмотрите на этот фрагмент кода.
# создаем класс Car class Car: def start(self): message = "Двигатель заведен" return message
В нем нами была создана локальная переменная message в рамках метода start() класса Car. Теперь создадим объект класса Car и попробуем получить доступ к локальной переменной message так, как показано во фрагменте кода ниже.
car_a = Car() print(car_a.message)
Если мы попробуем выполнить указанный фрагмент кода, появится ошибка AttributeError, которая выглядит следующим образом.
AttributeError: 'Car' object has no attribute 'message'
Причина того кроется в том, что у нас нет возможности получить доступ к локальной переменной за пределами блока, где данная локальная переменная была определена.
Глобальная переменная
Определение глобальной переменной осуществляется за пределами любого блока. То есть, за рамками метода, операторов if, и так далее. Доступ к глобальной переменной может осуществляться в каком-угодно месте класса. Давайте проанализируем такой фрагмент кода.
# создаем класс Car class Car: message1 = "Двигатель заведен" def start(self): message2 = "Автомобиль заведен" return message2 car_a = Car() print(car_a.message1)
В данном скрипте была создана глобальная переменная message1, а ее значение было выведено на экран. В выдаче вы обнаружите значение переменной message1, которая была выведена без ошибки.
Учтите то, что есть определенная разница между атрибутами класса и экземпляра, а также между глобальными и локальными переменными.
Атрибуты экземпляра и класса отличаются по способу получения доступа к ним. Проще говоря, мы говорим об использовании названия класса и использовании названия экземпляра. С другой же стороны, глобальные и локальные переменные отличаются между собой по областям видимости. Проще говоря, местам, где доступ к ним может быть получен.
Таким образом, мы можем работать с любой локальной переменной лишь в пределах метода. Тем не менее в этой статье локальные переменные и атрибуты экземпляров определяются в рамках метода, локальные переменные определяются собственным ключевым словом.
Модификаторы доступа
Модификаторы доступа в Python нужны для того, чтобы изменять область видимости переменных по умолчанию. Выделяют три типа модификаторов доступа в объектно-ориентированном программировании:
- Публичный – public.
- Приватный – private.
- Защищенный – protected.
Можно получить доступ к переменным с модификаторами публичного доступа с любого фрагмента кода. Если переменная приватная, то лишь в рамках одного класса. Что касается защищенных переменных, то лишь в рамках одного и того же пакета.
Столпы объектно-ориентированного программирования
Любой более-менее опытный разработчик знает, что объектно-ориентированное программирование базируется на трех столпах:
- Полиморфизм.
- Наследование.
- Инкапсуляция.
Наследование
Наследование в объектно-ориентированном программировании очень напоминает наследование в реальной жизни, где ребенок принимает те или иные черты родителей в дополнение к его собственным.
В объектно-ориентированном программировании, соответственно, под наследованием подразумевается отношение IS-A. Например, гоночный болид – это транспорт. Он имеет все характеристики, ему присущие. Наследование является одним из самых значимых столпов объектно-ориентированного программирования, поскольку подразумевает повторное использование.
Вот пример наследования с комментариями.
# Создание класса Vehicle class Vehicle: def vehicle_method(self): print("Это родительский метод из класса Vehicle") # Создание класса Car, который наследует Vehicle class Car(Vehicle): def car_method(self): print("Это метод из дочернего класса")
Полиморфизм
Полиморфизм означает, что объект может в разных ситуациях вести себя по-разному. Реализация полиморфизма в программировании осуществляется путем перегрузки метода, либо через его переопределение.
Перегрузка метода
Перегрузка метода относится к свойству метода вести себя различным образом в зависимости от того, сколько параметров, и какого они типа.
# создаем класс Car class Car: def start(self, a, b=None): if b is not None: print (a + b) else: print (a)
Переопределение метода
Переопределение метода – это наличие одноименного метода как в дочернем, так и родительском классе. Выглядит это так.
# создание класса 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(), выдача будет зависеть от объекта, через который метод вызывался.
Инкапсуляция
Это еще один столп объектно-ориентированного типа разработки приложений. Фактически инкапсуляция означает скрытие информации. Обычно классы не могут иметь прямого доступа к данным другого класса. Все это осуществляется через методы класса.
И чтобы управлять доступом к данным класса, как раз и используются свойства и модификаторы доступа.
Выводы
Таким образом, объектно-ориентированное программирование – это удобно и современно. Оно имеет множество преимуществ по сравнению с процедурным программированием.