Меню
Видеоучебник

Программирование объектной модели

Урок 24. Основы алгоритмизации и программирования на языке Python

На этом уроке ученики продолжат изучение основных понятий объектно-ориентированного программирования и запрограммируют решение задачи, объектно-ориентированный анализ которой они провели на прошлом уроке. В процессе решения задачи ученики узнают о практическом применении понятий, изученных ранее.
Плеер: YouTube Вконтакте

Конспект урока "Программирование объектной модели"

Вопросы:

·     Поля и методы класса.

·     Программирование объектов и классов.

Ранее мы начали рассматривать задачу об исследовании урожайности дерева. Вспомним её. Нужно исследовать урожайность дерева, если известно, что каждый третий год у дерева отрастает новая ветка, которая способна выдерживать вес, равный пяти килограммам, и каждый год этот вес увеличивается на тридцать процентов. При этом на ветвях дерева растут фрукты со случайным весом от двухсот до пятисот граммов. В первый год на каждой ветви вырастает по одному фрукту, а в каждый следующий год количество фруктов на ветке увеличивается в два раза. Когда суммарный вес фруктов превышает вес, который может выдержать ветвь дерева, то ветвь ломается и фрукты, которые росли на ветке, приходят в негодность.

Мы построили объектную модель этой задачи и выделили в ней три класса объектов: «Дерево», «Ветвь» и «Фрукт». При этом нас интересуют такие свойства дерева, как его возраст и ветви. А также дерево должно уметь расти и вычислять свою урожайность. У ветвей дерева нас интересуют максимальный вес, который они могут выдержать, а также их фрукты. Ветвь должна уметь расти вместе с деревом и вычислять свою урожайность. У фруктов же нас интересует только их вес. При этом дерево должно уметь запрашивать у своих ветвей их урожайность, а ветвь должна уметь запрашивать вес у своих фруктов.

Сегодня мы реализуем эту объектную модель на языке Python, но прежде, чем начать, рассмотрим ещё несколько новых понятий. Ранее мы сделали вывод о том, что каждый объект – это экземпляр класса. То есть в классах описываются свойства объектов и их поведение. Для реализации свойств объектов в классах есть поля, а для реализации поведения – методы. Поле – это переменная, принадлежащая объекту, а метод – это функция, реализованная для объектов класса и принадлежащая этому классу.

Теперь рассмотрим, как реализовать объектную модель задачи в программе. Мы определили классы объектов, необходимые для решения задачи. Опишем эти классы. Начнём с класса «Фрукт». Описание любого класса в языке Python начинается со слова class, после которого следуют имя класса и двоеточие. Чтобы создать класс, не описывая его свойства и поведение, достаточно на следующей строке записать слово pass. Оно означает, что всё это будет описано позже.

Теперь, чтобы в основной программе создать объект класса «Фрукт», нужно объявить переменную, назовём её f, и присвоить ей значение, состоящее из имени класса – «Фрукт», а также пустых скобок. Таким образом, мы создали в основной программе объект класса «Фрукт» с именем f, или, иначе говоря, мы присвоили объявленной переменной f объект класса «Фрукт».

class Frukt:

    pass

 

f = Frukt ()

Подробнее рассмотрим, как это работает. Запись после знака равенства означает, что мы вызвали у класса «Фрукт» одноимённый метод. Однако вы можете справедливо возразить, что у класса «Фрукт» мы не описывали ни одного метода. Дело в том, что такой метод автоматически создаётся у любого описанного класса. Он называется «Конструктор» и вызывается в программе для того, чтобы создать объект этого класса.

Однако, в построенной нами объектной модели задачи указано, что у объектов класса «Фрукт» должно быть свойство «Вес». Чтобы определить его, в классе «Фрукт» опишем свой метод-конструктор, который заменит определённый по умолчанию. Для этого уберём слово pass, и на его месте опишем в составе класса функцию, имя которой состоит из слова __init__. Укажем у этой функции единственный параметр – self. Этим словом обозначается ссылка на созданный объект класса. Она означает, что будет изменяться именно этот объект. В этой функции объявим у объекта поле с названием Ves. Для этого запишем слово self, после которого, через точку, укажем имя поля – Ves. По условию задачи вес фрукта должен быть случайным на промежутке от 200 до 500 граммов. Чтобы получить случайное число за пределами класса, загрузим из модуля random функцию с именем uniform, которая возвращает случайное вещественное число на заданном промежутке. А теперь в описываемом методе присвоим полю Ves значение этой функции с параметрами, равными начальному и конечному значению промежутка, на котором нужно сгенерировать число. Нам нужно число, находящееся на промежутке от 200 до 500, включая концы.

