Шуман Х. - Python для детей - 2019. # Startwerte festlegen Red (255,0,0)
Скачать 5.95 Mb.
|
Глава Классы и модули 6 122 Рис. 6.6.Результат работы программы: три монстра! Теперь я хотел бы расширить все три класса с помощью метода, который имеет возвращаемое значение. Для роди- тельского класса Monster код выглядит так: def Type(self) : return "Монстр" Метод show тоже должен быть расширен. Я сделал новую вер- сию программы, код которой выглядит так (⇒ monster4.py): class Monster : # Инициализация атрибута def __init__(self, name, character) : self.Name = name self.Character = character # Метод def Type(self) : return "Монстр" def show(self) : print("Имя: " + self.Name) print("Особенность: " + self.Character) print("Тип: " + self.Type()) class GMonster (Monster): # Метод def Type(self) : return "Дух монстра" Наследование 123 class SMonster (Monster): # Метод def Type(self) : return "Душа монстра" # Основная программа Frank = Monster("Фрэнки", "необычный") Frank.show() Albert = GMonster("Альберт", "задумчивый") Albert.show() Sigmund = SMonster("Зигмунд", "веселый") Sigmund.show() Расширенный метод show()теперь также отображает тип со- ответствующего объекта: print("Тип: " + self.Type()) Помимо прочего, важно, чтобы слово self было помещено перед методом. Это касается всех элементов. Теперь у дочернего класса есть все элементы родительского класса и, конечно, его собственные новые атрибуты и мето- ды – в зависимости от того, что было переопределено. дочерний родительский Атрибут +НаследуемыеАтрибуты Методы +НаследуемыеМетоды ( ) : class ¾ Введи код из листинга выше, а затем запусти програм- му. Должно получиться примерно так, как показано на рис. 6.7. Глава Классы и модули 6 124 Рис. 6.7.Результат работы программы: отображается тип монстра Обычно объект Frank вызывает собственный метод Type() в собственном методе show. Интересно, что два других объ- екта сначала вызывают унаследованный метод (= show()) и не получают доступ к дочернему (унаследованному) ме- тоду, а обращаются к собственному методу. Как узнать, какой метод Type() использовать? Поскольку у них фактически есть два метода с одним и тем же име- нем Type(), они могут выбрать и использовать неправиль- ный метод «по ошибке». В Python существует механизм, благодаря которому автоматически используется дочер- ний метод. На самом деле так происходит и в других языках программиро- вания, таких как, например, C++ или Java: программа, подобная приведенной выше, выводит тип Монстр три раза. Только если определить методы Type() по-разному, код будет работать авто- матически, как в Python. Почему так происходит? Существует два способа вызова метода. Обычно в той позиции, где вызывается метод, задается начальный адрес. Этот параметр определяет, какой метод используется. Или ты помечаешь пози- цию вызова с помощью заполнителя. Только во время запуска программы будет использоваться адрес соответствующего ме- тода. Модули программы 125 Другие языки программирования: Python: def show() : def show() : фиксированный адрес заполнитель нормальный метод виртуальный метод Это виртуальные методы. В то время как в других языках про- граммирования такие методы имеют метку virtual, все методы в Python по умолчанию виртуальны. Модули программы Исходный код будет только увеличиваться в объеме со вре- менем. Эта программа не слишком велика, но представь, что ты захотел расширить код на нужное приложение или реализовать дополнительный функционал. Тогда код мож- но разделить на несколько файлов – модулей. moster.py mosterlab.py Определение класса с помощью атрибутов и методов Определение класса с помощью атрибутов и методов Основная программа: создание и использование объекта Ctrl+X Ctrl+V Давай создадим новый файл, в котором мы сможем сохра- нить определения классов. В предыдущем файле остается только основная программа. ¾ Убедись, что последняя версия программы открыта. Создай новый файл в среде разработки Python (IDLE), щелкнув мышью по пункту File (Файл) в строке меню, а затем выбрав пункт New file (Создать файл) (или на- жав сочетание клавиш Ctrl+N). Глава Классы и модули 6 126 ¾ Перейди в файл с кодом программы, выдели целиком определения классов монстров и вырежи их (нажав со- четание клавиш Ctrl+X) (рис. 6.8). Затем перейди в окно нового файла и нажми сочетание клавиш Ctrl+V, чтобы вставить скопированный код. Рис. 6.8.Определения классов монстров выделены ¾ Сохрани оба файла, первый (старый) под новым име- нем (у меня это monster5.py). Новый файл назовем mon sterlab.py. Скопированный код теперь находится в файле модуля: class Monster : # Инициализация атрибута def __init__(self, name, character) : self.Name = name self.Character = character Модули программы 127 # Метод def Type(self) : return "Монстр" def show(self) : print("Имя: " + self.Name) print("Особенность: " + self.Character) print("Тип: " + self.Type()) class GMonster (Monster): # Метод def Type(self) : return "Дух монстра" class SMonster (Monster): # Метод def Type(self) : return "Душа монстра" Разумеется, старая программа без определения классов больше не работает. Но как основная программа узнает, где теперь находятся определения классов? moster.py mosterlab.py import mosterlab Основная программа: создание и использование объекта Классы, с которыми мы работали раньше, определены в од- ном модуле, который импортируется в программу. Это вы- глядит так: import mosterlab Заверши код программы, вставив эту строку кода вверху. Затем запусти программу. Тебя удивило сообщение об ошибке? Так или иначе компью- тер не узнаёт класс Monster (и, разумеется, два других дочер- них класса) (рис. 6.9). Глава Классы и модули 6 128 Рис. 6.9.Ошибка определения имени Monster Как сообщить компьютеру, что мы уже импортировали со- ответствующий модуль? Недостаточно просто импортиро- вать модуль, также нужно указать его имя, если ты хочешь использовать некий код из этого модуля (например, функ- цию). Давай сделаем это здесь (⇒ monster5.py): import monsterlab # Основная программа Frank = monsterlab.Monster("Фрэнки", "необычный") Frank.show() Albert = monsterlab.GMonster("Альберт", "задумчивый") Albert.show() Sigmund = monsterlab.SMonster("Зигмунд", "веселый") Sigmund.show() Поместив перед именем каждого класса имя модуля, из которого происходит класс, компьютер не будет делать ошибок. ¾ Исправь исходный код основного модуля, затем запус- ти программу. Теперь все работает (рис. 6.10). Модули программы 129 Рис. 6.10.Ошибка исправлена Наша семейка монстров готова, но не все еще идеально. Во-первых, существует множество атрибутов и методов, которые могут быть согласованы в обоих классах – в зави- симости от твоего воображения. Займись этим. А тем временем я хотел бы внести несколько потенциаль- ных улучшений, чтобы основная программа стала чуть бо- лее элегантной. Если мы немного изменим строку кода, отвечающую за импорт модуля, мы сможем сократить код ниже (⇒ monster6.py): from monsterlab import * # Основная программа Frank = Monster("Фрэнки", "необычный") Frank.show() Albert = GMonster("Альберт", "задумчивый") Albert.show() Sigmund = SMonster("Зигмунд", "веселый") Sigmund.show() Вместо импорта всего модуля мы лишь извлекаем отдель- ные классы, обозначенные звездочкой (*) после слова im port . Также может использоваться и такая строка: from monsterlab import Monster Но тогда программа будет работать только для объекта класса, потому что ссылка на вызов monsterlab указана до вызова класса. Глава Классы и модули 6 130 import from import Модуль Модуль Элемент Таким образом, ты можешь интегрировать полный модуль или его части. И если ты хочешь извлечь все элементы, прос то укажи звездочку: from Modul import * На первый взгляд кажется, что это сложнее и лучше – прос- то импортировать модуль. Но разница в том, что тебе боль- ше не нужно перечислять имена классов в модуле. Также ты можешь сократить объявление объекта – вот так: Frank = Monster("Фрэнки", "необычный") Albert = GMonster("Альберт", "задумчивый") Sigmund = SMonster("Зигмунд", "веселый") Приватный или публичный? Остается недостаток, который ты даже не заметил вначале и, возможно, никогда не заметил бы. После создания объ- екта, такого как Frank, можно напрямую получить доступ к атрибутам внутри объекта (⇒ monster7.py): Frank = Monster("Фрэнки", "необычный") print(Frank.Name); print(Frank.Character) print(Frank.Type()) Что не так? А вот что. В нашем примере ничего страшного нет, но представь себе определение более сложных классов, которые имеют много атрибутов. Часто нежелателен пря- мой доступ к атрибутам извне. Потому что в зависимости от уровня доступа можно изменить значение атрибута. В случае с многочисленными объектами может случиться так, что что-то изменилось, хотя не должно было. И найти такую ошибку часто бывает нелегко. Вернемся к термину, который я упомянул в начале: ин- капсуляция. Ее также можно назвать замкнутостью и защи- той от внешнего доступа. В нашем случае достаточно полу- чить доступ к методу show(), и ты получишь доступ ко всему Приватный или публичный? 131 остальному коду, т. к. он является внутренним. А нужно ли его менять с наружным доступом? Давай посмотрим, как Python защищает атрибуты и мето- ды от внешнего доступа. Для этого нам нужно войти в мо- дуль monsterlab. ¾ Открой файл monsterlab.py и сохрани его под именем monsterlabx.py. Затем тебе нужно добавить два символа подчеркивания (_) для каждого из атрибутов Name и Character, а также метода Type() : class Monster : # Инициализация атрибута def __init__(self, name, character) : self.__Name = name self.__Character = character # Метод def _Type(self) : return "Монстр" def show(self) : print("Имя: " + self.__Name) print("Особенность: " + self.__Character) print("Тип: " + self.__Type()) class GMonster (Monster): # Метод def __Type(self) : return "Дух монстра" class SMonster (Monster): # Метод def __Type(self) : return "Душа монстра" Импортируем новый модуль в основную программу и со- ответствующим образом настроим имена (⇒ monster8.py): from monsterlabX import * # Основная программа Frank = Monster("Фрэнки", "необычный") print(Frank.__Name); print(Frank.__Character) print(Frank.__Type()) Запуск программы выдаст сообщение об ошибке (рис. 6.11): Глава Классы и модули 6 132 Рис. 6.11.Ошибка: не обнаружены атрибуты объекта Хотя _Name (а также _Character и _Type) четко определены и модуль импортирован, основная программа не может по- лучить доступ к этим элементам. Потому что рассматрива- емые атрибуты и метод Type теперь считаются закрытыми. И именно этого мы и хотели достичь. В Python применяется следующее правило: если ты добавляешь к имени класса два символа подчеркивания, элемент становит- ся закрытым. В противном случае каждый элемент считается публичным. Доступ к публичным элементам можно получить из любой позиции, но в пределах объекта. (Вспомни о доступности глобальных и локальных переменных.) ¾ Чтобы попробовать новый модуль, тебе нужно изме- нить только первую строку в исходном коде monster6.py (если ты назвал свой модуль иначе, разумеется, указы- вай свое имя): from monsterlabX import * Запуск программы заканчивается горьким разочаровани- ем (рис. 6.12). Что происходит? Приватный или публичный? 133 Рис. 6.12.Все монстры стали одного типа Теперь дочерние классы Monster забыли про свой тип и ис- пользуют родительский. Поэтому, очевидно, нельзя ска- зать, что программа стала лучше. Итак, все элементы долж- ны быть определены заново? Улучшение было своего рода «промежуточным». Взгляни на решение, которое предоставляет Python (⇒ monsterlabx.py): class Monster : # Инициализация атрибута def __init__(self, name, character) : self.__Name = name self.__Character = character # Метод def _Type(self) : return "Монстр" def show(self) : print("Имя: " + self.__Name) print("Особенность: " + self.__Character) print("Тип: " + self._Type()) class GMonster (Monster): # Метод def _Type(self) : return "Дух монстра" class SMonster (Monster): # Метод def _Type(self) : return "Душа монстра" Смотри внимательно, чтобы увидеть разницу: теперь метод Type() имеет только одно подчеркивание (_). И вот разница между соответствующими параметрами доступа (табл. 6.1). Глава Классы и модули 6 134 Таблица 6.1. Типы доступа Тип Имя Доступ Public (публичный) Без подчеркивания В любом месте Protected (защищенный) С одним подчеркиванием В дочерних классах Private (приватный) С двумя подчеркиваниями Только внутри объекта Внеси изменения, а затем запусти программу еще раз. (Ты можешь скачать все файлы примеров с сайта dmkpress.com .) Теперь, как раньше, все монстры знают свой тип. Снаружи ты не можешь получить доступ к методу, если только он не касается класса Monster. (Таким образом доступ к методу остается «защищенным».) Подведение итогов Вот и закончилась очередная глава. Ты написал собствен- ные классы и создал собственные объекты. Однако ты еще не гуру программирования (все самое интересное еще впе- реди). Посмотрим, что ты узнал о Python. class Определяет класс self Ссылка на объект или класс _init Метод инициализации from Импортирует отдельные элементы модуля * (звездочка) Доступ ко всем элементам модуля pass Пустая строка, отображающая пустую структуру __ Определяет приватный элемент _ Определяет защищенный элемент Связывает объект с атрибутом или методом Несколько вопросов... 1. Что такое методы и атрибуты класса? 2. Что такое инкапсуляция? 3. В чем разница между приватными и публичными эле- ментами? ...а задач нет 135 7 Введение в tkinter До сих пор мы ограничивались вводом кода вручную, но в операционной системе Windows мы привыкли к чему-то большему. Мы используем графический интерфейс, откры- ваем меню, щелкаем по окнам с кнопками, изображениям и полям ввода. Но наши проекты на Python до сих пор были невзрачными, без красивого интерфейса. Теперь это нужно изменить. Ты можешь сделать это с помощью окон, кнопок и т. д. Итак, в этой главе ты узнаешь: что такое tkinter; об элементах Label и Button; как работать с событиями; что такое компоновка окон; о диалоговых окнах. Создаем окно Работая с интерфейсом IDLE, мы не использовали многие компоненты, которые содержит операционная система Windows. Было бы круто вывести хотя бы одно диалоговое окно на экран. И если нам это удастся, мы заполним это окно разными компонентами. Глава Введение в tkinter 7 136 Что такое компоненты? Это объекты, которые обычно использу- ются для управления программой. Например, кнопки (Button), меню и метки (Label). Виджеты тоже относятся к компонентам tkinter Разумеется, нам нужен модуль, который мы должны снача- ла импортировать: import tkinter Модуль tkinter содержит все компоненты, необходимые для создания графического пользовательского интерфейса. Особенностью модуля является то, что он работает незави- симо от того, что делает операционная система. Кстати, слово tkinter является аббревиатурой для Toolkit inter- face – инструменты для разработки интерфейсов. Модуль содер- жит инструменты для разработки так называемого GUI – Graphical User Interface – графического пользовательского интерфейса. Наша первая графическая программа очень короткая: import tkinter Window = tkinter.Tk() Если ты запустишь этот код, то увидишь окно, показанное на рис. 7.1. Рис. 7.1.Созданное окно Сначала создается объект окна, который запускается на- жатием функцией Tk(). Это основной класс модуля tkinter. Важно, что имя Tk написано с прописной буквы «T», а слово tkinter со строчной «t». Создаем окно 137 ¾ Создай новый файл, напиши исходный код и запусти программу. Как насчет приятного приветствия, как мы это делали в главе 1? Воспользуемся компонентом, называемым Label. ¾ Обнови исходный код, а затем перезапусти программу (⇒ window1.py): import tkinter Window = tkinter.Tk() Display = tkinter.Label(Window, text="Привет!") Display.pack() Функция Display создает объект Label, первый параметр указывает на окно, в котором должно отображаться содер- жимое Label, а второй представляет собой текст, который надо отобразить. Все это «обернуто», так сказать, в метод pack . Без этой инструкции ты ничего не увидишь в окне (рис. 7.2). Рис. 7.2.Приветствие в окне Окно стало еще более маленьким, чем предыдущее. Это связано с тем, что размер окна адаптируется под размер компонентов. Позже мы рассмотрим, как можем влиять на размер окна. Теперь я хотел бы усложнить программу, чтобы за «при- ветствием» последовал вопрос «как дела?», а также доба- вить две кнопки Button, чтобы окно выглядело следующим образом: Рис. 7.3.Усложненная программа Выглядит (по-прежнему) не очень красиво, но ключевое слово pack в tkinter гарантирует, что окно занимает столь- ко места, сколько необходимо. Кроме того, все компоненты расположены один за другим. Мы изменим это позже. |