мета-данные страницы
  •  
Загрузка не удалась. Возможно, проблемы с правами доступа?

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
python:week22 [21/01/2019 01:00] – [Абстракция] ybezrukovpython:week22 [21/01/2019 12:02] (текущий) – [Процедурные языки] ybezrukov
Строка 4: Строка 4:
  
 Чтобы понять что такое объектно-ориентированное программирование (ООП) нужно сначала понять что оно заменяет. Ранние языки были процедурными: программисту требовалось описать конкретный набор процедур которые выполнял компьютер. Чтобы понять что такое объектно-ориентированное программирование (ООП) нужно сначала понять что оно заменяет. Ранние языки были процедурными: программисту требовалось описать конкретный набор процедур которые выполнял компьютер.
- 
- 
- 
-To understand what an object-oriented programming language is, you need to understand what it replaced. Early programming languages were procedural—so-called because the programmer would define a very specific set of procedures that the computer would undertake. 
  
 В те времена процедуры записывались на перфокартах. На каждом шаге данные читались, обрабатывались и сохранялись. Этот подход хорошо работал тогда, да и сейчас в общем-то свою функцию выполняет. Однако, когда нам требуется написать программу, которая сложнее чем набор простых шагов - то в этом случае решение с помощью процедурного подход становится сложным для работы. Одним из вариантов преодоления результирующей сложности программы является ООП. В те времена процедуры записывались на перфокартах. На каждом шаге данные читались, обрабатывались и сохранялись. Этот подход хорошо работал тогда, да и сейчас в общем-то свою функцию выполняет. Однако, когда нам требуется написать программу, которая сложнее чем набор простых шагов - то в этом случае решение с помощью процедурного подход становится сложным для работы. Одним из вариантов преодоления результирующей сложности программы является ООП.