Теперь у объекта f, принадлежащего классу Frukt, появилось поле Ves. К нему можно обратиться, записав имя объекта – f, и через точку после него – имя поля – Ves.

Если при создании объекта мы хотим задать значение одного из его полей из программы, то мы можем добавить в метод-конструктор ещё один параметр, а в самом методе присвоить соответствующему полю значение этого параметра. Таким образом мы завершили описание класса Frukt. На самом деле можно сказать, что класс – это структурный тип данных, описанный программистом, а объекты класса – это просто значения этого типа.

from random import uniform

class Frukt:

    def __init__ (self):

        Ves = uniform (200, 500)

Теперь перейдём к классу Vetv. Опишем метод-конструктор этого класса. Согласно объектной модели задачи, у объектов этого класса нас интересует максимальный вес, который могут выдержать ветвь и фрукты, растущие на ветви. Из условия задачи нам известно, что при появлении ветви, максимальный вес, который она может выдержать, равен 5 килограммам или 5000 граммов. Так как фруктов на ветви может быть много, присвоим в конструкторе полю Frukty список. При появлении ветви на ней должен быть всего 1 фрукт, поэтому в списке будет всего один объект класса Frukt, созданный с помощью описанного в этом классе конструктора.

class Vetv:

    def __init__ (self):

        self.MaxVes = 5000

        self.Frukty = [Frukt ()]

Также из объектной модели нам известно, что ветвь дерева должна уметь вычислять свою урожайность и расти. Начнём с вычисления урожайности. Для этого опишем в классе метод с параметром self, который так и назовём – Urojainost. В этом методе объявим переменную u = 0, в которой будем вычислять урожайность. Опишем цикл с параметром x для перебора элементов списка Frukty этой ветви. В цикле будем увеличивать значение переменной u на значение поля Ves параметра x. Таким образом мы вычислим в переменной u суммарный вес фруктов на ветви, но теперь нужно проверить, не превышает ли он максимальный вес, который может выдержать ветвь. Если максимальный вес превышен, то ветвь ломается и её урожайность становится равной 0, метод вернёт это значение. Если же максимальный вес не превышен, то метод вернёт суммарный вес фруктов на ветви.

def Urojainost (self):

    u = 0

    for x in self.Frukty:

        u = u + x.Ves

    if u > self.MaxVes:

        return 0

    else:

        return u   

Теперь опишем рост ветви. Он заключается в том, что значение максимального веса, который может выдержать ветвь, увеличивается на 30% по сравнению с прошлым годом, а количество фруктов на ветке увеличивается в 2 раза. Для этого присвоим полю Frukty результат выражения генератора для создания списка из фруктов с длиной, значение которой в 2 раза больше нынешней. Мы описали класс Vetv.

def Rost (self):

        self.MaxVes = self.MaxVes * 1.3

        self.Frukty = [Frukt () for i in range (len (self.Frukty) * 2)]

Теперь опишем класс Derevo. Начнём с его конструктора. В начальный момент возраст дерева равен нулю. Так как у дерева может быть много ветвей, присвоим полю Vetvi список. Так как изначально у дерева нет ветвей, то этот список будет пуст.

class Derevo:

    def __init__ (self):

        self.Vozrast = 0

        self.Vetvi = []

Согласно объектной модели задачи, дерево должно уметь расти и вычислять свою урожайность. Начнём с описания роста дерева. Он заключается в том, что значение его возраста увеличивается на 1, а также в росте его ветвей. Поэтому запишем цикл с параметром i, в котором будем перебирать индексы ветвей дерева. В этом цикле у каждой ветви дерева будем вызывать метод Rost. Так как по условию задачи каждый третий год вырастает новая ветвь, то мы запишем ветвление с условием, что значение поля Vozrast делится без остатка на 3. Если это условие выполняется, то мы добавляем в список ветвей дерева новую ветвь. Также с ростом дерева будем убирать из списка её ветвей те ветви, которые сломались под весом своих фруктов, то есть урожайность которых равна нулю. Для этого присвоим полю Vetvi значение выражения-генератора, создающего список из элементов нынешнего списка ветвей, урожайность которых больше нуля.

