Похожие презентации:
История развития ООП
1.
История развития ООП. Базовыепонятия ООП: объект, его свойства
и методы, класс, интерфейс.
Основные принципы ООП:
инкапсуляция, наследования,
полиморфизм.
2.
ООП• Одна из самых распространенных методик
разработки программных продуктов —
объектно-ориентированное программирование
(ООП). При работе с функциями мы уже
использовали принцип модульности (сокрытие
сложного алгоритма за вызовом функций) и
библиотеки (упаковка функций, решающих схожие
задачи, в одно хранилище — библиотеку). В ООП
появляется еще один пример модульности —
объект. Объекты хранят внутри себя и данные, и
обрабатывающие их функции.
3.
ООП!• Язык программирования Python появился в 1991 году. К
этому времени была разработана теоретическая база
объектно-ориентированного программирования,
появились исследовательские языки программирования,
проверившие эти идеи на практике, и даже возникло
первое поколение объектно-ориентированных языков для
широкого круга программистов.
• Поэтому, ориентируясь на чужие успехи и неудачи, Гвидо
ван Россум и его коллеги смогли спроектировать
достаточно простую и мощную реализацию ООП. Python
поддерживает ООП на сто процентов: все данные в нем
являются объектами. Числа всех типов, строки, списки,
словари, даже функции, модули и, наконец, сами типы
данных — все это объекты!
4.
ООП• прямой аналогии между объектами
материального мира и объектами из мира
программирования нет. Ведь в
программировании есть объекты, которые
обозначают какой-то процесс (например,
функции), или состояние процесса, или
вообще произвольные абстрактные понятия.
Например, массив или число с плавающей
точкой — сами по себе достаточно
абстрактные понятия, которые имеют
отдаленные аналоги в реальном мире.
5.
ООП• Представим себе комнату. В ней есть мебель:
несколько столов, стулья, шкафы. Стулья могут
отличаться цветом, формой, количеством ножек, но
все равно мы всегда сможем отличить стул от
шкафа.
• Важно!
• Если задуматься, у каждого объекта есть набор
свойств и действия, в которых он может
участвовать. Основываясь на этих свойствах
(наличие сиденья) и действиях (на стуле можно
сидеть), мы классифицируем объекты, то есть
относим их к тому или иному классу.
6.
7.
Основные понятияКласс
• Описывает модель объекта, его свойства и поведение. Говоря
языком программиста, класс — такой тип данных, который
создается для описания сложных объектов.
Экземпляр
• Для краткости вместо «Объект, порожденный классом „Стул“»
говорят «экземпляр класса „Стул“».
Объект
• Хранит конкретные значения свойств и информацию о
принадлежности к классу. Может выполнять методы.
Атрибут
• Свойство, присущее объекту. Класс объекта определяет, какие
атрибуты есть у объекта. Конкретные значения атрибутов —
характеристика уже не класса, а конкретного экземпляра этого
класса, то есть объекта
8.
Основные понятияМетод
Действие, которое объект может
выполнять над самим собой или
другими объектами.
Чтобы стало чуть понятнее, давайте
разберем на примере: 1, 2, 3, "abc",
[10, 20, 30] — объекты. А int, str, list —
классы. Да, все типы данных, которые
мы изучали ранее, на самом деле —
классы:
1, 2, 3 — экземпляры класса int
"abc" — класса str
[10, 20, 30] — экземпляр класса list, в
который вложены экземпляры int
Чтобы узнать, к какому классу
относится тот или иной объект, можно
воспользоваться функцией type.
Например,
>>> type(123)
# => '<class 'int'>'
>>> type([1, 2, 3])
# => '<class 'list'>'
9.
Создание классовДавайте создадим простейший класс, который будет
моделировать обычный фрукт. На языке Python это будет
выглядеть так:
class Fruit:
pass
PEP 8
Имена классов по стандарту именования PEP 8 должны
начинаться с большой буквы. Встроенные классы (int, float,
str, list и др.) этому правилу не следуют, однако в вашем
коде его лучше придерживаться — так делает
большинство программистов на Python.
10.
Определение класса• Определение этого класса состоит из
зарезервированного слова class, имени класса
и пустой инструкции после отступа.
• Внутри класса с дополнительным уровнем
отступов должны определяться его методы, но
сейчас их нет. Однако хотя бы одна инструкция
должна быть, поэтому приходится
использовать пустую инструкцию-заглушку
pass. Она предназначена как раз для таких
случаев.
• Описав класс, мы создали модель фрукта.
11.
Теперь создадим два конкретных фрукта —экземпляра класса Fruit:
a = Fruit()
b = Fruit()
Переменные a и b содержат ссылки на два разных
объекта — экземпляра класса Fruit, которые
можно наделить разными атрибутами:
a.name = 'apple'
a.weight = 120
# теперь a - это яблоко весом 120 грамм
b.name = 'orange'
b.weight = 150
# а b - это апельсин весом 150 грамм
12.
Важно!Атрибуты можно не только устанавливать, но и читать. При чтении
еще не созданного атрибута будет появляться ошибка AttributeError.
Вы ее часто увидите, допуская неточности в именах атрибутов и
методов.
print(a.name, a.weight) # apple 120
print(b.name, b.weight) # orange 150
b.weight -= 10 # Апельсин долго лежал на складе и усох
print(b.name, b.weight) # orange 140
c = Fruit()
c.name = 'lemon'
c.color = 'yellow'
# Атрибут color появился только в объекте c.
# Забыли добавить свойство weight и обращаемся к нему
print(c.name, c.weight)
# Ошибка AttributeError, нет атрибута weight
13.
Методы классовНа данный момент мы пользуемся объектами только для
хранения соответствий между именами атрибутов и их
значениями. Но на самом деле возможности объектов
значительно шире. Давайте рассмотрим, как можно
запрограммировать объекты на выполнение определенных
действий:
class Greeter:
def hello_world(self):
print("Привет, Мир!")
greet = Greeter()
greet.hello_world() # выведет "Привет, Мир!"
14.
После беглого осмотра этого кода видно, что внутрикласса Greeter находится определение чего-то, похожего
на функцию, печатающую фразу «Привет, Мир!» На
самом деле мы написали метод, с синтаксисом вызова
которого вы хорошо знакомы по методу строк split или
методу списков append. Теперь мы можем создавать
такие методы самостоятельно.
Важно!
• При создании собственных методов обратите
внимание на два момента:
• Метод должен быть определен внутри класса
(добавляется уровень отступов)
• У методов всегда есть хотя бы один аргумент, и
первый по счету аргумент должен называться self
15.
• Аргументу self следует уделить особоевнимание. В него передается тот объект,
который вызвал этот метод. Поэтому self еще
часто называют контекстным объектом.
Рассмотрим чуть подробнее. Когда программа
вызывает метод объекта, Python передает ему в
первом аргументе экземпляр вызывающего
объекта, который всегда связан с параметром
self. Иными словами, greet.hello_world()
преобразуется в вызов Greeter.hello_world(greet).
Этот факт объясняет особую важность параметра
self и то, почему он всегда должен быть первым
в любом методе объекта, который вы пишете.
Вызывая метод, вы не должны передавать
значение для self явно — интерпретатор сделает
это за вас.
16.
• Вообще говоря, self — обычная переменная,которая может называться по-другому. Но
так категорически не рекомендуется делать:
соглашение об имени контекстного объекта
— самое строгое из всех соглашений в мире
Python. Его выполняют 99,9 %
программистов. Если нарушить это
соглашение, другие программисты просто
не будут понимать ваш код. Кроме того,
некоторые текстовые редакторы
подсвечивают слово self цветом, и это
удобно.
17.
class Greeter:def hello_world(self):
print("Привет, Мир!")
def greeting(self, name):
'''Поприветствовать человека с именем
name.'''
print(f"Привет, {name}!")
def start_talking(self, name, weather_is_good):
'''Поприветствовать и начать разговор с
вопроса о погоде.'''
print(f"Привет, {name}!")
if weather_is_good:
print("Хорошая погода, не так ли?")
else:
print("Отвратительная погода, не так ли?")
greet = Greeter()
greet.hello_world() # Привет, Мир!
greet.greeting("Петя") # Привет, Петя!
greet.start_talking("Саша", True)
# Привет, Саша!
# Хорошая погода, не так ли?
В примере выше мы не
передавали нашему
методу никаких
аргументов. Это довольно
скучно, ведь мы уже
умеем передавать
аргументы функциям.
Давайте расширим
пример и добавим в наш
класс два новых метода:
Значение self
автоматически получается
из объекта, на котором
сделан вызов метода, но
мы его пока никак не
используем.
18.
class Car:def start_engine(self):
engine_on = True # К сожалению, не
сработает
def drive_to(self, city):
if engine_on: # Ошибка NameError
print(f"Едем в город {city}.")
else:
print("Машина не заведена,
никуда не едем")
c = Car()
c.start_engine()
c.drive_to('Владивосток')
Давайте попробуем запоминать
информацию из предыдущих вызовов
методов. Напишем класс «Машина»,
которую, как известно, надо сначала
завести, а потом уже ехать:
Итак, первая версия класса «Машина»
специально сделана нерабочей, чтобы
показать, что переменные внутри методов
ведут себя точно так же, как и
переменные функций. То есть если мы
инициализируем переменную внутри
метода, то после его завершения все
созданные таким образом переменные
уничтожаются и оказываются недоступны
как следующему вызову этого же метода,
так и другим методам. Под
«уничтожением» мы понимаем
исчезновение самих переменных, а не
объектов, на которые они ссылаются. Если
ссылка на объект сохранилась где-нибудь
(например, мы вернули объект с
помощью return), он все еще доступен.
Если ссылок не осталось, объект будет
скоро переработан сборщиком мусора.
• Напомним, что такие переменные
называются локальными.
19.
Инициализация экземпляровкласса
• Но вернемся к методам.
Пора нашей машине
наконец поехать — и в
этом нам поможет
контекстный объект self.
Он общий для всех
методов класса, и именно
в нем мы с помощью
атрибутов сохраним
информацию о состоянии
двигателя:
class Car:
def start_engine(self):
self.engine_on = True
def drive_to(self, city):
if self.engine_on:
print(f"Едем в город
{city}.")
else:
print("Машина не
заведена, никуда не едем.")
20.
Теперь наша машина отлично заведется и поедет:car1 = Car()
car1.start_engine()
car1.drive_to('Владивосток') # Едем в город
Владивосток.
Однако одна проблема осталась. При попытке выехать
на незаведенной машине
car2 = Car()
car2.drive_to('Лиссабон')
вместо красивого сообщения о том, что незаведенная
машина не поедет, получим «падение» программы с
ошибкой AttributeError (отсутствие атрибута или
метода). Еще бы! Ведь атрибут создавался в методе
start_engine, а мы не вызвали его для объекта car2.
21.
• Кроме того, стоит добавить метод stop_engine,чтобы не только заводить машину, но и глушить
двигатель. Этот метод помог бы нам избежать
вышеуказанной ошибки, но странно глушить еще
не заведенную машину, чтобы избежать ошибки:
ведь интуитивно мы понимаем, что машина
должна создаваться с выключенным двигателем.
• Нет ли способа задать значение атрибута
engine_on по умолчанию? Да. Есть метод
__init__, который относится к группе так
называемых специальных методов, которые
имеют особое значение для интерпретатора
Python.
22.
Метод __init__Особое значение метода __init__
заключается в том, что, если такой
метод в классе определен,
интерпретатор автоматически
вызывает его при создании
каждого экземпляра этого класса
для инициализации экземпляра.
Давайте воспользуемся этим,
чтобы при создании объекта
создать атрибут engine_on и
записать в него False.
class Car:
def __init__(self):
self.engine_on = False
def start_engine(self):
self.engine_on = True
def drive_to(self, city):
if self.engine_on:
print(f"Едем в город {city}.")
else:
print("Машина не заведена, никуда
не едем.")
car1 = Car()
car1.start_engine()
car1.drive_to('Владивосток') # Едем в
город Владивосток.
car2 = Car()
car2.drive_to('Лиссабон') # Машина не
заведена, никуда не едем.
23.
Метод __init__ после self может
получать параметры, передаваемые
ему при создании экземпляра:
car1 = Car('красная') # Создали машину
красного цвета
class Car:
def __init__(self, color):
self.engine_on = False
self.color = color
def start_engine(self):
self.engine_on = True
def drive_to(self, city):
if self.engine_on:
print(f"{self.color} машина едет в
город {city}.")
else:
print(f"{self.color} машина не
заведена, никуда не едем.")
car2 = Car('синяя') # И еще одну синего
car1.start_engine()
# Обратите внимание, что мы завели только
одну машину,
# ту, на которую ссылается car1 (красную).
# car2 -- это другой объект, он не изменится.
car1.drive_to('Владивосток')
# красная машина едет в город Владивосток.
car2.drive_to('Лиссабон')
# синяя машина не заведена, никуда не едем.
24.
• Важно!• Еще раз обратите внимание на комментарии в тексте.
Они показывают, что при записи атрибутов в self метод
изменяет только свой контекстный объект. Мы знаем,
что объекты класса совместно используют код методов
класса (то есть поведение), но хранят свои собственные
копии всех атрибутов данных (то есть состояние). Это
достигается за счет связывания значений атрибутов с
объектом, то есть с self.
• Обсудим еще один вопрос: зачем нам понадобился
метод start_engine, ведь его можно было бы заменить
строчкой car.engine_on = True? Казалось бы, это лишнее
усложнение. На самом деле нет. При дальнейшей
разработке нашей программы может оказаться, что
завести двигатель можно только в машине, в которой
есть бензин. Если бы мы в нескольких десятках мест
программы написали car.engine_on = True, нам пришлось
бы найти все эти места и вставить в них проверку на
наличие бензина в баке. А с методом start_engine мы
можем изменить только этот метод.
25.
• Инкапсуляция• Такая технология сокрытия информации о внутреннем
устройстве объекта за внешним интерфейсом из
методов называется инкапсуляцией. Надо стараться
делать интерфейс методов достаточно полным. Тогда
вы, как и другие программисты, будете пользоваться
этими методами, а изменения в атрибутах не будут
расползаться по коду, использующему ваш класс.
Кроме того, инкапсуляция позволяет шире
использовать такое понятие, как полиморфизм. О нем
мы поговорим на следующем уроке.
• В некоторых языках программирования автор класса
может закрыть доступ к атрибутам извне класса и
заставить всех использующих его программистов
работать только с методами. К сожалению, в Python
так делать нельзя, однако стоит по возможности
пользоваться только методами.
26.
Соглашения об именовании, вызовметодов атрибутов
• Давайте разберемся еще с одним примером. Напишем
класс робота-почтальона, который должен разносить
письма в определенные дома и квартиры. (Для
простоты считаем, что робот обслуживает одну улицу, и
не будем ее указывать.) Класс назовем длинным
именем RoboticMailDelivery, чтобы показать, как в
Python принято называть классы с длинным составным
именем.
• PEP 8
• Имя класса должно начинаться с большой буквы, между
словами не должно быть прочерка, каждое слово
внутри имени должно начинаться с большой буквы.
• Например, RoboticMailDelivery.
27.
Текст примера:class RoboticMailDelivery:
def __init__(self):
self.house_flat_pairs = []
Метод add_mail добавляет кортеж
(номер_дома, номер_квартиры) в
список-атрибут с помощью метода
append. Как видно, вызовы методов
для объектов-атрибутов производят
обычным образом, вызов метода
def add_mail(self, house_number,
дописывается справа от объекта:
flat_number):
self.house_flat_pairs.append(...).
'''Добавить информацию о доставке
PEP 8
письма по номеру дома
• Для имен атрибутов и методов
house_number, квартирe flat_number.'''
применяются те же правила, что и для
имен переменных и функций. Имя
self.house_flat_pairs.append((house_number,
должно быть записано в нижнем
flat_number))
регистре, слова внутри имени
разделяются подчеркиванием:
flat_numbers_for_house,
def flat_numbers_for_house(self,
house_flat_pairs.
house_number):
Важно!
'''Вернуть список квартир в доме
• Документация с описанием методов
house_number,
записывается в '''многострочных
в которые нужно доставить письма.'''
строках''' перед первой инструкцией
flat_numbers = []
как в функциях, так и в методах.
for h, f in self.house_flat_pairs:
if h == house_number:
flat_numbers.append(f)
return flat_numbers
28.
Смотрим• https://www.youtube.com/watch?v=Z7AY41tE
-3U&list=PLA0M1Bcd0w8zPwP7tFgwONhZOHt9rz9E
• https://www.youtube.com/watch?v=XmCAGU
o5k70
• https://www.youtube.com/watch?v=ChEdFh7
Q-Vw