HTML Diff
2 added 2 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#статьи</a></p>
1 <p><a>#статьи</a></p>
2 <ul><li>8 фев 2023</li>
2 <ul><li>8 фев 2023</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>Самая популярная парадигма современной разработки: обучаем питонистов на кошечках, напитках и вечеринках.</p>
4 </ul><p>Самая популярная парадигма современной разработки: обучаем питонистов на кошечках, напитках и вечеринках.</p>
5 <p>Иллюстрация: Катя Павловская для Skillbox Media</p>
5 <p>Иллюстрация: Катя Павловская для Skillbox Media</p>
6 <p>Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.</p>
6 <p>Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.</p>
7 <p>Объектно-ориентированное программирование применяют практически все крупные компании, потому что эта методика упрощает разработку. Но в то же время её боятся многие начинающие разработчики. Поэтому в этой статье мы покажем, что это на самом деле не так уж и сложно.</p>
7 <p>Объектно-ориентированное программирование применяют практически все крупные компании, потому что эта методика упрощает разработку. Но в то же время её боятся многие начинающие разработчики. Поэтому в этой статье мы покажем, что это на самом деле не так уж и сложно.</p>
8 - <p>Краеугольное понятие в ООП - объект. Это такой своеобразный контейнер, в котором сложены данные и прописаны действия, которые можно с этими анными совершать.</p>
8 + <p>Краеугольное понятие в ООП - объект. Это такой своеобразный контейнер, в котором сложены данные и прописаны действия, которые можно с этими данными совершать.</p>
9 <p>Чтобы понять, чем объекты так полезны и для чего их изобрели, сравним ООП с другой методикой разработки - процедурной. В ней весь код можно поделить на два вида: основную программу и вспомогательные функции, которые могут вызываться как программой, так и другими функциями:</p>
9 <p>Чтобы понять, чем объекты так полезны и для чего их изобрели, сравним ООП с другой методикой разработки - процедурной. В ней весь код можно поделить на два вида: основную программу и вспомогательные функции, которые могут вызываться как программой, так и другими функциями:</p>
10 <em>Инфографика: Skillbox Media</em><p>У такого программирования есть существенный недостаток - части кода сильно зависят друг от друга. Например, основная программа вызывает функцию, та вызывает вторую, та, в свою очередь, - третью. При этом, допустим, вторую функцию могут параллельно вызывать ещё несколько других, а также основная программа. Схематически вся эта процедурная путаница представлена на рисунке:</p>
10 <em>Инфографика: Skillbox Media</em><p>У такого программирования есть существенный недостаток - части кода сильно зависят друг от друга. Например, основная программа вызывает функцию, та вызывает вторую, та, в свою очередь, - третью. При этом, допустим, вторую функцию могут параллельно вызывать ещё несколько других, а также основная программа. Схематически вся эта процедурная путаница представлена на рисунке:</p>
11 <em>Инфографика: Skillbox Media</em><p>Если мы изменим какую-нибудь функцию, то остальные части кода могут быть к этому не готовы - и сломаются. Тогда придётся переписывать ещё и их, а они, в свою очередь, завязаны на другие функции. В общем, проще будет написать новую программу с нуля.</p>
11 <em>Инфографика: Skillbox Media</em><p>Если мы изменим какую-нибудь функцию, то остальные части кода могут быть к этому не готовы - и сломаются. Тогда придётся переписывать ещё и их, а они, в свою очередь, завязаны на другие функции. В общем, проще будет написать новую программу с нуля.</p>
12 <p>Кроме того, в процедурном программировании нередко приходится дублировать код и писать похожие функции с небольшими различиями. Например, чтобы поддерживать совместимость разных частей программы друг с другом.</p>
12 <p>Кроме того, в процедурном программировании нередко приходится дублировать код и писать похожие функции с небольшими различиями. Например, чтобы поддерживать совместимость разных частей программы друг с другом.</p>
13 <p>Логика ООП совершенно иная: к основной программе подключаются не функции, а объекты, внутри которых уже лежат собственные переменные и функции. Так выстраивается более иерархичная структура. Переменные внутри объектов называются полями, или<strong>атрибутами</strong>, а функции -<strong>методами</strong>.</p>
13 <p>Логика ООП совершенно иная: к основной программе подключаются не функции, а объекты, внутри которых уже лежат собственные переменные и функции. Так выстраивается более иерархичная структура. Переменные внутри объектов называются полями, или<strong>атрибутами</strong>, а функции -<strong>методами</strong>.</p>
14 <em>Инфографика: Skillbox Media</em><p>Объекты независимы друг от друга и самодостаточны, так что, если мы сломаем что-то в одном объекте, это никак не отразится на других. Более того: даже если мы полностью изменим содержание объекта, но сохраним его поведение, весь код продолжит работать.</p>
14 <em>Инфографика: Skillbox Media</em><p>Объекты независимы друг от друга и самодостаточны, так что, если мы сломаем что-то в одном объекте, это никак не отразится на других. Более того: даже если мы полностью изменим содержание объекта, но сохраним его поведение, весь код продолжит работать.</p>
15 <p>Каждый объект в ООП строится по определённому классу - абстрактной модели, описывающей, из чего состоит объект и что с ним можно делать.</p>
15 <p>Каждый объект в ООП строится по определённому классу - абстрактной модели, описывающей, из чего состоит объект и что с ним можно делать.</p>
16 <p>Например, у нас есть класс "Кошка", обладающий атрибутами "порода", "окрас", "возраст" и методами "мяукать", "мурчать", "умываться", "спать". Присваивая атрибутам определённые значения, можно создавать вполне конкретные объекты.</p>
16 <p>Например, у нас есть класс "Кошка", обладающий атрибутами "порода", "окрас", "возраст" и методами "мяукать", "мурчать", "умываться", "спать". Присваивая атрибутам определённые значения, можно создавать вполне конкретные объекты.</p>
17 <p>Допустим:</p>
17 <p>Допустим:</p>
18 <ul><li>Порода = абиссинская.</li>
18 <ul><li>Порода = абиссинская.</li>
19 <li>Окрас = рыжий.</li>
19 <li>Окрас = рыжий.</li>
20 <li>Возраст = 4.</li>
20 <li>Возраст = 4.</li>
21 </ul><p>Таким образом мы можем создать сколь угодно много разных кошек:</p>
21 </ul><p>Таким образом мы можем создать сколь угодно много разных кошек:</p>
22 <em>Инфографика: Skillbox Media</em><p>При этом любой объект класса "Кошка" (неважно, рыжая она, серая или чёрная) будет мяукать, мурчать, умываться и спать - если мы пропишем соответствующие методы.</p>
22 <em>Инфографика: Skillbox Media</em><p>При этом любой объект класса "Кошка" (неважно, рыжая она, серая или чёрная) будет мяукать, мурчать, умываться и спать - если мы пропишем соответствующие методы.</p>
23 <p>Всё объектно-ориентированное программирование строится на четырёх понятиях: инкапсуляции, наследовании, полиморфизме и абстракциях. Поэтому давайте объявим наш класс "Кошка" и будем объяснять ООП на нём:</p>
23 <p>Всё объектно-ориентированное программирование строится на четырёх понятиях: инкапсуляции, наследовании, полиморфизме и абстракциях. Поэтому давайте объявим наш класс "Кошка" и будем объяснять ООП на нём:</p>
24 class Cat(): def __init__(self, breed, color, age): self.breed = breed self.color = color self.age = age def meow(self): print('Мяу!') def purr(self): print('Мрррр')<p>Метод __init__ - инициализатор класса. Он вызывается сразу после создания объекта, чтобы присваивать значения динамическим атрибутам. self - ссылка на текущий объект, она даёт доступ к атрибутам и методам, с которыми вы работаете. Её аналог в других языках программирования - this.</p>
24 class Cat(): def __init__(self, breed, color, age): self.breed = breed self.color = color self.age = age def meow(self): print('Мяу!') def purr(self): print('Мрррр')<p>Метод __init__ - инициализатор класса. Он вызывается сразу после создания объекта, чтобы присваивать значения динамическим атрибутам. self - ссылка на текущий объект, она даёт доступ к атрибутам и методам, с которыми вы работаете. Её аналог в других языках программирования - this.</p>
25 <p><strong>Примечание 1.</strong>Слово self общепринятое, но не обязательное, вместо него можно использовать любое другое. Однако это может запутать тех, кто будет читать ваш код.</p>
25 <p><strong>Примечание 1.</strong>Слово self общепринятое, но не обязательное, вместо него можно использовать любое другое. Однако это может запутать тех, кто будет читать ваш код.</p>
26 <p><strong>Примечание 2.</strong>Названия классов принято писать с прописной буквы, а объектов - со строчной.</p>
26 <p><strong>Примечание 2.</strong>Названия классов принято писать с прописной буквы, а объектов - со строчной.</p>
27 <p>Итак, мы создали класс Cat, в котором объявили три атрибута: порода - breed, цвет - color и возраст - age. А ещё добавили два метода, чтобы наша кошка умела мяукать - meow() и мурчать - purr().</p>
27 <p>Итак, мы создали класс Cat, в котором объявили три атрибута: порода - breed, цвет - color и возраст - age. А ещё добавили два метода, чтобы наша кошка умела мяукать - meow() и мурчать - purr().</p>
28 <p>Давайте создадим пару объектов нашего класса:</p>
28 <p>Давайте создадим пару объектов нашего класса:</p>
29 cat1 = Cat('Абиссинская', 'Рыжая', 4) cat2 = Cat('Британская', 'Серая', 2)<p>Отлично, теперь, когда у нас есть основа, приступим к изучению принципов ООП.</p>
29 cat1 = Cat('Абиссинская', 'Рыжая', 4) cat2 = Cat('Британская', 'Серая', 2)<p>Отлично, теперь, когда у нас есть основа, приступим к изучению принципов ООП.</p>
30 <p>Доступ к данным объекта должен контролироваться, чтобы пользователь не мог изменить их в произвольном порядке и что-то поломать. Поэтому для работы с данными программисты пишут методы, которые можно будет использовать вне класса и которые ничего не сломают внутри.</p>
30 <p>Доступ к данным объекта должен контролироваться, чтобы пользователь не мог изменить их в произвольном порядке и что-то поломать. Поэтому для работы с данными программисты пишут методы, которые можно будет использовать вне класса и которые ничего не сломают внутри.</p>
31 <p>Вернёмся к нашим кошечкам. Мы можем разрешить изменять атрибут "возраст", но только в большую сторону, а атрибуты "порода" и "цвет" лучше открыть только для чтения - ведь порода кошки не меняется, а цвет если и меняется, то не по её инициативе.</p>
31 <p>Вернёмся к нашим кошечкам. Мы можем разрешить изменять атрибут "возраст", но только в большую сторону, а атрибуты "порода" и "цвет" лучше открыть только для чтения - ведь порода кошки не меняется, а цвет если и меняется, то не по её инициативе.</p>
32 <p>В нашем классе "Кошка" мы сделали все атрибуты открытыми, поэтому давайте это исправим:</p>
32 <p>В нашем классе "Кошка" мы сделали все атрибуты открытыми, поэтому давайте это исправим:</p>
33 class Cat(): def __init__(self, breed, color, age): self._breed = breed self._color = color self._age = age @property def breed(self): return self._breed @property def color(self): return self._color @property def age(self): return self._age @age.setter def age(self, new_age): if new_age &gt; self._age: self._age = new_age return self._age<p>Код стал выглядеть немного сложнее, но мы сейчас всё объясним. Сначала мы сделали все атрибуты закрытыми с помощью символа _. Он говорит интерпретатору, что эта переменная будет доступна только внутри методов класса.</p>
33 class Cat(): def __init__(self, breed, color, age): self._breed = breed self._color = color self._age = age @property def breed(self): return self._breed @property def color(self): return self._color @property def age(self): return self._age @age.setter def age(self, new_age): if new_age &gt; self._age: self._age = new_age return self._age<p>Код стал выглядеть немного сложнее, но мы сейчас всё объясним. Сначала мы сделали все атрибуты закрытыми с помощью символа _. Он говорит интерпретатору, что эта переменная будет доступна только внутри методов класса.</p>
34 <p>Нам всё ещё нужно получать доступ к атрибутам, поэтому мы предоставляем его через @property и объявляем для каждого атрибута свой метод - breed, color, age. В каждом из этих методов мы возвращаем значение нашего закрытого атрибута. Это делает его доступным только для чтения.</p>
34 <p>Нам всё ещё нужно получать доступ к атрибутам, поэтому мы предоставляем его через @property и объявляем для каждого атрибута свой метод - breed, color, age. В каждом из этих методов мы возвращаем значение нашего закрытого атрибута. Это делает его доступным только для чтения.</p>
35 <p>И последнее - мы должны позволить пользователям увеличивать возраст кота. Для этого воспользуемся @age.setter и ещё раз объявим метод age, а внутри него напишем простое условие и вернём значение атрибута.</p>
35 <p>И последнее - мы должны позволить пользователям увеличивать возраст кота. Для этого воспользуемся @age.setter и ещё раз объявим метод age, а внутри него напишем простое условие и вернём значение атрибута.</p>
36 <p>Теперь создадим экземпляр класса:</p>
36 <p>Теперь создадим экземпляр класса:</p>
37 cat = Cat('Абиссинская', 'Рыжая', 4)<p>Выведем значения атрибутов:</p>
37 cat = Cat('Абиссинская', 'Рыжая', 4)<p>Выведем значения атрибутов:</p>
38 print(cat.breed) # Абиссинская print(cat.color) # Рыжая print(cat.age) # 4<p>И попробуем изменить атрибут age:</p>
38 print(cat.breed) # Абиссинская print(cat.color) # Рыжая print(cat.age) # 4<p>И попробуем изменить атрибут age:</p>
39 cat.age = 5 print(cat.age) # 5<p>Всё успешно. А теперь сделаем это с другим атрибутом:</p>
39 cat.age = 5 print(cat.age) # 5<p>Всё успешно. А теперь сделаем это с другим атрибутом:</p>
40 cat.breed = 'Сиамская' print(cat.breed) # AttributeError: can't set attribute on line 34 in main.py<p>Мы получили ошибку, потому что запретили изменять этот атрибут.</p>
40 cat.breed = 'Сиамская' print(cat.breed) # AttributeError: can't set attribute on line 34 in main.py<p>Мы получили ошибку, потому что запретили изменять этот атрибут.</p>
41 <p>Классы могут передавать свои атрибуты и методы классам-потомкам. Например, мы хотим создать новый класс "Домашняя кошка". Он практически идентичен классу "Кошка", но у него появляются новые атрибуты "хозяин" и "кличка", а также метод "клянчить вкусняшку".</p>
41 <p>Классы могут передавать свои атрибуты и методы классам-потомкам. Например, мы хотим создать новый класс "Домашняя кошка". Он практически идентичен классу "Кошка", но у него появляются новые атрибуты "хозяин" и "кличка", а также метод "клянчить вкусняшку".</p>
42 <p>Достаточно объявить "Домашнюю кошку" наследником "Кошки" и прописать новые атрибуты и методы - вся остальная функциональность перейдёт от родителя к потомку.</p>
42 <p>Достаточно объявить "Домашнюю кошку" наследником "Кошки" и прописать новые атрибуты и методы - вся остальная функциональность перейдёт от родителя к потомку.</p>
43 <p>Давайте объявим новый класс:</p>
43 <p>Давайте объявим новый класс:</p>
44 class HomeCat(Cat): def __init__(self, breed, color, age, owner, name): super().__init__(breed, color, age) self._owner = owner self._name = name @property def owner(self): return self._owner @property def name(self): return self._name def getTreat(self): print('Мяу-мяу')<p>В первой строке мы как раз наследуем все методы и атрибуты класса Cat. А чтобы всё создалось корректно, мы должны вызвать метод super() в методе __init__() и через него заполнить атрибуты класса-родителя. Поэтому мы и передаём в этот метод "породу", "окрас" и "возраст".</p>
44 class HomeCat(Cat): def __init__(self, breed, color, age, owner, name): super().__init__(breed, color, age) self._owner = owner self._name = name @property def owner(self): return self._owner @property def name(self): return self._name def getTreat(self): print('Мяу-мяу')<p>В первой строке мы как раз наследуем все методы и атрибуты класса Cat. А чтобы всё создалось корректно, мы должны вызвать метод super() в методе __init__() и через него заполнить атрибуты класса-родителя. Поэтому мы и передаём в этот метод "породу", "окрас" и "возраст".</p>
45 <p>Кроме атрибутов для класса-родителя у класса-потомка есть и собственные атрибуты: "хозяин" - owner и "кличка" - name. Их мы будем использовать только в этом классе, поэтому они будут недоступны для класса-родителя.</p>
45 <p>Кроме атрибутов для класса-родителя у класса-потомка есть и собственные атрибуты: "хозяин" - owner и "кличка" - name. Их мы будем использовать только в этом классе, поэтому они будут недоступны для класса-родителя.</p>
46 <p>Мы сразу сделали атрибуты класса-потомка закрытыми и объявили для них собственные методы. А также добавили метод<strong> </strong>getTreat(), которого нет в классе-родителе.</p>
46 <p>Мы сразу сделали атрибуты класса-потомка закрытыми и объявили для них собственные методы. А также добавили метод<strong> </strong>getTreat(), которого нет в классе-родителе.</p>
47 <p>Давайте создадим объект класса:</p>
47 <p>Давайте создадим объект класса:</p>
48 my_cat = HomeCat('Сиамская', 'Белая', 3, 'Иван', 'Роза') print(my_cat.owner) print(my_cat.breed) my_cat.getTreat() # Мяу-мяу my_cat.purr() # Мрррр<p>Как видим, у нас работают и новые методы, и старые. Наследование прошло успешно.</p>
48 my_cat = HomeCat('Сиамская', 'Белая', 3, 'Иван', 'Роза') print(my_cat.owner) print(my_cat.breed) my_cat.getTreat() # Мяу-мяу my_cat.purr() # Мрррр<p>Как видим, у нас работают и новые методы, и старые. Наследование прошло успешно.</p>
49 <p>Этот принцип позволяет применять одни и те же команды к объектам разных классов, даже если они выполняются по-разному. Например, помимо класса "Кошка", у нас есть никак не связанный с ним класс "Попугай" - и у обоих есть метод "спать". Несмотря на то что кошки и попугаи спят по-разному (кошка сворачивается клубком, а попугай сидит на жёрдочке), для этих действий можно использовать одну команду.</p>
49 <p>Этот принцип позволяет применять одни и те же команды к объектам разных классов, даже если они выполняются по-разному. Например, помимо класса "Кошка", у нас есть никак не связанный с ним класс "Попугай" - и у обоих есть метод "спать". Несмотря на то что кошки и попугаи спят по-разному (кошка сворачивается клубком, а попугай сидит на жёрдочке), для этих действий можно использовать одну команду.</p>
50 <p>Допустим у нас есть два класса - "Кошка" и "Попугай":</p>
50 <p>Допустим у нас есть два класса - "Кошка" и "Попугай":</p>
51 class Cat: def sleep(self): print('Свернулся в клубок и сладко спит.') class Parrot: def sleep(self): print('Сел на жёрдочку и уснул.')<p>А теперь пусть у нас есть метод, который ожидает, что ему на вход придёт объект, у которого будет метод sleep:</p>
51 class Cat: def sleep(self): print('Свернулся в клубок и сладко спит.') class Parrot: def sleep(self): print('Сел на жёрдочку и уснул.')<p>А теперь пусть у нас есть метод, который ожидает, что ему на вход придёт объект, у которого будет метод sleep:</p>
52 def homeSleep(animal): animal.sleep()<p>Посмотрим, как это будет работать:</p>
52 def homeSleep(animal): animal.sleep()<p>Посмотрим, как это будет работать:</p>
53 cat = Cat() parrot = Parrot() homeSleep(cat) # Свернулся в клубок и сладко спит. homeSleep(parrot) # Сел на жёрдочку и уснул.<p>Хотя классы разные, их одноимённые методы работают похожим образом. Это и есть полиморфизм.</p>
53 cat = Cat() parrot = Parrot() homeSleep(cat) # Свернулся в клубок и сладко спит. homeSleep(parrot) # Сел на жёрдочку и уснул.<p>Хотя классы разные, их одноимённые методы работают похожим образом. Это и есть полиморфизм.</p>
54 <p>При создании класса мы упрощаем его до тех атрибутов и методов, которые нужны именно в этом коде, не пытаясь описать его целиком и отбрасывая всё второстепенное. Например, у всех хищников есть метод "охотиться", поэтому все животные, которые являются хищниками, автоматически будут уметь охотиться.</p>
54 <p>При создании класса мы упрощаем его до тех атрибутов и методов, которые нужны именно в этом коде, не пытаясь описать его целиком и отбрасывая всё второстепенное. Например, у всех хищников есть метод "охотиться", поэтому все животные, которые являются хищниками, автоматически будут уметь охотиться.</p>
55 <p>Рассмотрим класс Predator:</p>
55 <p>Рассмотрим класс Predator:</p>
56 class Predator: def hunt(self): print('Охотится...')<p>Этот класс будет общим для всех животных, которые являются хищниками, - например, кошек:</p>
56 class Predator: def hunt(self): print('Охотится...')<p>Этот класс будет общим для всех животных, которые являются хищниками, - например, кошек:</p>
57 class Cat(Predator): def __init__(self, name, color): super().__init__() self._name = name self._color = color @property def name(self): return self._name @property def color(self): return self._color<p>У кошки есть свои атрибуты: "имя" - name и "окрас" - color. Но при этом она потомок хищников, а значит, умеет охотиться:</p>
57 class Cat(Predator): def __init__(self, name, color): super().__init__() self._name = name self._color = color @property def name(self): return self._name @property def color(self): return self._color<p>У кошки есть свои атрибуты: "имя" - name и "окрас" - color. Но при этом она потомок хищников, а значит, умеет охотиться:</p>
58 cat = Cat('Даниэла', 'Чёрный') cat.hunt() # Охотится…<p>Давайте ещё пофантазируем и посоздаём классы.</p>
58 cat = Cat('Даниэла', 'Чёрный') cat.hunt() # Охотится…<p>Давайте ещё пофантазируем и посоздаём классы.</p>
59 <p>Представьте ситуацию: вашего друга пригласили на пафосную вечеринку в закрытый клуб. Там довольно странный этикет: в разное время все должны пить строго определённые напитки. Причём любой из них, в зависимости от ситуации, все пьют определённым способом: или обычными глотками по 20 мл, или маленькими по 10, или залпом всё, что осталось. Более того: размер глотка для одного и того же напитка может внезапно поменяться по ходу вечеринки.</p>
59 <p>Представьте ситуацию: вашего друга пригласили на пафосную вечеринку в закрытый клуб. Там довольно странный этикет: в разное время все должны пить строго определённые напитки. Причём любой из них, в зависимости от ситуации, все пьют определённым способом: или обычными глотками по 20 мл, или маленькими по 10, или залпом всё, что осталось. Более того: размер глотка для одного и того же напитка может внезапно поменяться по ходу вечеринки.</p>
60 <p>Вы выучиваете все эти дурацкие правила и вызываетесь помочь другу, но общаться с ним можете только через микронаушник. Таким образом, друг становится интерфейсом вашего взаимодействия с напитками.</p>
60 <p>Вы выучиваете все эти дурацкие правила и вызываетесь помочь другу, но общаться с ним можете только через микронаушник. Таким образом, друг становится интерфейсом вашего взаимодействия с напитками.</p>
61 <p>Для начала создадим класс Drink:</p>
61 <p>Для начала создадим класс Drink:</p>
62 class Drink: pass # Пока ставим заглушку вместо будущего кода.<p>У любого напитка есть атрибуты: название, стоимость в рублях и объём в миллилитрах. Предположим для простоты, что на нашей вечеринке принято всегда пить из посуды одинакового объёма (200 мл), а остальные атрибуты могут меняться от напитка к напитку.</p>
62 class Drink: pass # Пока ставим заглушку вместо будущего кода.<p>У любого напитка есть атрибуты: название, стоимость в рублях и объём в миллилитрах. Предположим для простоты, что на нашей вечеринке принято всегда пить из посуды одинакового объёма (200 мл), а остальные атрибуты могут меняться от напитка к напитку.</p>
63 <p>Соответственно, объём - это статический атрибут, неизменный во всех объектах класса. Название и стоимость, напротив, - динамические: они принадлежат не всему классу в целом, а конкретному объекту, и их значение определяется уже после его создания.</p>
63 <p>Соответственно, объём - это статический атрибут, неизменный во всех объектах класса. Название и стоимость, напротив, - динамические: они принадлежат не всему классу в целом, а конкретному объекту, и их значение определяется уже после его создания.</p>
64 class Drink: # Присваиваем значение статическому атрибуту. volume = 200 # Создаём метод для инициализации объекта. def __init__ (self, name, price): # Присваиваем значения динамическим атрибутам. self.name = name self.price = price<p>Создадим объект coffee - экземпляр класса Drink. В нашем примере создание нового объекта обозначает заказ нового напитка:</p>
64 class Drink: # Присваиваем значение статическому атрибуту. volume = 200 # Создаём метод для инициализации объекта. def __init__ (self, name, price): # Присваиваем значения динамическим атрибутам. self.name = name self.price = price<p>Создадим объект coffee - экземпляр класса Drink. В нашем примере создание нового объекта обозначает заказ нового напитка:</p>
65 coffee = Drink ('Кофе', 300)<p>Теперь у нас есть объект coffee, который содержит статический атрибут volume, полученный от класса Drink, и динамические атрибуты name и price, которые мы указали при создании объекта. Давайте попробуем к ним обратиться:</p>
65 coffee = Drink ('Кофе', 300)<p>Теперь у нас есть объект coffee, который содержит статический атрибут volume, полученный от класса Drink, и динамические атрибуты name и price, которые мы указали при создании объекта. Давайте попробуем к ним обратиться:</p>
66 print (coffee.name) &gt;&gt;&gt; Кофе print (coffee.price) &gt;&gt;&gt; 300 print (coffee.volume) &gt;&gt;&gt; 200<p>Так как статические атрибуты определяются на уровне класса, то и обращаться к ним можно не только через объект, но и через сам класс:</p>
66 print (coffee.name) &gt;&gt;&gt; Кофе print (coffee.price) &gt;&gt;&gt; 300 print (coffee.volume) &gt;&gt;&gt; 200<p>Так как статические атрибуты определяются на уровне класса, то и обращаться к ним можно не только через объект, но и через сам класс:</p>
67 Drink.volume &gt;&gt;&gt; 200<p>К динамическим атрибутам мы так обратиться не сможем.</p>
67 Drink.volume &gt;&gt;&gt; 200<p>К динамическим атрибутам мы так обратиться не сможем.</p>
68 <p>Итак, напиток заказан, и с ним нужно что-то делать. Так как вы общаетесь через микронаушник, то не видите, в каком состоянии напиток друга. Что ж, попросим друга сообщить вам об этом. Для этого добавим ещё один метод внутри класса Drink:</p>
68 <p>Итак, напиток заказан, и с ним нужно что-то делать. Так как вы общаетесь через микронаушник, то не видите, в каком состоянии напиток друга. Что ж, попросим друга сообщить вам об этом. Для этого добавим ещё один метод внутри класса Drink:</p>
69 class Drink: volume = 200 def __init__ (self, name, price): self.name = name self.price = price # Метод, чтобы запросить у друга информацию о напитке. def drink_info (self): print (f'Название: {self.name}. Стоимость: {self.price}. Объём: {self.volume}') # Создаём объект coffee. coffee = Drink ('Кофе', 300) # Обращаемся к методу drink_info через объект coffee. coffee.drink_info() &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Объём: 200<p>Тусовка делает первый глоток. Скомандуем другу, чтобы он присоединился. Для этого нужен ещё один динамический атрибут remains, информирующий нас, сколько миллилитров напитка осталось. Изначально остаток будет равен объёму посуды. После этого прописываем метод, указывающий товарищу, сколько конкретно глотать в соответствии с этикетом:</p>
69 class Drink: volume = 200 def __init__ (self, name, price): self.name = name self.price = price # Метод, чтобы запросить у друга информацию о напитке. def drink_info (self): print (f'Название: {self.name}. Стоимость: {self.price}. Объём: {self.volume}') # Создаём объект coffee. coffee = Drink ('Кофе', 300) # Обращаемся к методу drink_info через объект coffee. coffee.drink_info() &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Объём: 200<p>Тусовка делает первый глоток. Скомандуем другу, чтобы он присоединился. Для этого нужен ещё один динамический атрибут remains, информирующий нас, сколько миллилитров напитка осталось. Изначально остаток будет равен объёму посуды. После этого прописываем метод, указывающий товарищу, сколько конкретно глотать в соответствии с этикетом:</p>
70 class Drink: volume = 200 def __init__ (self, name, price): self.name = name self.price = price # Устанавливаем начальное значение атрибута remains. self.remains = self.volume def drink_info (self): # Добавляем информацию об остатке напитка в метод drink_info. print (f'Название: {self.name}. Стоимость: {self.price}. Начальный объём: {self.volume}. Осталось: {self.remains}') # Метод, чтобы сказать другу сделать глоток. def sip (self): # Проверяем, достаточно ли напитка осталось. if self.remains &gt;= 20: self.remains -= 20 print ('Друг сделал глоток') # Если напитка не хватает, сообщаем об этом. else: print ('Не хватает напитка для полноценного глотка') coffee = Drink ('Кофе', 300) coffee.sip() # Говорим другу сделать глоток. coffee.drink_info() # Узнаём информацию о напитке. &gt;&gt;&gt; Друг сделал глоток &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Начальный объём: 200. Осталось: 180<p>Чтобы нам не приходилось каждый раз проверять, хватает ли напитка для нужного глотка, напишем служебный метод _is_enough. Затем перепишем метод sip и добавим методы small_sip и drink_all:</p>
70 class Drink: volume = 200 def __init__ (self, name, price): self.name = name self.price = price # Устанавливаем начальное значение атрибута remains. self.remains = self.volume def drink_info (self): # Добавляем информацию об остатке напитка в метод drink_info. print (f'Название: {self.name}. Стоимость: {self.price}. Начальный объём: {self.volume}. Осталось: {self.remains}') # Метод, чтобы сказать другу сделать глоток. def sip (self): # Проверяем, достаточно ли напитка осталось. if self.remains &gt;= 20: self.remains -= 20 print ('Друг сделал глоток') # Если напитка не хватает, сообщаем об этом. else: print ('Не хватает напитка для полноценного глотка') coffee = Drink ('Кофе', 300) coffee.sip() # Говорим другу сделать глоток. coffee.drink_info() # Узнаём информацию о напитке. &gt;&gt;&gt; Друг сделал глоток &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Начальный объём: 200. Осталось: 180<p>Чтобы нам не приходилось каждый раз проверять, хватает ли напитка для нужного глотка, напишем служебный метод _is_enough. Затем перепишем метод sip и добавим методы small_sip и drink_all:</p>
71 # Продолжаем дописывать методы класса Drink. # Служебный метод, чтобы узнать, достаточно ли напитка. def _is_enough (self, need): if self.remains &gt;= need and self.remains &gt; 0: return True print ('Осталось недостаточно напитка') return False # Говорим другу сделать глоток. def sip (self): if self._is_enough(20) == True: self.remains -= 20 print ('Друг сделал глоток') # Говорим другу сделать маленький глоток. def small_sip (self): if self._is_enough(10) == True: self.remains -= 10 print ('Друг сделал маленький глоток') # Говорим другу выпить напиток залпом. def drink_all (self): if self._is_enough(0) == True: self.remains = 0 print ('Друг выпил напиток залпом') coffee = Drink ('Кофе', 300) # Заказываем кофе. coffee.remains = 10 #Приравниваем остаток кофе к 10 мл. coffee.sip() # Пытаемся сделать обычный глоток. coffee.learn_info() # Узнаём информацию о напитке. &gt;&gt;&gt; Осталось недостаточно напитка &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Начальный объём: 200. Осталось: 10<p>Обратите внимание ещё на такой нюанс: в строке<em>coffee.remains = 10</em>мы извне вмешались в объект и приравняли его атрибут<em>remains</em>к 10. Это удалось потому, что все атрибуты и методы в Python по умолчанию являются публичными, то есть доступными извне.</p>
71 # Продолжаем дописывать методы класса Drink. # Служебный метод, чтобы узнать, достаточно ли напитка. def _is_enough (self, need): if self.remains &gt;= need and self.remains &gt; 0: return True print ('Осталось недостаточно напитка') return False # Говорим другу сделать глоток. def sip (self): if self._is_enough(20) == True: self.remains -= 20 print ('Друг сделал глоток') # Говорим другу сделать маленький глоток. def small_sip (self): if self._is_enough(10) == True: self.remains -= 10 print ('Друг сделал маленький глоток') # Говорим другу выпить напиток залпом. def drink_all (self): if self._is_enough(0) == True: self.remains = 0 print ('Друг выпил напиток залпом') coffee = Drink ('Кофе', 300) # Заказываем кофе. coffee.remains = 10 #Приравниваем остаток кофе к 10 мл. coffee.sip() # Пытаемся сделать обычный глоток. coffee.learn_info() # Узнаём информацию о напитке. &gt;&gt;&gt; Осталось недостаточно напитка &gt;&gt;&gt; Название: Кофе. Стоимость: 300. Начальный объём: 200. Осталось: 10<p>Обратите внимание ещё на такой нюанс: в строке<em>coffee.remains = 10</em>мы извне вмешались в объект и приравняли его атрибут<em>remains</em>к 10. Это удалось потому, что все атрибуты и методы в Python по умолчанию являются публичными, то есть доступными извне.</p>
72 <p>Чтобы регулировать вмешательство во внутреннюю работу объекта, в ООП есть несколько уровней доступа:<strong>публичный</strong>(public),<strong>защищённый</strong>(protected) и <strong>приватный</strong>(private). Защищённые атрибуты и методы можно вызывать только внутри класса и его классов-наследников. Приватные - только внутри класса: даже наследники не имеют доступа к ним.</p>
72 <p>Чтобы регулировать вмешательство во внутреннюю работу объекта, в ООП есть несколько уровней доступа:<strong>публичный</strong>(public),<strong>защищённый</strong>(protected) и <strong>приватный</strong>(private). Защищённые атрибуты и методы можно вызывать только внутри класса и его классов-наследников. Приватные - только внутри класса: даже наследники не имеют доступа к ним.</p>
73 <p>В Python это реализовано следующим образом: перед защищёнными атрибутами и методами пишут одинарное нижнее подчёркивание (<em>_example</em>), перед приватными - двойное (<em>__example</em>). Именно это мы сделали в методе<em>_is_enough</em>. Одинарным нижним подчёркиванием мы объявили его защищённым.</p>
73 <p>В Python это реализовано следующим образом: перед защищёнными атрибутами и методами пишут одинарное нижнее подчёркивание (<em>_example</em>), перед приватными - двойное (<em>__example</em>). Именно это мы сделали в методе<em>_is_enough</em>. Одинарным нижним подчёркиванием мы объявили его защищённым.</p>
74 <p>При этом в Python само по себе объявление атрибутов и методов защищёнными и приватными не ограничивает доступ к ним извне. Мы всё ещё можем вызвать метод<em>_is_enough</em>из любого места программы:</p>
74 <p>При этом в Python само по себе объявление атрибутов и методов защищёнными и приватными не ограничивает доступ к ним извне. Мы всё ещё можем вызвать метод<em>_is_enough</em>из любого места программы:</p>
75 # Вызываем метод _is_enough и спрашиваем его, осталось ли хотя бы 10 мл напитка. coffee._is_enough(10) &gt;&gt;&gt; True<p>Атрибуты и методы, объявленные приватными, вызвать напрямую уже нельзя, но есть обходной путь:</p>
75 # Вызываем метод _is_enough и спрашиваем его, осталось ли хотя бы 10 мл напитка. coffee._is_enough(10) &gt;&gt;&gt; True<p>Атрибуты и методы, объявленные приватными, вызвать напрямую уже нельзя, но есть обходной путь:</p>
76 # Создаём класс Drink с приватным атрибутом __volume. class Drink: __volume = 200 # Создаём экземпляр класса Drink. coffee = Drink() # Используем обходной путь, чтобы обратиться к приватному атрибуту. coffee._Drink__volume &gt;&gt;&gt; 200<p><strong>Примечание.</strong>Возможность игнорировать уровни доступа - нарушение важного для ООП принципа инкапсуляции. Поэтому, несмотря на наличие технической возможности, программисты, пишущие на Python, договорились не обращаться к защищённым и приватным методам откуда-то извне.</p>
76 # Создаём класс Drink с приватным атрибутом __volume. class Drink: __volume = 200 # Создаём экземпляр класса Drink. coffee = Drink() # Используем обходной путь, чтобы обратиться к приватному атрибуту. coffee._Drink__volume &gt;&gt;&gt; 200<p><strong>Примечание.</strong>Возможность игнорировать уровни доступа - нарушение важного для ООП принципа инкапсуляции. Поэтому, несмотря на наличие технической возможности, программисты, пишущие на Python, договорились не обращаться к защищённым и приватным методам откуда-то извне.</p>
77 <p>Так что и мы объявим защищёнными атрибуты<em>volume</em>и <em>remains</em>, чтобы помнить: ими стоит пользоваться только внутри класса<em>Drink</em>и его наследников. Теперь всё выглядит так:</p>
77 <p>Так что и мы объявим защищёнными атрибуты<em>volume</em>и <em>remains</em>, чтобы помнить: ими стоит пользоваться только внутри класса<em>Drink</em>и его наследников. Теперь всё выглядит так:</p>
78 class Drink: # Определяем статический атрибут. _volume = 200 # Вызываем инициализатор класса и определяем динамические атрибуты. def __init__ (self, name, price): self.name = name self.price = price self._remains = self._volume # Просим друга сообщить информацию о напитке. def drink_info (self): print (f'Название: {self.name}. Стоимость: {self.price}. Начальный объём: {self._volume}. Осталось: {self._remains}') # Служебный метод, позволяющий узнать, достаточно ли напитка. def _is_enough (self, need): if self._remains &gt;= need and self._remains &gt; 0: return True print ('Осталось недостаточно напитка') return False # Говорим другу сделать глоток. def sip (self): if self._is_enough(20) == True: self._remains -= 20 print ('Друг сделал глоток') # Говорим другу сделать маленький глоток. def small_sip (self): if self._is_enough(10) == True: self._remains -= 10 print ('Друг сделал маленький глоток') # Говорим другу выпить напиток залпом. def drink_all (self): if self._is_enough(0) == True: self._remains = 0 print ('Друг выпил напиток залпом')<p>Вечеринка идёт полным ходом. Но тут случается непредвиденное: ваш друг, который уже слегка приспособился к местным нравам и даже начал получать удовольствие, внезапно кричит вам в наушник, что вечер вновь перестаёт быть томным. "Они объявили время соков! - паникует он. - А у каждого сока свой вкус, тут чёрт ногу сломит!"</p>
78 class Drink: # Определяем статический атрибут. _volume = 200 # Вызываем инициализатор класса и определяем динамические атрибуты. def __init__ (self, name, price): self.name = name self.price = price self._remains = self._volume # Просим друга сообщить информацию о напитке. def drink_info (self): print (f'Название: {self.name}. Стоимость: {self.price}. Начальный объём: {self._volume}. Осталось: {self._remains}') # Служебный метод, позволяющий узнать, достаточно ли напитка. def _is_enough (self, need): if self._remains &gt;= need and self._remains &gt; 0: return True print ('Осталось недостаточно напитка') return False # Говорим другу сделать глоток. def sip (self): if self._is_enough(20) == True: self._remains -= 20 print ('Друг сделал глоток') # Говорим другу сделать маленький глоток. def small_sip (self): if self._is_enough(10) == True: self._remains -= 10 print ('Друг сделал маленький глоток') # Говорим другу выпить напиток залпом. def drink_all (self): if self._is_enough(0) == True: self._remains = 0 print ('Друг выпил напиток залпом')<p>Вечеринка идёт полным ходом. Но тут случается непредвиденное: ваш друг, который уже слегка приспособился к местным нравам и даже начал получать удовольствие, внезапно кричит вам в наушник, что вечер вновь перестаёт быть томным. "Они объявили время соков! - паникует он. - А у каждого сока свой вкус, тут чёрт ногу сломит!"</p>
79 - <p>Действительно. Хьюстон, у нас проблемы. Сок, на первый взгляд, - напиток как напиток: его тоже можно пить глотками и залпом, у него есть цена и объём. Но, как пел гражданин Шнуров, есть один момент: в отличие от любого напитка, у сока появляется новый, специфический атрибут, который не поддерживается классом<em>Drink,</em> - вкус фрукта или ягоды, из которых он выжат.</p>
79 + <p>Дйствительно. Хьюстон, у нас проблемы. Сок, на первый взгляд, - напиток как напиток: его тоже можно пить глотками и залпом, у него есть цена и объём. Но, как пел гражданин Шнуров, есть один момент: в отличие от любого напитка, у сока появляется новый, специфический атрибут, который не поддерживается классом<em>Drink,</em> - вкус фрукта или ягоды, из которых он выжат.</p>
80 <p>Не паникуем: даже из самой сложной ситуации всегда есть как минимум два выхода. Можно, конечно, полностью скопировать класс<em>Drink</em>и изменить в этой копии всё, что нам нужно. Но мы поступим изящнее - создадим класс<em>Juice</em>и сделаем его наследником класса<em>Drink</em>:</p>
80 <p>Не паникуем: даже из самой сложной ситуации всегда есть как минимум два выхода. Можно, конечно, полностью скопировать класс<em>Drink</em>и изменить в этой копии всё, что нам нужно. Но мы поступим изящнее - создадим класс<em>Juice</em>и сделаем его наследником класса<em>Drink</em>:</p>
81 # Создаём класс-потомок и указываем в скобках родительский класс, от которого наследуем. class Juice (Drink): # Вызываем инициализатор класса и указываем в нём новый динамический атрибут taste. def __init__ (self, name, price, taste): # Вызываем конструктор класса-родителя и просим его определить значения динамических атрибутов, которые в нём прописаны. super().__init__ (name, price) # Определяем значение нового динамического атрибута taste. self.taste = taste<p><strong>Примечание.</strong>Обратите внимание, что из класса-потомка мы не можем напрямую обратиться к приватным атрибутам и методам класса-родителя.</p>
81 # Создаём класс-потомок и указываем в скобках родительский класс, от которого наследуем. class Juice (Drink): # Вызываем инициализатор класса и указываем в нём новый динамический атрибут taste. def __init__ (self, name, price, taste): # Вызываем конструктор класса-родителя и просим его определить значения динамических атрибутов, которые в нём прописаны. super().__init__ (name, price) # Определяем значение нового динамического атрибута taste. self.taste = taste<p><strong>Примечание.</strong>Обратите внимание, что из класса-потомка мы не можем напрямую обратиться к приватным атрибутам и методам класса-родителя.</p>
82 <p>Создаём объект класса<em>Juice</em>и вызываем в нём методы, унаследованные от родительского класса<em>Drink</em>:</p>
82 <p>Создаём объект класса<em>Juice</em>и вызываем в нём методы, унаследованные от родительского класса<em>Drink</em>:</p>
83 # Создаём экземпляр класса Juice. apple_juice = Juice ('сок', 250, 'яблочный') # Пробуем вызвать методы, прописанные в родительском классе Drink. apple_juice.small_sip() # Говорим другу сделать маленький глоток. apple_juice.sip() # Говорим другу сделать обычный глоток. apple_juice.drink_info() # Просим друга сообщить информацию о напитке. &gt;&gt;&gt; Друг сделал маленький глоток &gt;&gt;&gt; Друг сделал глоток &gt;&gt;&gt; Название: сок. Стоимость: 250. Начальный объём: 200. Осталось: 170<p>Родительский класс<em>Drink</em>поделился с потомком своими атрибутами и методами, так что нам не пришлось писать их заново.</p>
83 # Создаём экземпляр класса Juice. apple_juice = Juice ('сок', 250, 'яблочный') # Пробуем вызвать методы, прописанные в родительском классе Drink. apple_juice.small_sip() # Говорим другу сделать маленький глоток. apple_juice.sip() # Говорим другу сделать обычный глоток. apple_juice.drink_info() # Просим друга сообщить информацию о напитке. &gt;&gt;&gt; Друг сделал маленький глоток &gt;&gt;&gt; Друг сделал глоток &gt;&gt;&gt; Название: сок. Стоимость: 250. Начальный объём: 200. Осталось: 170<p>Родительский класс<em>Drink</em>поделился с потомком своими атрибутами и методами, так что нам не пришлось писать их заново.</p>
84 <p>Теперь посмотрим на атрибут<em>name</em>. В классе<em>Drink</em>, когда мы могли заказать что угодно, от кофе и чая до кваса и коктейля, имело смысл каждый раз указывать название напитка. Но в классе<em>Juice</em>название всегда будет одинаковым: "сок". Тогда зачем всё время при заказе сока спрашивать атрибут<em>name</em>?</p>
84 <p>Теперь посмотрим на атрибут<em>name</em>. В классе<em>Drink</em>, когда мы могли заказать что угодно, от кофе и чая до кваса и коктейля, имело смысл каждый раз указывать название напитка. Но в классе<em>Juice</em>название всегда будет одинаковым: "сок". Тогда зачем всё время при заказе сока спрашивать атрибут<em>name</em>?</p>
85 <p>Переопределим в классе<em>Juice</em>метод<em>__init__</em>: пусть значением атрибута name всегда будет "сок". И затем снова закажем яблочный сок:</p>
85 <p>Переопределим в классе<em>Juice</em>метод<em>__init__</em>: пусть значением атрибута name всегда будет "сок". И затем снова закажем яблочный сок:</p>
86 class Juice (Drink): # Создаём статический атрибут, который будет содержать название нашего напитка. _juice_name = 'сок' # Вызываем инициализатор класса и указываем в нём только те аргументы, которые запрашиваем при создании объекта. def __init__ (self, price, taste): # Передаём конструктору родительского класса значение атрибута __juice_name. super().__init__ (self._juice_name, price) self.taste = taste apple_juice = Juice (250, 'яблочный') # Создаём объект класса Juice.<p>Что же именно происходит при создании объекта<em>apple_juice</em>?</p>
86 class Juice (Drink): # Создаём статический атрибут, который будет содержать название нашего напитка. _juice_name = 'сок' # Вызываем инициализатор класса и указываем в нём только те аргументы, которые запрашиваем при создании объекта. def __init__ (self, price, taste): # Передаём конструктору родительского класса значение атрибута __juice_name. super().__init__ (self._juice_name, price) self.taste = taste apple_juice = Juice (250, 'яблочный') # Создаём объект класса Juice.<p>Что же именно происходит при создании объекта<em>apple_juice</em>?</p>
87 <p>1. Мы вызываем инициализатор класса Juice и в скобках передаём ему аргументы<em>price</em>и <em>taste.</em></p>
87 <p>1. Мы вызываем инициализатор класса Juice и в скобках передаём ему аргументы<em>price</em>и <em>taste.</em></p>
88 <p>2. Инициализатор класса<em>Juice</em>с помощью функции<em>super()</em>вызывает другой инициализатор - родительского класса<em>Drink.</em></p>
88 <p>2. Инициализатор класса<em>Juice</em>с помощью функции<em>super()</em>вызывает другой инициализатор - родительского класса<em>Drink.</em></p>
89 <p>3. Инициализатор класса<em>Drink</em>просит передать ему аргументы<em>name</em>и <em>price</em>. В качестве аргумента name он получает статический атрибут<em>_juice_name</em>, который мы прописали в классе<em>Juice</em>. А аргумент price подтягивается из инициализатора класса<em>Juice.</em></p>
89 <p>3. Инициализатор класса<em>Drink</em>просит передать ему аргументы<em>name</em>и <em>price</em>. В качестве аргумента name он получает статический атрибут<em>_juice_name</em>, который мы прописали в классе<em>Juice</em>. А аргумент price подтягивается из инициализатора класса<em>Juice.</em></p>
90 <p>4. В инициализаторе класса<em>Drink</em>присваиваются значения атрибутам<em>name</em>,<em>price</em>и <em>_remains.</em></p>
90 <p>4. В инициализаторе класса<em>Drink</em>присваиваются значения атрибутам<em>name</em>,<em>price</em>и <em>_remains.</em></p>
91 <p>5. В инициализаторе класса<em>Juice</em>присваивается значение атрибуту<em>taste</em>.</p>
91 <p>5. В инициализаторе класса<em>Juice</em>присваивается значение атрибуту<em>taste</em>.</p>
92 <p>Если вам всё ещё сложно сориентироваться, что откуда берётся и куда передаётся, посмотрите на эту схему. Разными цветами здесь обозначены пути, по которым атрибутам присваиваются их значения:</p>
92 <p>Если вам всё ещё сложно сориентироваться, что откуда берётся и куда передаётся, посмотрите на эту схему. Разными цветами здесь обозначены пути, по которым атрибутам присваиваются их значения:</p>
93 <em>Изображение: Skillbox Media</em><p>Итак, вы объяснили другу, что больше не надо каждый раз объявлять, что он хочет именно сок, - а то все подумают, что он деревенщина. Всем и так понятно, что он не компот заказывает. Но посмотрите, что происходит, когда мы просим его сообщить информацию об экземпляре класса Juice:</p>
93 <em>Изображение: Skillbox Media</em><p>Итак, вы объяснили другу, что больше не надо каждый раз объявлять, что он хочет именно сок, - а то все подумают, что он деревенщина. Всем и так понятно, что он не компот заказывает. Но посмотрите, что происходит, когда мы просим его сообщить информацию об экземпляре класса Juice:</p>
94 apple_juice = Juice (250, 'яблочный') apple_juice.drink_info() &gt;&gt;&gt; Название: сок. Стоимость: 250. Начальный объём: 200. Осталось: 200<p>Он сообщает нам, что пьёт сок, но не говорит, какой именно. Чтобы получить от друга дополнительную информацию, переопределим метод<em>drink_info</em>родительского класса:</p>
94 apple_juice = Juice (250, 'яблочный') apple_juice.drink_info() &gt;&gt;&gt; Название: сок. Стоимость: 250. Начальный объём: 200. Осталось: 200<p>Он сообщает нам, что пьёт сок, но не говорит, какой именно. Чтобы получить от друга дополнительную информацию, переопределим метод<em>drink_info</em>родительского класса:</p>
95 class Juice (Drink): # Определяем статический атрибут. _juice_name = 'сок' # Вызываем инициализатор класса и определяем динамические атрибуты. def __init__ (self, price, taste): super().__init__ (self._juice_name, price) self.taste = taste # Переопределяем родительский метод drink_info, чтобы он сообщал нам информацию о вкусе сока. def drink_info (self): print (f'Вкус сока: {self.taste}. Стоимость: {self.price}. Начальный объём: {self._volume}. Осталось: {self._remains}') # Создаём экземпляр класса Juice и вызываем метод learn_info. apple_juice = Juice (250, 'яблочный') apple_juice.drink_info() &gt;&gt;&gt; Вкус сока: яблочный. Стоимость: 250. Начальный объём: 200. Осталось: 200<p>Так мы реализовали принцип полиморфизма. Неважно, что пьёт наш друг - кофе или сок, мы можем запросить у него информацию о напитке одной и той же командой drink_info. И приятель уже сам будет ориентироваться по ситуации: если он пьёт сок, то сообщит нам его вкус, а если любой другой напиток - его название.</p>
95 class Juice (Drink): # Определяем статический атрибут. _juice_name = 'сок' # Вызываем инициализатор класса и определяем динамические атрибуты. def __init__ (self, price, taste): super().__init__ (self._juice_name, price) self.taste = taste # Переопределяем родительский метод drink_info, чтобы он сообщал нам информацию о вкусе сока. def drink_info (self): print (f'Вкус сока: {self.taste}. Стоимость: {self.price}. Начальный объём: {self._volume}. Осталось: {self._remains}') # Создаём экземпляр класса Juice и вызываем метод learn_info. apple_juice = Juice (250, 'яблочный') apple_juice.drink_info() &gt;&gt;&gt; Вкус сока: яблочный. Стоимость: 250. Начальный объём: 200. Осталось: 200<p>Так мы реализовали принцип полиморфизма. Неважно, что пьёт наш друг - кофе или сок, мы можем запросить у него информацию о напитке одной и той же командой drink_info. И приятель уже сам будет ориентироваться по ситуации: если он пьёт сок, то сообщит нам его вкус, а если любой другой напиток - его название.</p>
96 <p><strong>Примечание.</strong>Все классы в Python по умолчанию являются наследниками суперкласса<em>object</em>и наследуют его атрибуты и методы. Такими унаследованными методами, например, являются встроенные<em>__new__</em>,<em>__init__</em>,<em>__del__</em>и многие другие.</p>
96 <p><strong>Примечание.</strong>Все классы в Python по умолчанию являются наследниками суперкласса<em>object</em>и наследуют его атрибуты и методы. Такими унаследованными методами, например, являются встроенные<em>__new__</em>,<em>__init__</em>,<em>__del__</em>и многие другие.</p>
97 <p>Вечеринка потихоньку подходит к концу, и вашего товарища пока не спалили. Время соков прошло, и каждый теперь волен пить то, что пожелает. Вроде бы можно расслабиться. Но, как вы знаете, у нас и тамада хороший, и конкурсы интересные: посетителей внезапно огорошивают новой затеей. Рассаживайтесь, говорят, за столики в соответствии со стоимостью только что заказанного напитка. Все начинают выкрикивать, почём бокалы в их руках, а официанты отводят их на новые места. Друг снова в ступоре, но мы его спасём.</p>
97 <p>Вечеринка потихоньку подходит к концу, и вашего товарища пока не спалили. Время соков прошло, и каждый теперь волен пить то, что пожелает. Вроде бы можно расслабиться. Но, как вы знаете, у нас и тамада хороший, и конкурсы интересные: посетителей внезапно огорошивают новой затеей. Рассаживайтесь, говорят, за столики в соответствии со стоимостью только что заказанного напитка. Все начинают выкрикивать, почём бокалы в их руках, а официанты отводят их на новые места. Друг снова в ступоре, но мы его спасём.</p>
98 <p>Так как объявить стоимость можно для любого напитка, пропишем метод<em>tell_price</em>в классе<em>Drink</em> - и дочерний класс<em>Juice</em>автоматически унаследует его:</p>
98 <p>Так как объявить стоимость можно для любого напитка, пропишем метод<em>tell_price</em>в классе<em>Drink</em> - и дочерний класс<em>Juice</em>автоматически унаследует его:</p>
99 def tell_price (self): print (f'Друг объявляет стоимость своего напитка') return self.price<p>Теперь проверим, действительно ли он работает с объектами как класса<em>Drink</em>, так и класса<em>Juice</em>:</p>
99 def tell_price (self): print (f'Друг объявляет стоимость своего напитка') return self.price<p>Теперь проверим, действительно ли он работает с объектами как класса<em>Drink</em>, так и класса<em>Juice</em>:</p>
100 tea = Drink ('чай', 500) print (tea.tell_price()) # Сначала друг объявит стоимость чая. beetlejuice = Juice (1988, 'жучиный') print (beetlejuice.tell_price()) # Потом друг объявит стоимость жучиного сока. &gt;&gt;&gt; Друг объявляет стоимость своего напитка &gt;&gt;&gt; 500 &gt;&gt;&gt; Друг объявляет стоимость своего напитка &gt;&gt;&gt; 1988<p>Профит, коллеги: ваш друг уходит с вечеринки с новой подружкой и приглашением на следующее мероприятие. А всё благодаря вам и объектно-ориентированному программированию.</p>
100 tea = Drink ('чай', 500) print (tea.tell_price()) # Сначала друг объявит стоимость чая. beetlejuice = Juice (1988, 'жучиный') print (beetlejuice.tell_price()) # Потом друг объявит стоимость жучиного сока. &gt;&gt;&gt; Друг объявляет стоимость своего напитка &gt;&gt;&gt; 500 &gt;&gt;&gt; Друг объявляет стоимость своего напитка &gt;&gt;&gt; 1988<p>Профит, коллеги: ваш друг уходит с вечеринки с новой подружкой и приглашением на следующее мероприятие. А всё благодаря вам и объектно-ориентированному программированию.</p>
101 <p>Подведём краткие итоги:</p>
101 <p>Подведём краткие итоги:</p>
102 <ul><li>Объектно-ориентированное программирование - распространённая и эффективная парадигма, которая подходит для выполнения многих задач. Здесь основной строительной единицей программы является не функция, а объект, представляющий собой экземпляр некоторого класса.</li>
102 <ul><li>Объектно-ориентированное программирование - распространённая и эффективная парадигма, которая подходит для выполнения многих задач. Здесь основной строительной единицей программы является не функция, а объект, представляющий собой экземпляр некоторого класса.</li>
103 <li>ООП строится вокруг четырёх основных принципов: абстракция, инкапсуляция, наследование и полиморфизм.</li>
103 <li>ООП строится вокруг четырёх основных принципов: абстракция, инкапсуляция, наследование и полиморфизм.</li>
104 <li>Язык Python отлично поддерживает ООП. В нём всё является объектом, даже числа и сами классы. Тем не менее в Python есть баг с уровнями доступа, нарушающий принцип инкапсуляции. Но при ответственном подходе к работе с кодом это не должно стать проблемой.</li>
104 <li>Язык Python отлично поддерживает ООП. В нём всё является объектом, даже числа и сами классы. Тем не менее в Python есть баг с уровнями доступа, нарушающий принцип инкапсуляции. Но при ответственном подходе к работе с кодом это не должно стать проблемой.</li>
105 </ul><p>Python для всех</p>
105 </ul><p>Python для всех</p>
106 <p>Вы освоите Python на практике и создадите проекты для портфолио - телеграм-бот, веб-парсер и сайт с нуля. А ещё получите готовый план выхода на удалёнку и фриланс. Спикер - руководитель отдела разработки в "Сбере".</p>
106 <p>Вы освоите Python на практике и создадите проекты для портфолио - телеграм-бот, веб-парсер и сайт с нуля. А ещё получите готовый план выхода на удалёнку и фриланс. Спикер - руководитель отдела разработки в "Сбере".</p>
107 <p><a>Пройти бесплатно</a></p>
107 <p><a>Пройти бесплатно</a></p>
108 <a><b>Бесплатный курс по разработке на Python ➞</b>Пройдите бесплатный курс по Python и создайте с нуля телеграм-бот, веб-парсер и сайт. Спикер - руководитель отдела разработки в "Сбере". Пройти курс</a>
108 <a><b>Бесплатный курс по разработке на Python ➞</b>Пройдите бесплатный курс по Python и создайте с нуля телеграм-бот, веб-парсер и сайт. Спикер - руководитель отдела разработки в "Сбере". Пройти курс</a>