def Rost (self):

        self.Vozrast = self.Vozrast + 1

        for i in range (len (self.Vetvi)):

            self.Vetvi[i].Rost ()

        if self.Vozrast % 3 == 0:

            self.Vetvi = self.Vetvi + [Vetv ()]

        self.Vetvi = [x for x in self.Vetvi if x.Urojainost () > 0]

Теперь опишем метод для вычисления урожайности дерева. В этом методе объявим переменную u, равную 0. Дальше напишем цикл с параметром x, в котором будем перебирать ветви дерева. На каждом шаге будем увеличивать значение переменной u на значение, которое будет возвращать метод Urojainost, вызванный у параметра x. После цикла метод завершит свою работу, вернув значение переменной u. Мы завершили описание класса Derevo.

def Urojainost (self):

        u = 0

        for x in self.Vetvi:

            u = u + x.Urojainost ()

        return u

Теперь опишем основную программу. С помощью инструкции print выведем на экран сообщение о том, что это программа для исследования урожайности дерева, а также запрос на ввод возраста дерева, в котором нас интересует урожайность. Дальше в переменную v считаем возраст дерева. Теперь создадим в переменной d объект класса Derevo и опишем цикл while, который будет работать до тех пор, пока значение поля Vozrast объекта d меньше значения переменной v. В этом цикле у объекта d будем вызывать метод Rost. После того, как цикл завершит свою работу, с помощью инструкции print выведем на экран сообщение о том, что в возрасте v лет урожайность дерева в килограммах равна делённому на тысячу значению, которое вернул метод Urojainost, вызванный у объекта d. Округлим его до целых.

print ('Программа для исследования урожайности дерева.\nВведите возраст дерева.')

v = int (input ())

d = Derevo ()

while d.Vozrast < v:

    d.Rost ()

print ('В возрасте', v, 'лет урожайность дерева составила', round (d.Urojainost () / 1000), 'кг.')

Сохраним описанный модуль и запустим его на выполнение. Вычислим урожайность дерева на 20-й год. Как видим, она составила 13 килограммов. Программа работает правильно. Задача решена. Конечно, эту задачу можно было достаточно просто решить, используя лишь структурное программирование. Однако так мы можем точно сказать о значении всех данных, которые мы использовали в программе.

Но даже в объектном стиле решение можно существенно упростить. С точки зрения класса Derevo нас не интересует структура объекта Vetv. Согласно правилам обмена информацией, описанным в этом классе, объекту Derevo важно лишь то, чтобы у ветви были методы Rost и Urojainost. Также с точки зрения дерева объекты класса Fukt нас не интересуют вообще. Поэтому мы можем изменить устройство класса Vetv. Добавим у объектов этого класса поле Vozrast, значение которого равно в начале будет равно нулю. Тогда при вычислении урожайности ветви мы можем вместо перебора весов всех фруктов вычислить её как случайное число на промежутке от 200 до 500, умноженное на количество фруктов, то есть на 2 в степени, равной возрасту ветви. А при росте ветви нам достаточно, помимо изменения максимального веса, который она выдерживает, увеличивать значение возраста ветви на единицу. Таким образом, в классе Vetv мы можем избавиться от поля Frukty, а также в коде программы от класса Frukt.

class Vetv:

    def __init__ (self):

        self.MaxVes = 5000

        self.Vozrast = 0

 

    def Urojainost (self):

        u = uniform (200, 500) * 2 ** self.Vozrast

        if u > self.MaxVes:

            return 0

        else:

            return u

 

    def Rost (self):

        self.MaxVes = self.MaxVes * 1.3

        self.Vozrast = self.Vozrast + 1

Сохраним изменения в программе и запустим её на выполнение. Вычислим урожайность дерева на 50-й год. Она составила 14 килограммов. Программа работает правильно.

Мы узнали:

·     Для реализации свойств и поведения объектов в классах используются поля и методы.

·     Поле – это переменная, принадлежащая объекту класса.

·     Метод – это функция, реализованная для объектов класса и принадлежащая этому классу.

·     Метод-конструктор используется для создания объектов класса, к которому он принадлежит.

0
1584

Комментарии 0

Чтобы добавить комментарий зарегистрируйтесь или на сайт