Объектно-ориентированное программирование в Python (Часть 2)

Продолжаем разбираться в основных аспектах объектно-ориентированного программирования. В частности, необходимо разобрать методы.

Методы в объектно-ориентированном программировании

Как мы прежде разобрались, в объектно-ориентированном подходе к разработке приложений методы нужны для того, чтобы реализовать определенный функционал объектов. Ранее мы уже создавали методы 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 нужны для того, чтобы изменять область видимости переменных по умолчанию. Выделяют три типа модификаторов доступа в объектно-ориентированном программировании:

  1. Публичный – public.
  2. Приватный – private.
  3. Защищенный – protected.

Можно получить доступ к переменным с модификаторами публичного доступа с любого фрагмента кода. Если переменная приватная, то лишь в рамках одного класса. Что касается защищенных переменных, то лишь в рамках одного и того же пакета.

Столпы объектно-ориентированного программирования

Любой более-менее опытный разработчик знает, что объектно-ориентированное программирование базируется на трех столпах:

  1. Полиморфизм.
  2. Наследование.
  3. Инкапсуляция.

Наследование

Наследование в объектно-ориентированном программировании очень напоминает наследование в реальной жизни, где ребенок принимает те или иные черты родителей в дополнение к его собственным. 

В объектно-ориентированном программировании, соответственно, под наследованием подразумевается отношение 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(), выдача будет зависеть от объекта, через который метод вызывался.

Инкапсуляция

Это еще один столп объектно-ориентированного типа разработки приложений. Фактически инкапсуляция означает скрытие информации. Обычно классы не могут иметь прямого доступа к данным другого класса. Все это осуществляется через методы класса. 

И чтобы управлять доступом к данным класса, как раз и используются свойства и модификаторы доступа. 

Выводы

Таким образом, объектно-ориентированное программирование – это удобно и современно. Оно имеет множество преимуществ по сравнению с процедурным программированием.

ОфисГуру
Adblock
detector