Саммерфилд - Программирование на Python 3 (1077331), страница 70
Текст из файла (страница 70)
Кеу = Кеу сг !пел!!ту аввегт паваттг(ве1(. Кеу, " са11 ") !т вецсепсе !в лопе: ве1(. 1!в! = [] е1тт (1в!пвтапсе(вецсепсе, Бог!ее[!вт) апц вецсепсе.веу == ве1(. Кеу): Глава 6. Объектно-ориентированное программирование ве!г. 11ы = аеооепсе. 1тат(: ) е1ве: ве1г. 11вт = воггео(11вт(веооепсе), кеу=ве1г. кеу) Поскольку имя функции является ссылкой на объект (то есть на функцию), мы можем хранить функции в переменных как ссылки на любые другие объекты. Здесь частная переменная ве1(, Хеу хранит ссылку на ключевую функцию, передаваемую методу, или функцию идентичности.
Первая инструкция метода использует тот факт, что оператор ИЛИ возвращает первый операнд, если в логическом контексте он оценивается как Тгсе (то есть когда аргумент кеу имеет значение, отличное от г(опе), или второй операнд — в противном случае. Немного болеедлинный,ноболееочевидныйвариант:ве1(. 'кеу = кеу 1( Кеу тв пот Капе е1ве 1Оеп11ту. Теперь, когда мы определились с ключевой функцией, мы используем инструкцию аввегц чтобы убедиться, что эту функцию можно вызывать.
Встроенная функция Оаваттг() возвращает Тгое, если объект, переданный в первом аргументе, имеет атрибут, имя которого передается во втором аргументе. Имеются соответствующие функции ветатт г() и Ое1аттг(), которые будут описываться в главе 8. Все вызываемые объекты, такие как функции или методы, имеют атрибут са11 Чтобы порядок создания сделать максимально похожим на создание объектов типа 11вт, мы определили необязательный аргумент везоепсе, соответствующий единственному аргументу функции 1твт(). Класс Бэттери()вт включает в себя коллекцию типа 11вт в виде частной переменной ве1(. 11вт и сохраняет в этой переменной элементы в отсортированном порядке, используя заданную ключевую функцию.
Предложение е11г проверяет, является ли заданная последовательность объектом типа Богтег)((вт, и если это так, то проверяет, не используется ли для этой последовательности та же самая ключевая функция. Если последовательность удовлетворяет обоим критериям, то просто создается поверхностная копия последовательности без ее сортировки. Если большинство ключевых функций создается»на лету», с помощью инструкции 1ааоба, при сравнивании они не будут расцениваться как эквивалентные, даже если содержат один и тот же программный код, поэтому такая оптимизация на практике может не давать существенного эффекта.
вргореггу Оет Кеу(ве1Г): гетогп ве1Г. Кеу После создания сортированного списка его ключевая функция должна быть зафиксирована, поэтому мы сохраняем ее в частной переменной, чтобы предотвратить возможность ее изменения. Но у некоторых пользователей может возникнуть потребность получить ссылку на ключевую функцию (как будет показано в следующем подразделе), по- Собственные классы коллекций этому мы создаем свойство, доступное только для чтения, обеспечивающее такую возможность.
Се( або(эе!Г, ча1че): !псах = ве1(. ь!вест 1егт(ча1пе) 11 тпсех == !еп(ве1(. 1!э!): ве11. 11эт.аррепа(ча1ие) е1ве: ве11. 1!в!.1пвегт(тпсех, ча1ие) Когда вызывается этот метод, он должен вставить указанное значение в частный список ве1г, 11вт в нужную позицию, соблюдая порядок сортировки списка. Частный метод 8огтебс1вт. Ьтвест 1етт() возвращает индекс требуемой позиции, как будет показано чуть ниже. Если новое значение больше любого другого значения в списке, то его следует добавить в конец списка и в этом случае индекс позиции будет равен длине списка (номера позиций в списке начинаются с 0 и заканчиваются значением 1еп(1) - 1).
В этом случае добавление нового элемента должно выполняться методом п пепб(). В противном случае производится вставка нового значения в найденную позицию, которая может иметь порядковый номер О, если новое значение меньше любого другого значения в списке. се( мвест 1егт(ве1г, ча1че): Кеу = ве11. Кеу(ча1пе) 1егт, г1рш = О, 1еп(ве11. 1твт) чгвт1е 1етт < <трат: э!Со!е = (1етт + г1рж) О 2 11 ве11. кеу(ве!г. 1!вт(ешп!е)) < кеу; )етт = е!ас)е + т е1ве: <!опт = е1ОС!е гетпгп 1етт Этот частный метод вычисляет номер позиции в списке, куда должно быть вставлено новое значение.
Он вычисляет ключ для нового значения, используя ключевую функцию, и сравнивает с вычисленными ключами проверяемых элементов. Алгоритм, используемый методом, называется алгоритмом двоичного поиска (или поиск методом половинного деления), который обладает высокой скоростью даже в случае очень больших списков. Например, чтобы отыскать местоположение нового значения в списке, насчитывающем 1 000 000 элементов, требуется выполнить не более 21 сравнения.' Сравните это с простым, несортированным списком, для которого используется алгоритм линейного поиска, требующий выполнить в среднем 500 000 сравнений, Модуль Ывест, входящий в состав стандартной библиотеки языка Ру1)топ, содержит функцию ь(вест.
ь(вест 1егт() и ряд других функций, во к моменту написания этих строк ви одна из функций в модуле Ь! вест не обеспечивала возможность использовавиа ключевых функций. Глава 6. Обьектно-ориентироаанное программирование а в самом тяжелом случае — до 1 000 000 сравнений, чтобы отыскать местоположение нового значения в списке, насчитывающем 1 000 000 элементов. Сет геаоче(ве1(, ча1ое): 1псех = ве1(. отвес( 1егт(ча1пе) 1( 1псех < 1еп(ве!г.
11вт) апс ве1г. 11вт[1псех] == ча1ое: се1 ве1(. 11вт(1псех] е1ве: гатве ча1оееггог("(О),геаоче(х). х пот 1п 11вт".(отпав( ве1Г. с1авв . паве )) Этот метод используется, чтобы удалить первое вхождение заданного значения. Он использует метод ЗогтеОЕ1вт. Ь(вест 1егт(), чтобы отыскать позицию заданного значения, после чего проверяет, находится ли найденный индекс внутри списка и действительно ли в найденной позиции находится искомое значение.
Если условия выполняются, производится удаление элемента; в противном случае возбуждается исключение ча!иеЕггог (что полностью соответствует поведению метода 1твт, гезоче() в аналогичной ситуации). Сет геаоче ечегу(ве1Г, ча1ое): соопт = О тпоех = ве1(. (Пвест 1етт(ча1че) чш1е (1поех < 1еп(ве1Г. 1твт) апо ве!Г. 1твт[тпоех] == ча1ие): Се1 ве1(. 11вт[1поех] сочпт += 1 гетчгп сосет Этот метод похож на метод ЯагтеСЕ1вт, гееоче() и является расширением АР1 списка. Сначала он отыскивает в списке номер позиции первого вхождения заданного значения и затем в цикле, пока значение индекса находится в пределах списка и значение элемента в данной позиции совпадает с указанным значением, производит удаление элементов.
В этом программном коде имеется очень тонкий момент — так как в каждой итерации происходит удаление элемента списка, то после удаления элемента в позиции с данным индексом оказывается элемент, следовавший за удаленным. Сет соипт(ве1Г, ча1ое) .' сочпт = О (псех = ве1(. ь(вест 1етт(ча1ое) чп11е (1поех < 1еп(ве1(. 11вт) апо ве1(. 11вт[1псех] == ча1ое): 1поех в= т сосет е= т гетега сосет Этот метод возвращает число вхождений заданного значения в список (которое может быть равно О). Он использует очень похожий алго- Собственные классы коллекций 319 ритм, что и метод Зогтеб[!з!.
гевоче ечегу(), только здесь в каждой ите- рации необходимо увеличивать номер позиции. бег !псах(зе1г, ча1ие): 1пбех = зе!г. б!зес! 1егт(ча1ие) 1( 1пбех < !еп(зе11, 1!з!) апб зе1г. !!а![!псах] == ча1ие: ге!игл 1пбах гатза на1иееггрг("(0).!псах(х): х пот тп 1!зт".гпгват( зе11. с1азз , паве )) Так как объект типа Зог!еб[[з! представляет собой отсортированный список, мы можем использовать метод половинного деления, чтобы отыскать (или не отыскать) заданное значение в списке. бет бе11тев (зе!Г, 1пбех): бе1 зе1Г. 1!зт(!пбех] Специальный метод бе!!!за () обеспечивает поддержку синтаксиса бе1 1[л], где ! — это отсортированный список, а и — целое число, определяющее номер позиции в списке.
Мы не выполняем проверку на выход индекса за пределы списка„поскольку в этом случае вызов зе1(. !101 [тпбех] возбудит исключение 1пбехЕггог, что нам и требуется. бат рет!тев (зе11, 1пбех): ге!игл зе11. 1!зт[ 1пбах ] Этот метод обеспечивает поддержку синтаксиса х = 1[п], где 1 — это отсортированный список, а л — целое число, определяющее номер позиции в списке. бет за!!гав (зеы, 1пбех, ча1ие): гатзе турееггпг("иза абб() ти 1пзегт а ча1ие апб ге1у пп "тпе 1!з! то рит !т !и тпе г!рпт р1асе") Нам требуется предотвратить возможность изменения пользователем элемента списка в заданной позиции (то есть запретить возможность выполнения операции 1[и] = х), так как в этом случае может наругпиться порядок сортировки элементов в списке.
Как правило, чтобы показать, что операция не поддерживается тем или иным типом данных, используется исключение Туре Е г го г. бат Нег (за!О . гетигп 1таг(зе11. 11з!) Этот метод легко реализовать, потому что мы можем просто вернуть итератор частного списка, используя встроенную функцию 1!ег(). Этот метод используется для поддержки синтаксиса рог ча!ие !и ! Гегаб!е. Обратите внимание: когда объект интерпретируется как последовательность, то используется именно этот метод.
Так, чтобы преобразовать объект 1 типа Зог!еб[тз! в простой список, можно вызвать функцию 1!з!(1), в результате чего интерпретатор Руз]топ вызовет метод Зог!еб[тз!. 1!ег (1), чтобы получить последовательность, необходимую функции 11з!(). 320 Глава б, Объектно-ориентированное программирование Пет гечегвеп (ве11): гетчгп гечегвео(ве1Г, 11вт) Этот метод обеспечивает поддержку встроенной функции гечегвео(), благодаря чему мы можем записать, например„гог ча]ие тп гечегвег)(т гегаЫе). пег саптатпв (яе1(, ча1пе): тппех = ве1(. ьтвест 1егт(ча1ие) гетигп (!ппех < 1еп(ве1(. 1(вт) апд ве1(. 11вт(тппех] == ча1че) Метод соптатпв () обеспечивает поддержку оператора тп. И снова мы можем использовать быстрый алгоритм двоичного поиска вместо медленного алгоритма линейного поиска, используемого классом 1!в!.
Сет с1еаг(ве11). ве11. 11вт = [] Пет рор(ве1Г, тпеех=-1): гетчгп ве!г. 1твт.рпр(1псех) яет 1еп (ве11). гетпгп 1еп(яе11. 1твт) пег втг (ве1г): гетпгп втг(ве11. 11ят) Метод ЯогтеОЬ!вт.с1езг() отбрасывает существующий список и замещает его новым пустым списком. Метод Яогтег) Нят. рор() удаляет элемент из указанной позиции и возвращает его или возбуждает исключение 1пэехЕг гог, если индекс находится за пределами списка. В методах рор(), 1еп () и в!г () мы просто перекладываем работу на объект ве11.
11в!. Мы не предусматриваем переопределение специального метода герг (), поэтому, когда для объекта ! типа Яогтео(твт пользователь вызовет функцию герт(1), будет использоваться метод оЬ]ест. герг () базового класса. Он воспроизведет строку '<Зогтес11вт. ЯогтеОЕтвт оЬ)ест ат Ох97е7сес>', но, конечно, с другим значением числового идентификатора. Мы не можем предоставить иную реализацию метода герт (), потому что для этого пришлось бы представить в строке ключевую функцию, но у нас нет возможности создать репрезентативное представление ссылки на объект-функцию в виде строки, которую можно было бы передать функции еча1().