Саммерфилд - Программирование на Python 3 (1077331), страница 67
Текст из файла (страница 67)
Это не соответствует поведению объектов класса ЕсггуВоо1, поэтому мы переопределили этот метод. Точно так же функция!Ьт(ве1Г) будет просто отбрасывать дробную часть, превращая в 0 любое значение, кроме 1.0, поэтому здесь мы используем функцию 303 Собственные классы гоьпз(), чтобы округлять до 0 любое значение, меньшее или равное О. 5, и до 1 — значения, большие 0.5. Мы не стали переопределять методы Ьавл (), Гсгзат () и те методы, которые обеспечивают поддержку операторов сравнения, поскольку реализации этих методов в классе Г1оат корректно работают применительно к классу ЕьггуВОЬ1. Методы, которые мы переопределили, обеспечивают полную реализацию класса ЕсвауВОО1, и для этого потребовалось меньше программного кода, чем было представлено в предыдущем подподразделе.
Однако этот новый класс ЕьггуВЬО1 наследует более 30 методов, которые длл него не имеют смысла. Например, ни один из арифметических операторов или операторов сдвига (», —, *, г', «, » и т. д.) неприменим к объектам класса ЕпаауВЬЬ1. Ниже показано, как «исключается» реализация операции сложения: ааг асс (вв1Г, осьег): га1ве Ио(1ар1впептеСЕггог() Мы могли бы написать точно такой же программный код длл методов 1аоз () и гало (), чтобы полностью исключить возможность сложении. (Обратите внимание, что ИОТ1вр1езептеЬЕггаг — это стандартное исключение и оно полностью отличается от объекта Ио(1вр1евептео.) Чтобы более точно имитировать поведение встроенных классов языка РуЫтоп, вместо исключения Ио(1зр1евептеСЕггог можно возбуждать исключение ТуреЕггог.
Ниже показано, как можно было бы заставить метод ЕсвауВЬО1. аОС () вести себя так же, как встроенные классы, которые используются в недопустимой операции: Саг авз (ве1Г, о(пег): гаазе турееггсг("ьпвпррсгсев орагапс туре(в) Гог »: "'(О)' апс '(1)'".Гогпат( ва1Г, с1аяв . паве , отьег. Ь1авв . папе )) Реализация одноместных операций, которые требуется исключить, так чтобы они имитировали поведение встроенных типов, выглядит немного проще: Свг пер (ве!Г). гатве турееггог("ьас орегапс туре Гог влагу -: '(О)'".Гогзат( ва1Г.
Ь1авв . паве )) При реализации операторов сравнения используется намного более простой прием. Например, исключить поддержку оператора == мы могли бы так: сег еп (ве)Г, стьаг); гетьгп иот!ар1евептав Если метод, реализующий оператор сравнения («, =, ==,! =, » ), возвращает встроенный объект Ист!зр1евептео, то при попытке использовать этот метод интерпретатор сначала попытается выполнить сравни- 304 Глава б. Объектно-ориентированное программирование ванне, поменяв операнды местами (на случай, если у объекта о(пег имеется подходящий метод сравнения), а затем, если этот прием не поможет, возбудит исключение ТуреЕггог с сообщением, поясняющим, что данная операция не поддерживается для операндов этих типов. Но во всех остальных методах, не имеющих отношения к сравнению и которые требуется исключить, мы должны возбуждать либо исключение Нот!ар1евептеСЕггсг, либо исключение ТуреЕггог, как это было сделано в методах аэс () и пед () выше.
Однако было бы слишком утомительно исключать каждый нежелательный метод, как было показано выше, хотя такой подход работает и в программном коде выглядит понятнее. Теперь мы рассмотрим более совершенную методику исключения методов — она используется в модуле ЕсгауВ001А11, но для вас, пожалуй, было бы лучше перейти к следующему разделу (стр. 306) и вернуться сюда, если потребуется взглянуть на практический пример. Ниже приводится программный код, выполняющий исключение двух ненужных нам одноместных операций: тсг паве, орагатаг тп ((" пед ", "-"), (' вппех , тпсех() )): аяввадя = (Гзаа орегапя тура Гог влагу (О); '((ве1())'" .Гогаат(прагатог)) яхас("яя( (0)(вя1О: га!ва ТуряЕггог(Х"(1)'Х".Гогаат(" "яя!(ъве1(.
с!аяя . папа ))Г.гогаат(папа, аяявадя)) Встроенная функция ехес() динамически выполняет программный код из передаваемого ей объекта. В данном случае — это строка, но это могут быть объекты и некоторых других типов. По умолчанию программный код выполняется в объемлющем контексте, в данном случае — внутри определения класса ЕпггуВ001, поэтому выполняемые инструкции Сег будут создавать методы класса ЕпггуВ001, что нам и требуется. Программный код будет выполняться всего один раз, во время импортирования модуля ЕсггуВ001А11.
Ниже приводится программный код, который будет сгенерирован первым кортежем (" пед ", "-"): сяг пад (яя!г) га1вя ТуреЕггог(ГЬаа орагапа Суре Гог ипагу -; '(ве!Г)'' .(пгаат(ва!т=вя1(. с1авв . папа )) Здесь мы возбуждаем исключение с текстом сообщения, соответствующим тому, которое используется интерпретатором Ру()топ для своих собственных типов. Программный код, обрабатывающий двухместные методы и л-местные функции (такие как рои()), следует тому же шаблону, но с другим сообщением об ошибке, Для полноты картины ниже приводится программный код, который мы использовали: Собственные классы 305 тог паве, орегатог 1п ((" хог ", " '), (" тхог ', ' = ). (" аея ", "»"), (" 1аОО ", т»ы'), (" гаОО ', "+"), (" грен ", " ° ° "), (" (1оогстч ", "//"), (" т(1оогстч ", "//="), (" гт)ооге ч,", "О"), (" тгоеотч ", "/"), (" !!гово(ч ", "/="), (" гтгоее1ч ", "/"), (" 01чпое ", "О(чаос()"), (" гстчеое ", "Отчеос()"), (" еос ", "96"), (" твое ", таы'), (" гаое ", "96"), (" 1впттс ", "«"), (" ! 1вптгт ", "«ы'), (" г)вот(С ", "«"), (" гв)ПГС ", "»"), (" 1гвп(тт ", "»="), (" ггвпттт ", "»")): аевваде = ( ипворрог(ео орегапо туре(в) гог (О); "'((ве)Г))'(()о1п)) ((агдя))".Гагпат(орегатог)) ехес("Оет (0)(ве19, агдв):1п" !урез = (>,"'9," » агц.
о1авв , папе "гог агц тп агдз]Тп" га1ве турееггог(<"(!)Т".Тогаат(" "ве1(=ве1(. с1авв . паве ")ото=(>," апе1" 1т 1еп(аг9в) == ! е1ве 9",'9")," 'агдв=<", '9".)отп((урез)))",Гогеат(паве, еевваде)) Программный код получился немного более сложным, чем прежде, потому что для двухместных операторов мы должны выводить сообщения, где перечислены оба типа„как гуре1 аи<1 (уре2, а в случае трех и более типов, мы должны выводить их как Суре1, суре2, суреЗ, чтобы имитировать поведение встроенных типов. Ниже приводится программный код, сгенерированный для первого кортежа (" хог,", " "): Оет хог (ве1(, *агре): турея = ("'" + агд, с1авв .
папе » "'" гог агд тп агця] гатве ТуреЕггог("опворрогтес орегапе Суре(в) (ог "'(ве1()'()о(п) (агдв)".То<пас( ве1(=ве1(, о1авв , папе )отп=(" апе" 19 1еп(агдв) == 1 е1ве ","), агдв=", ".)отп(!урез))) Два цикла (ог ... тп, которые мы испольэовали здесь, можно просто копировать и затем добавлять или удалять одноместные операторы и методы в первом цикле и двухместные и и-местные операторы и методы — во втором, чтобы исключать реализации ненужных методов. Теперь, благодаря этому последнему фрагменту программного кода, если бы у нас имелись два объекта ЕоккуВ001, Т и 9, и мы попытались сложить их, используя выражение Т» 9, то получили бы исключение ТуреЕггог с сообщением»ппвпррог1ес( орегапс( Фуре(в) Хог+! 'гиккуВооГ апс] г иккуВооГ» (неподдерживаемые типы операндов для оператора +: 'гиккуВооГ и 'г цккуВооГ)„то есть именно то, что нам и требуется.
Зоб Глава 6. Объектно-ориентированное программирование Способ создания классов, который мы использовали первым при реализации класса ГааауВоо1, распространен намного шире и пригоден для создания практически любых типов. Однако если требуется создать класс неизменяемых объектов, основной способ такой реализации заключается в переопределении метода оЬ)ест, пен (), наследовании одного из неизменяемых типов языка Ру(Ьоп, таких как Г1оат, 1пт, вт г или твр1е, с последующей реализацией всех необходимых методов. Недостаток такого подхода заключается в том, что может потребоваться исключить реализации некоторых методов, а это приведет к нарушению полиморфизма; поэтому в большинстве случаев вариант на основе агрегирования, использованного в первой реализации класса ГваауВоо1, является намного более предпочтительным.
Собственные классы коллекций В подразделах этого раздела мы рассмотрим порядок создания классов, способных хранить большие объемы данных. Первый класс, который будет рассмотрен, — это класс 1ваВе, один из тех, что способны хранить изображения. Этот класс является типичным представителем многих классов, используемых для хранения данных, в том смысле, что он не только обеспечивает доступ к данным в памяти, но также имеет методы сохранения данных на диск и загрузки данных с диска. Второй и третий классы, которые мы изучим, Вогтеск(зт и ВогтеЕВ(ст, предназначены, чтобы восполнить редкий, вызывающий удивление недостаток — отсутствие изначально отсортированных коллекций в стандартной библиотеке Ру(Ьоп.
Создание классов, включающих коллекции Простейшим представлением 2-мерных цветных изображений является двухмерный массив, каждый элемент которого хранит значение цвета. 7о есть чтобы представить изображение размером 100х100, нам придется хранить 10 000 значений цвета. При создании класса 1ваое (в файле 1тауе ру) мы выбрали потенциально более эффективный подход. Класс 1ваое хранит одно значение цвета для фона плюс те пиксели изображения, цвет которых отличается от цвета фона.
Реализовано это с помощью словаря, своего рода разреженного массива, каждый ключ которого представляет координаты (х, у), а значение определяет цвет в точке с этими координатами. Если представить изображение размером 100х100, в котором половина пикселей имеют цвет фона, то нам потребуется хранить всего 5 000 + 1 значение цвета, что дает существенную экономию занимаемой памяти.
о Модуль |тоде.ру следует уже знакомому нам шаблону: рта К1е, он начинается со строки «зЬеЬапя», далее следует инств 341 формация об авторских правах в виде комментариев, затем — строка документирования модуля, и затем — инст- Собственные классы коллекций 307 рукции импорта, в данном случае импортируются модули ов и ртсй1е. Мы коротко опишем модуль рте)<1е, когда будем обсуждать вопросы сохранения и загрузки изображений.