Лутц М. - Изучаем Python (1077325), страница 142
Текст из файла (страница 142)
Поскольку это класс, он также поддерживает возможность создания множества экземпляров и адаптацию своего поведения наследованием в подклассах: с1авв Зет: бе( !птт (ве1(, ча1ое = [)): № Конструктор ве1т.бата = [) № Управляет слискои ве1(.сопсат(ча1ое) бет !птегвест(ве1Г, о!пег]: гев = [! Гог х !и ве1(.бата, !г х тп отлет: гев.аррепб(х) гетогп Вет(гев) № о!Пег — любая последовательность № эе1 Г - подраэуиеваеиий обьект № Выбрать обвив элеиенты № Вернуть новый экэеилляр Вет № оглег - любая последовательность № Копироват~ список № Добавить элементы иэ о!Пег бет сопсат(ве1Г, ча1че): тог х 1п ча1ие; [т пот х тп ве1(.бата. ве1(.бата.аррепб(х) № Лргуиент ча1ыег список, Вет..
№ Удалить дубликаты бег 1еп (ве1Г): гетогп 1еп(ве1(.бата) бе( рет!тея (ве1Г, Иеу): гетогп ве11 бата[кеу] бег апб (ве1(, о!пег); гетогп ве11.!птегвест(о!пег) бег ог (ве1(, о!пег). гетогп ве1т.оп!оп(отлет) бет герт (ве1г); гетогп 'Вес;' + 'ве1(.ба!а' № 1еп(ве1г) № ве1Г[!) № ве)Г б о!лег № ве)г ( отлет № Вывод Перегрузка операции доступа к элементам по их индексам позволяет экземплярам нашего класса яет выглядеть как настоящие списки. В упражнениях в конце этой главы вам будет предложено организо- бег опюп(ве11, отлет): гев = ве1т.бата[:) тог х тп о!пег: !Г пот х 1п гев: гев.аррепб(х) гетчгп Зет(гев) Глава 26. Дополнительные возможности классов 663 Расширение встроенных типов вать взаимодействие с этим классом и расширить его, поэтому подроб- нее об этом фрагменте мы поговорим в приложении В.
Расширение типов наследованием Начиная с версии Руб]топ 2.2 все встроенные типы можно наследовать. Функции преобразования типов, такие как 1!вс, в1г, б!сс и сор1е, превратились в имена встроенных типов. Теперь вызов функции преобразования типа [например, 11в1('враа')) в действительности является вызовом конструктора типа. а Подкласс встроенного типа/класса 1!в!. и Отобракает диапазон 1 .М на О..М-1, вызывает встроенную версию. о1авв муывт(1тв!): оег демгею (ве11, о!гает): ргтп! '(тпоех1пд %в ас %в)' % (ве1(, о(гает) гетогп 1зы, де!!!ею (ве11, ог(ве! - 1) тт паве == ' юатп рг!пт 1!в!('аЬс') х = Му[!в!('або') рып! х № зпзт наследуется из списка № герт маследуется из списки ргтпт х[1] рг!п! х[3] № иу!звт де!!!ею № Изменяет поведение метода суперкласса х.аррепо('враю'), рг!л! х № атрибуты, унаследованные № от суперкласса списка х,гекегве(); рг!ль х В этом файле подкласс Му!!в! расширяет метод де!!Сею встроенных списков простым отображением диапазона значений от 1 до Х на необходимый спискам диапазон от О до Х-1.
Уменьшение индекса на единицу и вызов версии метода из суперкласса — вот все, что в действительности делается, но этого вполне достаточно для достижения поставленной цели: % рутпоп !уревоЬо1авв. Ру ['а'. 'Ь', 'с'] ['а', 'ь', 'с'] (1пзех1пд ['а', 'Ь', 'с'] ат 1) а (1пвехтпд ['а', 'Ь', 'с'] а! 3) Это изменение позволяет адаптировать или расширять поведение встроенных типов с помощью инструкций с1авв: достаточно просто создать подклассы с новыми именами типов, где реализовать необходимые изменения. Экземпляры вашего нового подкласса могут использоваться везде, где допускается использовать оригинальный встроенный тип. Например, предположим, вас не устраивает тот факт, что стандартные списки начинают отсчет элементов с О, а не с 1.
Это не проблема — вы всегда можете создать свой подкласс, который изменяет эту характерную особенность списков. В файле !уревибс[авв.ру показано, как это делается: 664 Глава 26. Дополнительные возможности классов с [ а, Ь, с', вращ'] [ враю', 'с, 'о', 'а'] с1авв Зет(1твт): бе( готт (ве1(, ча1ое = []): № конструктор 1твт. готт ([]) № ддвптирует список ве!б сопсат(ча1ое) № копировать изменяеммй аргумент ло умолчанию бе( тптегвест(ве!;, отпег): гев = [] Гог х тп ве)Г тт х ш отпег: гов аррепб(х) ге(ого Зет(гев) № отлет - любая последовательность № зе)Г - подразумеваеимй обьект № Вмбрать общие злвнентм № Вернуть новмй экземпляр Вет Оег потоп(ве1(.
отлет) гев = Зет(яе1() гев,сопсат(отлет) гетогп гев № отлет — любая последоввтельность № Копировать меня и мой список се( сопсат(ве1(, ча1ое): тот х тп ча1ое. гт пот х гп ве1( ве)т.аррепо(х) № аргумент ча)ое: 1тят, Вет... № удалить дубликата сег апб (ве1(, отьег): гетогп ве1( тптегвест(отьег) оет ог (ве1(, отьег), гетогп ве1п потоп(отлет) оет герт (ве1(): ге(огп 'зет.' + 1твт. герт (ве1() тт паве == ' щатп х = Зет([1,3,5,7]) у = Зет([2, 1,4,5,5]) рыпт х, у, 1еп(х) ргтпт х.тптегвест(у), у.потоп(х) ргтпт х я у, х ) у х гечегве(); ргтпт х В эти результаты включен текст, который выводит метод класса при выполнении индексирования. Является ли изменение способа индексирования универсально хорошей идеей, это уже другой вопрос: пользователи вашего класса Иу[тят могут быть повергнуты в недоумение таким отступлением от общепринятого поведения последовательностей в языке Ру1]топ.
Однако возможность адаптировать встроенные типы подобным образом может оказаться мощным инструментом. Например, такой шаблон дает начало новому способу реализации множеств — в виде подкласса встроенного списка, а не в виде самостоятельного класса, который управляет встроенным в него объектом списка. Следующий пример реализации класса в файле зетви[гс1аав.ру адаптирует списки, добавляя методы и операторы, используемые для работы с множествами. Все остальное поведение наследуется от суперкласса 11ят, поэтому альтернатива получилась более короткой и простой: 665 Псевдочастные атрибуты класса Ниже приводится вывод, полученный в результате выполнения кода самопроверки, находящегося в конце файла.
Поскольку проблема наследования встроенных типов достаточно сложна, я опущу дальнейшие подробности, но я предлагаю внимательно посмотреть на полученные результаты, чтобы изучить поведение подкласса: % рутпоп зетззвс1азз.ру 5ет .[ 1, 3, 5, 7 ] Вет [ 2, 1. 4, 5 . 6 ] 4 5ет [1. 5] 5ет;[2. 1, 4, 5, 6, 3. 7] 6ет;[1. 5] 5ет;[1, 3, 5, 7, 2, 4, 6] Зет [7, 5, 3, 1] Есть и более эффективные способы реализации множеств — с помощью словарей, позволяющих заменить последовательное сканирование, используемое в данной реализации, на операцию обращения по ключу (хеширование) н тем самым повысить скорость работы. (За дополнительной информацией обращайтесь к книге «Ргоягавпп1пя Ру1]топ».) Если вас заинтересовали множества, тогда вам также стоит взглянуть на тип объектов вет, который рассматривался в главе б, — этот встроенный тип реализует операции над множествами.
Реализация операций над множествами прекрасно подходит для нужд обучения, но создавать такие реализации в современных версиях Ру1]топ больше не требуется. В качестве другого примера наследования можно привести новый тип Ьоо1, появившийся в Ру[]топ 2.3. Как упоминалось ранее в этой книге, Ьоо1 — это подкласс типа [пт с двумя экземплярами (Т гое и га1ве), которые ведут себя как целые числа 1 и О, но наследуют измененные версии методов вывода, особым образом отображающие их имена. Псевдочастные атрибуты класса В четвертой части книги мы узнали, что все имена, для которых выполняется присваивание на верхнем уровне модуля, становятся глобальными для этого модуля. 'Го же самое по умолчанию относится и к классам — сокрытие данных регулируется соглашениями, и клиенты могут получать и изменять любые атрибуты класса или экземпляра по своему усмотрению. Фактически все атрибуты являются «общедоступными» (рпЫ1с) и «виртуальными» (ч1г1па1), если говорить в терминах языка С++, — они доступны отовсюду и динамически отыскиваются во время выполнения.' Это излишне пугает программистоз, работающих с языком С++.
В языке Рут]топ возможно даже изменить нлн полностью удалить метод класса ао время выполнения программы. С другой стороны, почти никто ве использует такие возможности на практике. Как язык сценариев, Рут]топ больше печется о том, чтобы позволить, а не запретить. Кроме того, вспомните обсуждение перегрузки операторов з главе 24, где говорилось, что методы Оетзттг н зетзтт г могут использоваться для имитации поведения частных атрибутов, но на практике эта возможность обычно ве используется.