ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 03.12.2023
Просмотров: 395
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
314
Глава 14.Проекты для тренировки
Итоги
Головоломка «Ханойская башня» и игра «Четыре в ряд» — короткие программы, но так как мы следовали принципам, представленным в книге, их код хорошо чи- тается и прост в отладке. В этих программах применяется ряд полезных практик: они были автоматически отформатированы программой Black, для описания мо- дулей и функций использовались doc-строки, а константы мы разместили в начале файла. Переменные, параметры функций и возвращаемые значения функций огра- ничиваются одним типом данных, так что аннотации типов (как полезная форма дополнительной документации) оказываются излишними.
В «Ханойской башне» три башни представлены словарем с ключами 'A'
,
'B'
и 'C'
, значениями которых являются списки целых чисел. Такой подход работает, но если бы программа была сколько-нибудь большой и сложной, для представления этих данных стоило бы воспользоваться классом. Классы и средства ООП в этой главе не использовались, потому что об ООП речь пойдет только в главах 15–17. Про- сто помните, что для таких структур данных абсолютно нормально использовать класс. Башни выводятся на экран в ASCII-графике, а диски изображаются серией текстовых символов.
Игра «Четыре в ряд» также использует ASCII-графику для вывода представления игрового поля. Изображение поля строится из многострочного текста, хранимого в константе
BOARD_TEMPLATE
. Строка включает 42 пары фигурных скобок
{}
для каждой ячейки игрового поля 7
× 6. Строковый метод format()
заменяет каждую пару фигурных скобок ячейкой, находящейся в соответствующей позиции. При таком подходе более очевидно, как строка
BOARD_TEMPLATE
строит игровое поле, выводимое на экран.
Несмотря на различия в структурах данных, у этих двух программ много общего.
Обе программы выводят свои структуры данных на экран, запрашивают у игрока входные данные, проверяют ввод, а затем используют его для обновления своих структур данных, прежде чем возвращаться к началу цикла. Однако код для вы- полнения всех этих действий можно написать многими способами. Как сделать свой код удобочитаемым? Удобочитаемость — субъективное ощущение, а не объективная метрика, определяемая степенью соответствия некоторому набору правил. Исходный код, приведенный в этой главе, показывает, что, хотя к любому коду с запахом всегда стоит присмотреться еще раз, признаки проблемы далеко не всегда указывают на существование проблемы, которую необходимо исправить.
Удобочитаемость кода важнее бездумного следования политике недопустимости запахов кода в ваших программах.
1 ... 28 29 30 31 32 33 34 35 ... 40
ЧАСТЬ III
ОБЪЕКТНО-
ОРИЕНТИРОВАННЫЙ
PYTHON
15
Объектно-ориентированное
программирование и классы
Объектно-ориентированное программирование, или
ООП, — механизм языка программирования, позволяю- щий группировать переменные и функции в новые типы данных, называемые классами. На базе классов создаются
объекты. Распределяя свой код по классам, можно разбить монолитную программу на меньшие части, которые проще по- нять и отладить.
В небольших программах ООП добавляет не столько структуру, сколько рутину.
Хотя некоторые языки (например, Java) требуют организации всего кода в классах,
ООП-функциональность в Python не является обязательной. Программист может воспользоваться классами, если они ему нужны, или забыть про классы, если без них можно обойтись.
В докладе разработчика Python Джека Дидериха (Jack Diederich) «Перестаньте писать классы» на конференции PyCon 2012 (https://youtu.be/o9pEzgHorH0/) рас- сматриваются некоторые ситуации, в которых программисты пишут классы, хотя можно было бы обойтись более простой функцией или модулем.
Как бы то ни было, вам как программисту следует знать основы классов и их ис- пользования. Из этой главы вы узнаете, что такое классы, почему они используются в программах и какой синтаксис и концепции программирования лежат в их основе.
ООП — обширная тема, и эта глава содержит только краткое введение в нее.
Аналогия из реального мира: заполнение форм
317
Аналогия из реального мира: заполнение форм
Скорее всего, вам неоднократно приходилось заполнять всевозможные формы, анкеты и бланки, бумажные или электронные: при посещении врача, для покупок в интернете или для приглашения на свадьбу. Формы предоставляют унифициро- ванный механизм сбора необходимой информации людьми или организациями.
Разные формы предназначены для сбора разных видов информации. Врач описыва- ет состояние пациента, а на форме планирования свадьбы вы вводите информацию о приглашенных гостях.
В Python термины «класс», «тип» и «тип данных» имеют одинаковый смысл.
Класс, как и бумажная или электронная форма, представляет собой заготовку для создания объектов Python (также называемых экземплярами). Объекты содержат данные, которые представляют конкретного пациента, покупку в интернет-магазине или гостя на свадьбе. Классы напоминают пустые формы, а объекты, созданные на базе этих классов, — заполненные формы с реальными данными, которые требует форма. Так, на рис. 15.1 форму для подтверждения участия можно сравнить с клас- сом, а заполненную форму — с объектом.
Классы и объекты также можно рассматривать как электронные таблицы (рис. 15.2).
Просьба ответить до 16 июня.
Просьба ответ ить до 16 июн я.
Имя:
Имя:
Да, ябуду присутствовать.
Да, ябуду присут ствовать.
Нет, яне смогу присутствовать.
Нет, яне смогу п рисутствовать.
Количество гостей
Количество гостей
Элис Смит
Классы
Объекты
1
Рис. 15.1. Формы для приглашения гостей на свадьбу напоминают классы, а заполненные формы напоминают объекты
318
Глава 15.Объектно-ориентированное программирование и классы
Рис. 15.2. Электронная таблица с данными гостей. Здесь RSVP означает
«répondez s'il vous plaît» («просьба ответить»)
Заголовки столбцов определяют класс, а каждая отдельная строка таблицы — объект.
Классы и объекты часто приравнивают к моделям элементов реального мира, но не путайте карту с территорией. Содержимое класса зависит от того, что должна сделать программа. На рис. 15.3 изображены некоторые объекты разных классов, представляющие одного и того же человека. Не считая имени, они содержат со- вершенно разную информацию.
Кроме того, информация, содержащаяся в классах, зависит от потребностей вашей программы. Во многих учебниках ООП для примера используется класс
Car
, но при этом авторы забывают, что набор сведений, включаемых в класс, полностью зависит от того, какое приложение вы пишете. Не существует обобщенного класса
Car
, кото- рый бы включал метод honkHorn()
(подать сигнал) или атрибут numberOfCupholders
(количество подставок для стаканов) только потому, что этими характеристиками обладают реальные машины. Может быть, вы пишете веб-приложение для авто- салона, видеоигру с гонками или модель дорожного движения. Класс
Car для ав- тосалона может содержать атрибуты milesPerGallon
(количество миль на галлон) или manufacturersSuggestedRetailPrice
(рекомендованная цена производителя), подобно тому как имена этих атрибутов могут быть включены в заголовки столбцов
Создание объектов на базе классов
319
в электронных таблицах автосалона. Но в видеоигре и модели дорожного движения этих атрибутов не будет, потому что здесь они не актуальны. Класс
Car для видео- игры может содержать метод explodeWithLargeFireball()
(эффектно взорваться), но в приложение для автосалона и модели дорожного движения он не попадет… хочется надеяться.
Рис. 15.3. Четыре объекта, созданные на базе разных классов. Объекты представляют одного и того же человека в зависимости от того, какая информация о человеке нужна приложению
Создание объектов на базе классов
Вы уже использовали классы и объекты в Python, даже если не создавали их сами.
Вспомните модуль datetime
, который содержит класс с именем date
. Объекты клас- са datetime.date
(также называемые объектами datetime.date или объектами date
) представляют конкретную дату. Введите следующий фрагмент в интерактивной оболочке, чтобы создать объект класса datetime.date
:
>>> import datetime
>>> birthday = datetime.date(1999, 10, 31) # Передать год, месяц и день.
>>> birthday.year
1999
>>> birthday.month
10
>>> birthday.day
31
>>> birthday.weekday() # weekday() - метод, на что указывают круглые скобки.
6
320
Глава 15.Объектно-ориентированное программирование и классы
Атрибуты представляют собой переменные, связанные с объектом. Вызов datetime.
date()
создает новый объект date
, инициализируемый аргументами
1999
,
10
,
31
, так что объект представляет дату 31 октября 1999 года. Эти аргументы присваиваются атрибу- там year
, month и day класса date
; такие атрибуты присутствуют в каждом объекте date
С подобной информацией метод weekday()
класса может вычислить день недели.
В приведенном примере он возвращает 6 — обозначение воскресенья, потому что согласно электронной документации Python возвращаемым значением weekday()
является целое число от 0 (понедельник) до 6 (воскресенье). В документации так- же перечислены другие методы, содержащиеся в классе date
. И хотя объект date содержит много атрибутов и методов, это все еще один объект, который можно сохранить в переменной, — такой как birthday в приведенном примере.
Создание простого класса: WizCoin
Создадим класс
WizCoin
, представляющий набор монет в вымышленной волшебной валюте. В этой валюте используются следующие номиналы: кнаты, сикли (29 кнатов) и галлеоны (17 сиклей, или 493 кната). Помните, что объекты класса
WizCoin пред- ставляют количество монет разного номинала, а не денежную сумму. Условно говоря, этот класс сообщит, что у вас пять четвертаков и один гривенник, а не 1 р. 35 коп.
Создайте файл с именем wizcoin.py и введите следующий код для создания класса
WizCoin
. Обратите внимание: имя метода
__init__
начинается и завершается двумя символами подчеркивания (метод
__init__
рассматривается в подразделе «Мето- ды, __init__() и self» этой главы):
class WizCoin:
❶
def __init__(self, galleons, sickles, knuts):
❷
"""Создание нового объекта WizCoin по значениям galleons, sickles и knuts."""
self.galleons = galleons self.sickles = sickles self.knuts = knuts
# ВНИМАНИЕ: методы __init__() НИКОГДА не содержат команду return.
def value(self):
❸
"""The value (in knuts) of all the coins in this WizCoin object."""
return (self.galleons * 17 * 29) + (self.sickles * 29) + (self.knuts)
def weightInGrams(self):
❹
"""Возвращает вес монет в граммах."""
return (self.galleons * 31.103) + (self.sickles * 11.34) + (self.knuts
* 5.0)
Программа определяет новый класс с именем
WizCoin при помощи команды class
❶
При создании класса создается новый тип объектов. Использование команды
Создание простого класса: WizCoin
321
class для определения класса напоминает команду def
, задающую новые функции.
Внутри блока кода, следующего за командой class
, следуют три определения трех методов:
__init__()
(сокращение от initializer)
❷
, value()
❸
и weightInGrams()
❹
Обратите внимание: все методы имеют первый параметр с именем self
, который будет рассмотрен в следующем разделе.
По общепринятым соглашениям имена модулей (например, wizcoin в файле wiz- coin.py
) записываются в нижнем регистре, а имена классов (например,
WizCoin
) на- чинаются с буквы верхнего регистра. К сожалению, некоторые классы стандартной библиотеки Python — такие как date
— этому соглашению не следуют.
Чтобы потренироваться в создании новых объектов класса
WizCoin
, введите сле- дующий исходный код в отдельном окне редактора и сохраните файл с именем wcexample1.py в одной папке с wizcoin.py
:
import wizcoin purse = wizcoin.WizCoin(2, 5, 99) # Целые числа передаются __init__().
❶
print(purse)
print('G:', purse.galleons, 'S:', purse.sickles, 'K:', purse.knuts)
print('Total value:', purse.value())
print('Weight:', purse.weightInGrams(), 'grams')
print()
coinJar = wizcoin.WizCoin(13, 0, 0) # Целые числа передаются __init__().
❷
print(coinJar)
print('G:', coinJar.galleons, 'S:', coinJar.sickles, 'K:', coinJar.knuts)
print('Total value:', coinJar.value())
print('Weight:', coinJar.weightInGrams(), 'grams')
Вызовы
WizCoin()
❶
и
❷
создают объекты
WizCoin и выполняют для них код метода
__init__()
. В аргументах
WizCoin()
передаются три целых числа, которые пере- даются параметрам
__init__()
. Эти аргументы присваиваются атрибутам self.
galleons
, self.sickles и self.knuts объекта. Подобно тому как функция time.
sleep()
требует сначала импортировать модуль time и поставить префикс time.
перед именем функции, мы также должны импортировать wizcoin и поставить префикс wizcoin.
перед именем функции
WizCoin()
Результат выполнения программы выглядит приблизительно так:
G: 2 S: 5 K: 99
Total value: 1230
Weight: 613.906 grams
G: 13 S: 0 K: 0
Total value: 6409
Weight: 404.339 grams