Строка 42: Строка 38:
  
 Все эти переменные и методы хранятся в пределах объекта Слон. Так как они сокрыты, программист может менять структуру и содержимое объекта не беспокоясь о его интерфейсе: наборе публичных методов. Все эти переменные и методы хранятся в пределах объекта Слон. Так как они сокрыты, программист может менять структуру и содержимое объекта не беспокоясь о его интерфейсе: наборе публичных методов.
 +
 +===== Наследование =====
 +
 +В дополнение к классам, в ООП языках еще есть подклассы. Они содержат все атрибуты родительского класса, но могут иметь и дополнительные свои. В наших шахматах пешка должна иметь метод transformPiece(), который превратит ее в другую фигуру, когда она дойдет до конца доски. Однако, этот метод нужен только пешке, и нет никакого смысла иметь его в других фигурах. Поэтому создадим для класса Фигура подкласс Пешка. Так как он подкласс Фигуры, он будет содержать все ее параметры, иными словами объект класса Пешка включает цвет, высоту, форму и набор перемещений. А еще он имеет метод transformPiece() которого нет у других. И нам не нужно будет беспокоиться, если случайно попытаемся вызвать этот метод у Ладьи, это сразу приведет к ошибке.
 +
 +Создание подклассов позволяет экономить время. Вместо создания новых классов для всего программист создает базовый класс и затем расширяет его новыми подклассами по мере необходимости.
 +
 +===== Полиморфизм =====
 +
 +Полиморфизм это следствие наследования. Чтобы полностью его понять потребуется некоторый опыт в программировании, поэтому рассмотрим только самые основы. Если кратко, полиморфизм это возможность использовать метод с одинаковым именем для разных объектов.
 +
 +Например, класс Фигура может содержать метод move(), который двигает фигуру на одну клетку в каждом направлении. Это хорошо подходит Королю, но малоприменимо для всех остальных фигур. Чтобы исправить эту проблему, мы можем описать новый метод move() для подкласса Ладья. Этот метод опишет способ перемещения на произвольное количество клеток по горизонтали и вертикали.
 +
 +В этом случае, когда программист выполнит метод move() и передаст ему фигуру в качестве аргумента, программа будет точно знать как эта фигура должна двигаться. Это сэкономит кучу времени, которое ушло бы на попытки определить какой из кучи методов (например, move_horizontally(), move_vertically(), move_horizontally_one_step() и т.д.) нам потребуется использовать.
 +
 +===== Резюме =====
 +
 +Прочитанное наверняка вызывает больше вопросов об ООП чем дает ответов. Перечислим кратко основное:
 +
 +  * ООП собирает информацию в единую сущность, именуемую объектом.
 +  * Каждый объект это конкретный экземпляр класса.
 +  * Абстракция скрывает внутренние механизмы объекта, когда нам не нужно о них знать.
 +  * Инкапсуляция хранит и защищает соответствующие переменные и методы внутри объекта.
 +  * Наследование позволяет подклассам использовать атрибуты родительских классов.
 +  * Полиморфизм позволяет объектам и методам работать с разными ситуациями с помощью одного интерфейса.
 +
 +Если запомните эти пункты, то этого будет достаточно чтобы иметь представление об ООП. Сущности довольно туманные, но понимание придет с практикой. Мы обсудили только очень общие свойства ООП, каждый ООП язык имеет свои особенности, подходы и способы как заставить все работать. Рассмотрим ООП на примере Питона.
 +
 +====== ООП в Питоне ======
 +
 +===== Классы =====
 +
 +Класс это базовый строительный блок в ООП. Можно считать класс шаблоном или чертежом объекта, который описывает характеристики объекта. Предположим что у нас есть класс Автомобиль. Он может содержать такие параметры: четыре колеса, двигатель и как минимум одно кресло.
 +
 +<code python> 
 +class Vehicle:
 +  """ Этот класс описывает средство передвижения """
 +  pass
 +</code>
 +
 +Комментарий в начале класса это специальный вид комментария, который называется докстринг в переводе, строка документации. Он должен содержать краткое описание того что делает код. Использование тройных кавычек и говорит Питону о том, что это докстринг. 
 +
 +Ключевое слово ''pass'' говорит интерпретатору Питона ничего не делать. Требуется так как синтаксис Питона не допускает пустоты после :. 
 +
 +Если мы этот код запустим, то ничего не произойдет. Сначала требуется создать экземпляр класса. Что буквально означает: построить объект используя план, описанный в классе. Можно сделать сколько угодно копий, каждую со своими свойствами.
 +
 +<code python>
 +red_car = Vehicle()
 +green_car = Vehicle()
 +blue_car = Vehicle()
 +</code>
 +
 +И все равно, если мы запустим код ничего не произойдет. Он работает корректно, но мы нигде не сказали программе сделать что-то заметное. Класс Vehicle описывает шаблон средства передвижения, а дальше создаются объекты с именами red_car, greed_car, blue_car.
 +
 +Дополним наш код. Добавим в класс Vehicle метод ''__init__'':
 +
 +<code python>
 +class Vehicle:
 +  """ Этот класс описывает средство передвижения """
 +
 +  def __init__(self, color='plain'):
 +    """ задает параметры средства передвижения """
 +    print('Сделали машину!')
 +    self.color = color
 +
 +red_car = Vehicle()
 +green_car = Vehicle()
 +blue_car = Vehicle()
 +</code>
 +
 +Обратите особое внимание на метод ''__init__''. Это специальный метод в Питоне и должен начинаться и заканчиваться двумя символами подчеркивания. Он автоматически вызывается всякий раз, когда создается новый объект. Теперь, если мы запустим этот код, то на экране будет три раза написана строка ''Сделали машину!''
 +
 +Так же метод ''__init__'' имеет аргумент с именем ''color''. Запись ''color='plain''' в описании аргументов функции задает значение аргумента по-умолчанию (т.е. значение, которое получит переменная color если мы не укажем никакого аргумента. Добавим цвета:
 +
 +<code python>
 +red_car = Vehicle(color='red')
 +green_car = Vehicle(color='green')
 +blue_car = Vehicle(color='blue')
 +</code>
 +
 +Если напечатать на экран значение переменной, можно заметить, что у каждого экземпляра цвет свой, хотя созданы они были по одному описанию (классу).
 +
 +<code python>
 +print(red_car.color)
 +print(green_car.color)
 +print(blue_car.color)
 +</code>
 +
 +
 +Так вышло потому, что мы присвоили значение переменной ''self.color''. ''self'' это еще одно ключевое слово в Питоне, оно ссылается на конкретный экземпляр класса (объект). Всякий раз когда мы используем ''self'', мы можем изменить или прочитать данные, уникальные для объекта: красная машина - красная.
 +
 +Расширим метод ''__init__'' и добавим в него 
 +
 +<code python>
 +self.noise = 'Дрыннь!'
 +</code>
 +
 +Можно, конечно, просто напечатать на экран значение этой переменной. Сделаем немного иначе, пусть мы хотим чтобы машина ехала, и в будущем используем это в коде. Или изменим метод передвижения. Заведем функцию (метод) чтобы иметь контроль над тем как все работает. Добавим его сразу после метода ''__init__'':
 +
 +<code python>
 +def drive(self):
 +  print(self.noise)
 +</code>
 +
 +Вызвать этот метод можно написав:
 +
 +<code python>
 +red_car.drive()
 +</code>
 +
 +Всякий раз когда мы будем вызывать метод Питон будет печатать на экран звук движения машины. Вся наша программа сейчас должна выглядеть вот так:
 +
 +<code python>
 +class Vehicle:
 +  """ Этот класс описывает средство передвижения """
 +
 +  def __init__(self, color='plain'):
 +    """ задает параметры средства передвижения """
 +    print('Сделали машину!')
 +    self.color = color
 +    self.noise = 'Дрыннь!'
 +    
 +  def drive(self):
 +    print(self.noise)
 +    
 +red_car = Vehicle(color='red')
 +green_car = Vehicle(color='green')
 +blue_car = Vehicle(color='blue')
 +
 +print(red_car.color)
 +print(green_car.color)
 +print(blue_car.color)
 +
 +red_car.drive()
 +</code>
 +
 +===== Наследование =====
 +
 +Наследование это способ уменьшить дублирование и переиспользовать код. В терминах предков-потомков, наследование позволяет потомку делить общий код с предком. Создадим новый класс ElectricCar
 +
 +<code python> 
 +class ElectricCar(Vehicle):
 +  """ Электромобиль. """
 +  def charge(self):
 +    print('⚡🔋')
 +
 +electric_car = ElectricCar()
 +electric_car.charge()
 +electric_car.noise = 'Вжух!'
 +electric_car.drive()
 +
 +</code>
 +
 +Во время описания класса электромобиля мы указали имя класса Vehicle в скобках. Это инструкция Питону считать класс ElectricCar потомком или подклассом класса Vehicle. Это дает доступ до данных и методов, существующим в Vehicle.
 +
 +Электромобиль имеет так же свои методы. Он может заряжаться (этого другие машины не умеют). И издает другой звук при движении. Обратите внимание, мы не описывали метод drive() в новом классе, однако он работает и выдает верный результат.