Саммерфилд - Программирование на Python 3 (1077331), страница 53
Текст из файла (страница 53)
В строке документирования модуля приводится его описание, вслед за которым находятся доктесты, упомянутые выше. Следующий ниже программный код начинается двумя инструкциями импорта: одна импортирует модуль вув, а другая — модуль воЬргосевз. Модуль воЬргосезв подробно будет рассматриваться в главе 9. В модуле используется две тактики обработки ошибок. Некоторые функции имеют параметр типа ела г, то есть фактически строку, содержащую единственный символ. Нарушение этого требования рассматривается как фатальная ошибка программирования, поэтому для проверки длины аргументов используется инструкция азвег!. Передача номеров строк и столбцов со значениями, выходящими за пределы сетки, хотя и считается ошибкой, но рассматривается как нормальная ситуация, поэтому в подобных случаях возбуждается наше собственное исключение.
Теперь мы рассмотрим наиболее показательные и наиболее важные фрагменты программного кода модуля, начав с исключений: с1авв йапдеЕггог(Ехсерггоп): разо с1авв йо«йапдеЕггог(йапдеЕггог): рава о1авв СЬ1оппйапдеЕггог(йапдеЕггог). Разо Ни одна из функций в модуле, возбуждающих исключения, не возбуждает исключение НапдеЕггог, они всегда возбуждают конкретное исключение в зависимости от того, номер строки или столбца вышел за пределы сетки.
Но, используя существующую иерархию исключений, мы даем пользователю модуля возможность выбирать, будет ли он обрабатывать конкретные исключения или перехватывать их по базовому классу йапдеЕггог. Обратите также внимание на то, что внутри доктестов используются неквалифицированные имена исключений, но когда модуль импортируется инструкцией !в рог! Сла г6 г10, необходимо использовать полные квалифицированные имена исключений: СПагСг)О. ЯзпдеЕг гог, СПагбг1О. ЯоийапдеЕггог и СпагСг! О. СЬ1озпйапдеЕггог. СНАЯ АБ8ЕЯТ ТЕНР!АТЕ = ("спаг зов! Ье а юпд1е спагастег: '(О)' "ш !Ьо 1опд") пдх гоюв = 25 пах со1оппв = 80 дги! = 1] Ьасхдгоопс СПвг = Здесь определяются некоторые частные данные для использования внутри модуля.
Имена частных переменных начинаются с символа подчеркивания, поэтому, когда модуль будет импортироваться инструкцией Тгов СпагСг18 !прог! *, ни одна из этих переменных не будет импортирована. (Как вариант, можно было бы использовать список а11 .) Переменная СНАЯ АВНЕНТ ТЕМРЕАТŠ— это строка предназначенная для вызова метода ввг. Тогза!(), — позднее мы увидим, что такой Модули и пакеты 24з прием широко используется для генерации сообщений об ошибках в инструкциях авве гт. Назначение остальных переменных будет поясняться по мере того, как мы будем сталкиваться с ними.
тт зуэ. р1аттага. Ьтагтэаттп(аа)п"): Сет с1еаг зсгееп(): зьсргосезэ.саП(["пас.ехе", "МС", "с1з"]) е1эе; се( с1еаг эсгееп(); зьЬргьсезз.са11(["с1еаг"]) с1еаг зсгееп. Ссс = """С1еагп тпе зсгееп ьз1пр тпе ьпсег1утпр 1 юпооа зуэтеа'з с1еаг зсгееп сьааапп""" Очистка экрана консоли в разных системах выполняется по-разному. В %'1пс[оагв необходимо выполнить программу сас. ехе с соответствующими аргументами, а в большинстве систем [1]х[[Х запускается программа с1еаг. Функция зьЬргосезз, са11() из модуля зьЬр госева позволяет запускать внешние программы, поэтому мы можем использовать ее для очистки экрана с учетом особенностей системы. Строка эуэ.
р1ат(ога хранит имя операционной системы, под управлением которой выполняется программа, например, «тч]п32» или «11ппх2». Поэтому один из способов учесть различия между платформами — определить функцию с1еаг зсгееп(), как показано ниже: Ьет с1еаг зсгееп(): ссааапь = (["с1еаг"] 1( пот пуз.р1аттсга.зтагтзатть("атп") е1зе ["саз.ехе", "ТС", "с1з"]) эьзргссезэ.са11(соааапз) Недостаток такого подхода заключается в том, что, даже зная, что тип платформы не изменится в процессе работы программы, мы все равно вынуждены выполнять проверку при каждом вызове функции. Чтобы избежать необходимости проверки типа операционной системы, под управлением которой выполняется программа, при каждом вызове функции с1еаг зсгееп(), мы создаем платформозависимую функцию с1еаг зсгееп() на этапе импортирования модуля и с этого момента постоянно используем ее. Это возможно благодаря тому, что в языке Ру- 1]гоп инструкция Зег является самой обычной инструкцией — когда интерпретатор достигает условной инструкции 1(, он выполняет либо первую, либо вторую инструкцию ое(, динамически создавая ту или иную версию функции с1еаг эсгееп().
Так как определение функции находится за пределами какой-либо другой функции (или класса, очем будет рассказываться в следующей главе), она по-прежнему остается в глобальной области видимости и обращаться к ней можно так же, как к любой другой функции в модуле. После создания функции мы явно определяем строку документирования для нее — такой прием позволяет избежать необходимости дважды записывать одну и ту же строку документирования в двух местах, а, кроме того, иллюстрирует, что строка документирования — это всего 246 Глава 5.
Модули лишь атрибут функции. В число прочих атрибутов входят имя модуля функции и ее собственное имя. Оет гав[аз(щах гоив, щах со!ощпв, сиаг=мопе): """Изменяет размер сетки, очищает содержимое и изменяет символ фона, если аргумент спаг не равен Ноле аввегт мах грив > О апо щах со1ощпв > О, "гоо вма11" ц1оЬа1 цг1О, щах гома, щах со1ощпв, Ьасидгоопо соаг тт сяаг 1в пот Мопв: аввегт 1еп(спаг) == т, СНАМ АЯЗЕВт ТЕМР[АТЕ.(отжат(спаг) ьаскдгоопо сьаг = сваг щах гоив = щах гоив щах со1ощпв = щах со!омпв цгтс = [[ Ьасицгоопс спаг Тот со1ощп вп гапца( щах со!ощпв)] Гог гои тп гапда( щах гоив)] Эта функция использует инструкцию аввегт для обеспечения политики выявления ошибок программирования; в первом случае — ошибки прн попытке установить размеры сетки меньше, чем 1х1.
Если символ фона определен, применяется еще одна инструкция аввегг, чтобы гарантировать, что эта строка содержит точно один символ; в противном случае возбуждается исключение с текстом сообщения из шаблона СНЯВ АБСЕНТ ТЕМР[АТЕ, в котором поле (0) замещается полученной строкой слаг. К сожалению, мы вынуждены использовать инструкцию ц1оЬа1, потому что внутри этой функции приходится изменять глобальные переменные. Это как раз тот случай, когда на помощь может прийти объектно-ориентированное программирование, с которым мы познакомимся в главе 6.
о Содержимое для переменной д гад создается с помощью Генераторы списков, двух вложенных друг в друга генераторов списков. Пристр. 142 ем с применением оператора дублирования списка, такой как [[спаг] * со1ощпв] * гощв, не даст должного результата, потому что внутренние списки будут представлять собой всего лишь поверхностные копии одного и того же списка.
Вместо генераторов списков можно было бы использовать вложенные циклы рог ... тп: дпо = [] Гог гон тп галде( щах гоив). цпо.аррепо([]) Гог со1ощп тп гансе( щах со!ивов). дпс[- 1].аррепс( Ьасицгоопс сиаг) Но такой фрагмент сложнее для понимания и гораздо длиннее, чем генераторы списков. Поскольку основной целью нашего рассмотрения является реализация модуля, мы рассмотрим лишь одну функцию рисования, чтобы Модули и пакеты получить представление о том, как это рисование выполняется. Ниже приводится функция аОО Ьог!золта! 1!Ье(), поделенная падве части: оет аоо ьогыопта1 1!пе(гом, со1иапО, со1ыапт, сваг="-"): """Добавляет в сетку горизонтальную линию, используя указанный символ »> асс Ьогыопта1 1!пе(8, 20, 25, "=") »> сьаг ат(8, 20) == сьаг ат(8, 24) == "=" Тгые »> аоо ьоызопта1 1!пе(31, !1, !2) тгасеьаск (аов! гесепт са11 1авт) ВонйапоеЕггог Строка документирования содержит два теста, один из которых, как предполагается, будет проходить успешно, а другой будет возбуждать исключение.
Задавая в доктестах исключения, необходимо вставлять строку «ТгасеЬас]с»; она всегда одна и та же и сообщает модулю Оостевт, что ожидается исключение. Затем взамен строк с диагностическими сообщениями (количество которых может быть разным) следует указать многоточие и завершить тест строкой с именем исключения, которое ожидается получить. Функция спаг а(() — одна из тех, что предоставляется этим модулем; она возвращает символ в заданной позиции строки и столбца сетки.
аввег! 1еп(сЬаг) == з, СВАЙ А88ЕВТ ТЕМР[АТЕ.(огаат(слег) тгу: Гог со1ыап !и гапйе(со!ыапО, со1ыапт): оста[гон][со!пап] = сваг ехсерт 1псехЕггог; тт пот 0 <= гон <= аах гоив: га!зе ВоийапйеЕггог() га!ве Со!ыапйапйеЕггог() Реализация функции начинается с той же проверки длины аргумента слаг, которая производилась и в функции гез!зе().
Вместо того чтобы явно проверять аргументы с номерами строки и столбцов, функция работает в предположении, что аргументы имеют допустимые значения. Если из-за обращения к несуществующей строке или столбцу возбуждается исключение 1пбехЕггог, функция перехватывает его и возбуждает соответствующее исключение, характерное для модуля. Такой стиль программирования соответствует выражению «проще попросить прощения, чем разрешения» и считается более свойственным программированию на языке Ру(Ьоп, чем стиль «осмотрись, прежде чем прыгнуть», при котором проверки выполняются заранее. Реализация с опорой на исключения вместо предварительной проверки является более эффективной, когда исключения возникают достаточно редко.
(Контрольные инструкции аввег! мы не относим к стилю «осмотрись, прежде чем прыгнуть», потому что такие ошибки никогда не 248 Глава 5.Модули должны возникать и они часто убираются из окончательной версии программного кода.) Практически в самом конце модуля, после определения всех функций, имеется единственный вызов функции гев1ге(): гевые( аах гсав, аах со1сапв) Этот вызов инициализирует сетку с размерами по умолчанию (25х80), чем обеспечивает безопасное использование модуля в импортирующем программном коде. Без этого вызова импортирующая программа или модуль должны были бы явно вызывать функцию гев1ге() для инициализации сетки, что вынуждало бы программистов помнить об этом факте и приводило бы к множественным попыткам инициализации. ы паве == " аа1п 1авогг Сосгевг сосгева.тевгаос( 1 Последние три строки в модуле являются обычными для модулей, использующих модуль Еосгевс для выполнения доктестов.