Пояснительная записка Элективный курс по информатике "Введение в объектноориентированное программирование на Python"
Скачать 0.85 Mb.
|
Описание задачи Допустим, нам требуется написать программу, которая вычисляет площадь обоев для оклеивания комнаты определенных пользователем размеров. При этом необходимо учитывать, что окна, двери, пол и потолок оклеивать не надо. Для начала решим данную задачу логически. Комната — это прямоугольный параллелепипед, состоящий из шести прямоугольников. Его площадь представляет собой сумму площадей составляющих его прямоугольников. Площадь прямоугольника равна произведению его длины на ширину. Обои клеятся только на стены, следовательно площади верхнего и нижнего прямоугольников нам не нужны. Из рисунка можно заключить, что площадь одного прямоугольника равна x * z, второго – у * z. Противоположные прямоугольники равны, значит общая площадь четырех прямоугольников будет равна S = 2xz + 2уz = 2z(x+y). Потом из этой площади надо будет вычесть общую площадь дверей и окон. Двери и окна — это прямоугольники (как вычислить их площадь должно быть понятно). Создание классов-частей Теперь приступим к созданию программы. В соответствие с изучаемой темой написать ее надо используя объектно-ориентированную парадигму программирования, да еще и применяя "композиционный подход" (насколько он здесь уместен не обсуждается :b). Можно заметить, что фактически у нас есть три типа объектов - это объекты-окна, объекты-двери и объекты-комнаты. Получается три класса. Окна и двери являются частями помещения, а значит могут создаваться внутри класса «комнаты». Кроме того, для данной задачи существенное значение имеют только два свойства: длина и ширина. Поэтому классы «окна» и «двери» можно объединить в один. Понятно, что если для задачи были бы важны другие свойства (например, толщина стекла, материал), то возможно следовало бы создать два класса. 1. class Win_Door: 2. def __init__ ( self ,x,y): 3. self .square = x * y Здесь при вызове класса Win_Door будет автоматически создан атрибут square объекта, являющийся ссылкой на значение площади объекта. Создание класса-контейнера Можно по-разному реализовать класс-контейнер. Есть подозрение, что многое зависит от задачи, решаемой программистом, его мастерства и вкуса. Классы-части можно вызывать в методе __init__, тем самым объекты-части будут автоматически создаваться при создании объекта- контейнера. Однако в данной задаче мы пойдем другим путем: окна и двери будут создаваться специальным для этих целей методом (будем считать, что так интересней). Также класс должен содержать метод для вычисления площади требуемых обоев (wallpapers). В конце можно добавить метод, в котором реализован вывод тех или иных данных. 1. class Room: 2. def __init__ ( self ,x,y,z): 3. self .square = 2 * z * (x + y) 4. def win_door( self , d,e, f,g, m=1,n=1): 5. self .window = Win_Door(d,e) 6. self .door = Win_Door(f,g) 7. self .numb_w = m 8. self .numb_d = n 9. def wallpapers( self ): 10. self .wallpapers = self .square - \ 11. self .window.square * self .numb_w \ 12. - self .door.square * self .numb_d 13. def printer( self ): 14. ( "Площадь стен комнаты равна " \ 15. , str ( self .square), " кв.м" ) 16. ( "Оклеиваемая площадь равна: " , \ 17. str ( self .wallpapers), " кв.м" ) В методе __init__ создается атрибут square объекта представляющий собой площадь стен комнаты. Метод принимает три аргумента: длину, ширину и высоту помещения. В методе win_door создаются два объекта: window и door, а также атрибуты numb_w и numb_d (в последних будут содержаться значения о количестве окон и дверей). Если при вызове данного метода в программе не будет указано количество окон и дверей, то по умолчанию будут подставлены значения равные 1. Метод wallpapers вычисляет площадь_требуемых_обоев = площадь_комнаты — площадь_окна * количество_окон — площадь_двери * количество_дверей. В коде данная строка разбита на несколько строчек с помощью знака \ (так делают, если строка очень длинная). Также обратите внимание, как происходит обращение к свойствам square объектов-частей: указывается объект класса Room (в классе его заменяет self), далее объект-часть, и наконец, сам атрибут (свойство) объекта-части. Метод printer просто выводит данные. Создание объектов После того, как классы созданы, посмотрим как это все работает. 1. Создаем объект класса Room: 1. labor34 = Room( 5 , 4 , 2 ) 2. Создаем в помещении labor34 окна и двери: 1. labor34.win_door( 1.5 , 1.5 , 2 , 1 , 2 ) Обратите внимание, что количество дверей не указано, а значит их будет ровно 1. 3. Вычисляем метры обоев: 1. labor34.wallpapers() 4. Просим вывести, что получилось: 1. labor34.printer() В результате работы метода printer интерпретатор должен выдать что-то вроде этого: 1. Площадь комнаты равна 36 кв.м 2. Оклеиваемая площадь равна: 29.5 кв.м Может показаться, что в программе всего один реальный объект — labor34. Однако это не так. Там есть еще объекты labor34.window и labor34.door. Чтобы в этом убедиться достаточно обратиться к их свойствам. 1. (labor34.window.square) 2. (labor34.door.square) Практическая работа Попробуйте самостоятельно придумать задачу, для решения которой можно использовать композиционный подход. Напишите программу на Python. Модули и их импорт. Урок 8 Методическая разработка урока Элективный курс: Введение в объектно-ориентированное программирование на Python Уровень: Программирование для начинающих На прошлом уроке нами была написана "серьезная" программа, которую могут использовать другие программисты. Однако как? Просто копировать код и вставлять в свои скрипты? Или есть более экономный способ (в смысле уменьшения объема кода и удобства его использования)? При создании крупных программ оказался выгодным так называемый модульный принцип организации, когда есть основной файл с частью кода программы, к которому подсоединяется (в который импортируется) содержимое других файлов. Когда исходный код основного файла транслируется в машинный код, то импортируемые файлы также выполняются как и код основного файла. Такой способ организации программы позволяет изолировать часто используемый код в файл-модуль, а затем импортировать его в другие файлы без копирования кода. Но это далеко не единственное преимущество модульного принципа организации программы. Так как же импортировать содержимое одного файла в другой в языке программирования Python? Существует два основных способа: инструкция import и инструкция from. Первая инструкция запускает (интерпретирует) файл-модуль полностью, при этом для доступа к переменным (атрибутам) модуля из основного файла следует указывать имя модуля впереди требуемого атрибута: module.attribute (так называемая, точечная нотация). Инструкция from передает интерпретатору лишь указанные имена из файла-модуля, однако при доступе к этим переменным не надо указывать имя модуля. Первый способ хорош, если предстоит пользоваться содержимым почти всего модуля, второй — если будут востребованы одна-две функции или класс из модуля. В примере данного урока мы воспользуемся инструкцией import. Импорт в языке программирования Python осуществляется следующим образом: после слова import пишется имя импортируемого модуля. Модуль и файл в Python понятия почти не различимые. Файлы с кодом на языке Python обычно имеют расширение .py, однако в инструкции import расширение не указывается. Например, если мы имеем файл-модуль scale.py, то импортировать его в другой файл следует так: import scale. Где должен располагаться модуль? В принципе, где угодно, т.к. можно "вручную" настроить интерпретатор так, что он будет искать там, где пожелает программист. Однако, если ничего не настраивать, то интерпретатор Python найдет файлы, если их расположить например, в каталоге, куда установлен Python или в том же каталоге, где и файл, в который осуществляется импорт. Этим последним вариантом мы и воспользуемся. Итак, у нас есть файл с кодом, позволяющим вычислять оклеиваемую площадь помещения (урок №7). Пусть он называется rooms.py. Кроме того, удалим из него "код тестирования" … 1. labor34 = Room(5,4,2) 2. labor34.win_door(1.5,1.5, 2,1, 2) 3. labor34.wallpapers() 4. labor34.printer() 5. 6. (labor34.window.square) 7. (labor34.door.square) … и предположим, что классы этого модуля будут использоваться в другом (основном) файле. Допустим, этот основной файл предоставляет интерфейс пользователю для ввода данных и получения результата. Основной файл должен быть сохранен в том же каталоге, что и файл rooms.py. Первым делом, импортируем содержимое файла rooms.py 1. import rooms Далее организуем запрос данных у пользователя, одновременно преобразовав данные в целочисленный тип (функция int): 1. ( "Введите размеры помещения (в метрах) ..." ) 2. l = int ( input ( "длина: " )) 3. w = int ( input ( "ширина: " )) 4. h = int ( input ( "высота: " )) 5. 6. ( "Введите данные об оконных проемах (в метрах) ..." ) 7. h_w = int ( input ( "высота: " )) 8. w_w = int ( input ( "ширина: " )) 9. m = int ( input ( "количество: " )) 10. 11. ( "Введите данные о дверных проемах (в метрах) ..." ) 12. h_d = int ( input ( "высота: " )) 13. w_d = int ( input ( "ширина: " )) 14. n = int ( input ( "количество: " )) Теперь создаем объект класса Room. Описание класса находится в модуле rooms, который был импортирован инструкцией import (а не from), поэтому, чтобы получить доступ к классу Room и его атрибутам, следует при создании объекта указать модуль, в котором он находится: 1. uroom = rooms.Room(l,w,h) А теперь можно пользоваться атрибутами класса из модуля сколько влезет: 1. uroom.win_door(h_w, w_w, h_d, w_d, m,n) 2. uroom.wallpapers() 3. uroom.printer() Практическая работа: 1. Создайте скрипт, импортирующий модуль с классом Room и использующий его (как показано в данном уроке). 2. Допишите предыдущую программу, расширив ее возможности: можно по-желанию получить дополнительные сведения (площадь окна и двери). 3. Переделайте программу таким образом, чтобы она не запрашивала у пользователя данные, а предлагала выбор из пяти готовых решений: на экран выводятся характеристики различных помещений, — пользователю остается только выбрать. Строки документации исходного кода на Python. Урок 9 Методическая разработка урока Элективный курс: Введение в объектно-ориентированное программирование на Python Уровень: Программирование для начинающих Версия интерпретатора python: 3.-.- __doc__ - строки документации На прошлом уроке мы рассмотрели случай импорта программного кода одного файла в другой. Таким образом, например, один программист может использовать разработки другого. К тому же, существует множество стандартных модулей и библиотек, входящих в установочный пакет интерпретатора python, а также огромное количество модулей сторонних разработчиков. Понятно, что чтобы использовать чужую разработку надо знать, что она делает. Для этого вовсе необязательно анализировать исходный код, поскольку есть инструменты для его документирования, которыми приличные программисты должны регулярно пользоваться. Когда кому-то потребуется узнать, что делает тот или иной скрипт, достаточно будет выполнить специальную команду, которая выдаст описание модуля. Документировать исходный код на языке программирования Python можно по разному. Иногда бывает достаточно простых комментариев. Еще один способ — это создание строк документации. Они представляют собой текст, заключенный в кавычки (тройные, тройные одинарные, обычные). Такой текст может располагаться после заголовков классов, функций (методов), а также в начале файла (модуля). Пример: 1. """ Строка документации в начале файла. 2. А это ее продолжение с новой строки. """ 3. class … : 4. ''' Это строка документирования класса. 5. Причем она заключена в тройные одинарные кавычки... 6. или одинарные тройные 0_о ''' 7. 8. 9. def … : 10. """ Это документирование модуля. 11. Пишите коменты на инглише. """ Для получения доступа к такой документации предусмотрена специальный метод __doc__. Когда интерпретатор Python обрабатывает исходный код класса или функции и обнаруживает там строки документирования, то делает их значением атрибута __doc__ данного объекта. Чтобы посмотреть как это выглядит, рассмотрим реальный пример. В языке программирования Python строки, списки, числа являются по- сути такими же классами как и пользовательские (создаваемые для специализированного проекта). Данные классы автоматически загружаются (интерпретируются), когда запускается программа-интерпретатор python, поэтому не требуется дополнительный импорт модулей, содержащих их описание. Однако сути это не меняет, и получить доступ к их строкам документации можно также как и к пользовательскому классу (функции, модулю), а именно имяОбъекта.__doc__. Пример: 1. >>> ( str .__doc__) 2. str ( string [, encoding[, errors]]) - > str 3. 4. Create a new string object from the given encoded string 5. encoding defaults to the current default string encoding. 6. errors can be 'strict' , 'replace' or 'ignore' and defaults to 'strict' Таким образом, мы получили данные о классе str (описывает атрибуты строк). Однако мы не получили никаких сведений о методах данного класса. Да и вообще: какие методы есть у строк? Тут может помочь встроенная функция dir, которая выводит список переменных (атрибутов) переданного ей аргумента. 1. >>> dir ( str ) 2. [ '__add__' , '__class__' , '__contains__' , '__delattr__' , '__doc__' , '__eq__' , '__format__' , '__ge__' , '__getattribute__' , '__getitem__' , '__getnewargs__' , '__gt__' , '__hash__' , '__init__' , '__iter__' , '__le__' , '__len__' , '__lt__' , '__mod__' , '__mul__' , '__ne__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__rmod__' , '__rmul__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '_formatter_field_name_split' , '_formatter_parser' , 'capitalize' , 'center' , 'count' , 'encode' , 'endswith' , 'expandtabs' , 'find' , 'format' , 'index' , 'isalnum' , 'isalpha' , 'isdecimal' , 'isdigit' , 'isidentifier' , 'islower' , 'isnumeric' , 'isprintable' , 'isspace' , 'istitle' , 'isupper' , 'join' , 'ljust' , 'lower' , 'lstrip' , 'maketrans' , 'partition' , 'replace' , 'rfind' , 'rindex' , 'rjust' , 'rpartition' , 'rsplit' , 'rstrip' , 'split' , 'splitlines' , 'startswith' , 'strip' , 'swapcase' , 'title' , 'translate' , 'upper' , 'zfill' ] Допустим, нас интересует функция метод find, но мы точно не знаем, делает ли он то, что нам нужно. Выяснить это можно так: 1. >>> ( str .find.__doc__) 2. S.find(sub[, start[, end]]) - > int 3. 4. Return the lowest index in S where substring sub is found, such that sub is contained within s[start:end]. Optional arguments start and end are interpreted as in slice notation. 5. 6. Return - 1 on failure. Становятся известны следующие подробности. Оказывается функция find класса str просто возвращает первый индекс подстроки переданной ей в качестве аргумента, если та встречается в строке. По желанию можно указать откуда и докуда искать в исходной строке. Не в тему, но чтобы было понятно: 1. >>> a = "hello new worlds" # создаем строку 2. >>> a.find( "new" ) # ищем индекс первого элемента подстроки 3. 6 4. >>> a.find( "l" , 7 ) # ищем индекс символа, начиная с 8-го элемента 5. 13 Форматирование строк документирования Обратите внимание, когда мы вызывали метод __doc__ для объектов, то использовали встроенную функцию print. Зачем? Ведь в интерактивном режиме вывод работает и без print. Однако если при вызове метода __doc__ не использовать функцию print, то вывод не отформатируется, а будет выглядеть как в исходном коде: 1. >>> str .__doc__ 2. "str(string[, encoding[, errors]]) -> str \n\n Create a new string object from the given encoded string. \n encoding defaults to the current default string encoding. \n errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'." Обратите внимание на сочетание символов \n. Оно обозначает переход на новую строку. Вообще оформление строк документации должно соответствовать определенному стандарту. |