Саммерфилд - Программирование на Python 3 (1077331), страница 92
Текст из файла (страница 92)
сег ороасе споепсес 1свс(епсгу): 1поепсео 11вс.аорепс(епсгу[1тен]) гог воьепсгу сп вогсео(епсгу[сн1[Ояен]): ороасе споепсео 1свс(воьепсгу) На первой стадии алгоритма создается список элементов, каждый из которых представлен кортежем из трех элементов (ключ, элемент, 414 Глава 8. Усовершенствованные приемы программирования список дочерних элементов), следующих в том же порядке, в каком они находятся в оригинальном списке.
Во второй стадии, с пустым списком результата в начале, выполняются итерации через отсортированный список вп1г(ев, и для каждого элемента вызывается функция арба!е (пбеп1еб 1(в!(), которая выстраивает новый список с результатами. Функция арба!е !пбеп!еб 1(в!() является рекурсивной. Она добавляет каждый элемент верхнего уровня в список 1пбвп!еб 1!в!, после чего вызывает саму себя, чтобы добавить все элементы из списка дочерних элементов.
Добавив очередной дочерний элемент в список 1пбеп1еб 11ат, функция вновь вызывает саму себя, чтобы добавить дочерние элементы этого дочернего элемента, и т. д. Базовый случай (когда прекращается рекурсия) наступает, когда элемент, или дочерний элемент, или дочерний элемент дочернего элемента (и так далее), не имеет дочерних элементов. Интерпретатор пытается отыскать список (пбептеб 1(а! в локальной области видимости (во внутренней функции) и не находит его; тогда он пытается отыскать список в объемлющей области видимости и находит его там. Но, обратите внимание, что во внутренней функции производится добавление элементов в список тпбеп!еб 1!в!, хотя инструкция поп1оса1 при этом не использовалась, Это возможно потому, что инструкция поп1оса1 (как и инструкция 01ова1) применяется к ссылкам на объекты, а не к самим объектам, на которые они ссылаются.
Во второй версии функции абб еп! гу() мы использовали инструкцию поп1оса1 для переменной 1ече1 потому, что применяемый оператор 4= связывает ссылку на объект с новым объектом, то есть в действительности выполняется операция 1ечв1 = 1ече1 4 1, поэтому в переменную1ече1 записывается ссылка на новый объект типа 1пт, Но когда вызывается метод 11а!. аррепб() для списка 1пбеп!еб 11ат, изменяется сам список, то есть повторного присваивания ссылки здесь не происходит, и потому нет необходимости в использовании инструкции поп1оса1. (По тем же самым причинам, если у нас имеется словарь, список или другая глобальная коллекция, мы можем добавлять и удалять элементы коллекции без использования инструкции 61ова1,) Декораторы функций и методов о Декоратор — это функция, которая принимает функцию или метод в качестве единственного аргумента и возврастр.
438 щает новую функцию или метод, включающую декорированную функцию или метод, с дополнительными функциональными возможностями. Нам уже приходилось использовать некоторые предопределенные декораторы, например, Зргорег!у и Юс1аввпе!Поб. В этом подразделе мы узнаем, как создавать собственные декораторы функций, а позднее в этой главе узнаем, как создавать декораторы классов. 415 Улучшенные приемы процедурного программирования Для первого примера декоратора предположим, что у нас имеется множество функций, выполняющих вычисления, и некоторые из них всегда должны возвращать положительный результат.
Мы могли бы добавить в каждую из таких функций инструкцию авве гг, но использование декораторов проще и понятнее. Ниже приводится функция, декорированная декоратором Фроа(1(ие геви!1, который будет создан чуть ниже: эроа111иа геаи)1 Се( 01асг1а1пап1(а, Ь, с): гагигп (Ь " 2) - (4 * а с) Благодаря декоратору, если функция вернет отрицательный результат, будет возбуждено исключение Аваег11опЕггаг и программа завершит работу. И конечно, этот декоратор можно применить к любому числу функций. Ниже приводится реализация декоратора: иег роюыиа гааи11(гипсыип): иеГ нгаррег(*агца, **$магда): геаи11 = гипс11оп(.агца, *ммагца) аааагг гааи11 >= О, гипс11ип, паве а "() геаи11 1ап'1 >= О гагигп геаи11 нгарраг. папе = Гипс(!сп.
папе игаррег, иьс = гипс11сп. исс гегигп нгаррег Декоратор определяет новую локальную функцию, которая вызывает оригинальную функцию, В данном случае объявляется локальная функция нгаррег(), Она вызывает оригинальную функцию и запоминает результат, который используется в инструкции аавег(, проверяющей результат на положительность (или на необходимость завершить программу). Функция нгаррег() просто возвращает результат, полученный от декорируемой функции. После создания функции ига ррег() ее имя и строка документирования приводятся в соответствие с оригинальной функцией. Этим обеспечивается содействие механизму интроспекции, то есть в сообщениях об ошибках будет фигурировать имя оригинальной функции, а не функции нгаррег(). Наконец декоратор возвращает функцию нгаррег() — с этого момента она будет использоваться взамен оригинальной.
иег рсаы1ие геаи11(гипс(1ип); эгипсгсс1а.агапа(гипс11ип) иеГ нгаррег(.агда, **Кнагца): геаи11 = гипс1!Ьп(.агца, "киагда) аааег1 геаи11 >= О, гипс1(ип, паве + "() геви11 1ап'1 >= О" ге1игп геаи11 ге(игл нгарраг Выше приводится немного более понятная версия декоратора Эроа(11- ие геаи11. На этот раз функция нгаррег() сама обертывается декоратором О(ипсгос1в.нгарв из модуля (ипсгсс1а, который гарантирует, что 416 Глава 8, Усовершенствованные приемы программирования функция вгаррег() будет носить имя оригинальной функции и содержать ее строку документирования.
В некоторых случаях бывает удобно иметь параметризуемый декоратор, но, на первый взгляд, это кажется невозможным, так как декораторы принимают единственный аргумент — функцию или метод. У этой проблемы имеется замечательное решение. Мы можем вызвать функцию с требуемыми параметрами, и она вернет декоратор, который будет использован для декорирования следующей за ним функции. Например: эьсипбеб(0, 100) баг регсепс(авсипс, соса1): гесигп (авсипс у соса1) 100 Здесь функция Ьсипбеб() вызывается с двумя аргументами и возвращает декоратор, который используется для декорирования функции регсепс(). Цель данного декоратора состоит в том, чтобы гарантировать, что возвращаемое значение всегда будет находиться в диапазоне от 0 до 100 включительно.
Ниже приводится реализация функции Ьсопбеб(): бет Ьсопбеб(в!п1вив, вах1вив): бет бессгатог(типот!оп): эгопссои1а.ясара(гипс!!оп) бет вгаррег(*агца, **Квасца): геаи11 = сипсс!оп( агца, "квасца) ст геаосс < в!испив; ге!игл в1п1вив е1ст геаи11 > вахсвив: ге!игл вах!вив гесигп геао11 ге!игл вгаррег ге!игл бесогассг Эта функция создает функцию-декоратор, которая в свою очередь создает функцию-обертку. Функция-обертка выполняет вычисления и возвращает результат, который гарантированно будет находиться в заданных пределах.
Функция бесогассг() возвращает функцию нгаррег(), а функция Ьсопбеб() возвращает декоратор. Следует отметить, что всякий раз, когда вызывается функция Ьсипбеб(), внутри нее создается новый экземпляр функции-обертки, которая получает минимальное и максимальное значения, переданные при вызове Ьсипбеб(). Последний декоратор, который будет создан в этом разделе, имеет немного более сложную реализацию. Это функция регистрации, которая записывает имя, аргументы и результат любой декорируемой функции. Например: Э1одцеб бег б1асоопсеб рг1са(рг1се, рагсепсаце, ваке спсецаг=еа1аа): 417 Улучшенные приемы процедурного программирования гево11 = рг!се ((100 - регсепгаде) / 100) >г па1 (О < гево1! <= Рг1се): га>ве На1оеЕггаг(">пча11б рг1се") ге!ига гево11 >г пас яаке 1п!едег е1ве 1пг(гаипб(гево1!)) са11еб: б>всаоп1еб рг1се(100, са1!еб: б1всаипгеб рг!се(210, са1!еб: б>всаипгеб рг>се(210.
са1!еб: б!всаопгеб рг!се(210, са11еб: б>всаипгеб рг1се(210, 10) -> 90.0 5) -> 199.5 5, ааКе гпгедег=Тгое) -> 200 1П, Тгие) -> 181 -8) <1уре 'Не1оеЕггаг'>: !пче!>б рг1се Если интерпретатор выполняет программу в оптимизированном режиме (используется ключ командной строки -О или переменная окружения РУТНОИОРТ1М17Е содержит значение -О'), то регистрация отключается. Ниже приводится программный код, выполняющий настройку механизма регистрации и определение самого декоратора: д( беЬод 1аддег = 1а99!пя.яе1(ая9ег(ТЕа99ег ) 1аддег.ве((ече!(1а991пд.ОЕВВЯ) Ьвпб1ег = 1адд!пд.я!!енепб!ег(ав.раси.)а>п( (еарг!1е.яе11еврбгг(), "1а99еб. 1ад")) 1аддег.вббявпб1ег(папб1ег) бе( !аддеб((опс(>ап): Е(ипс1аа1в.игерв(Гипс11ап) бе( нгаррег(-агдв, **Кнвгдв); 1ад = "са11еб: " в (опас!ап.,паве х "(" 1ад г= ", ".!Ь>п(["(О! г)".гагпег(а) гаг а 1п агдв] + [ (О! в)=(1! Г) '.
гагад1(к, ч) Гаг К, ч гп Кнагдв.!(еав()]) гево11 = ехсер1!ап = Капе вгу: гево11 = гипс!!ап( агдв, .*кнегдв) гегогп гево11 ЕхСЕР1 Ехсер1!Ьп ав егг: ЕхСЕР11ап = егг Г>паыу: 1ад я= ((") -> " ч вгг(гево11)) 1( ехсерг>ап !в напе е1ве ") (0): (1)".гагае1((уре(ехсерг!ап), ехсер!>ап)) !аддег.беЬод(1ад) !Г ехсер(!ап Ев пас папе: га!ве ехсер1!ап В абаих случаях вто буква О, а не цифра О.
— Прим. перев. Если интерпретатор выполняет программу в отладочном режиме (обычный режим), то при каждом вызове функции б!Всоип(еб рг1се() в файл 1о55е<1.105', находящийся во временном каталоге, будет записываться сообщение, как показано в следующей выдержке из этого файла: 418 Глава 8. Усовершенствованные приемы программирования гетпгп нгаррег е1ве: Пет 1оддез(тппст1пп): гетпгп гппст1оп При работе в отладочном режиме переменная деборд имеет значение Тгпе. В этом случае выполняется настройка механизма регистрации с использованием модуля 1одд(пд, и затем создается декоратор 61оддес. Модуль 1одд1пд обладает очень широкими возможностями — он может записывать сообщения в файлы, выполнять ротацию файлов, отправлять сообщения по электронной почте, через сетевые соединения, серверам НТТР и многое другое.