Саммерфилд - Программирование на Python 3 (1077331), страница 30
Текст из файла (страница 30)
139 Последовательности Удаление элементов с помощью инструкции де! Несмотря на то, что название инструкции бе1 вызывает ассоциации со словом с(е1ете (удалив!а), она не обязательно удаляет какие-либо данные. Когда инструкция бе1 применяется к элементу данных, который не является коллекцией, она разрывает связь между ссылкой на объект и самим элементом данных и удаляет ссылку на объект. Например: »> х = 8143 а создается ссылка на объект 'х' и целое число 8143 »> х 8143 »> Ое1 х а удаляется ссылка на обьект 'х', число готово к утилизации >» х Тгасебасх (аозт гесепт са!1 1аат): ЫааеЕггог: паве 'х' !з пот оет!пес (ИааеЕггог имя 'х' не определено) Когда удаляется ссылка на объект, если не осталось других ссылок, указывающих на этот объект, то интерпретатор помечает элемент данных, на который указывала ссылка, как готовый к утилизации. Невозможно предсказать, когда произойдет утилизация и произойдет ли она вообще (это зависит от реализации РуФ)топ), поэтому, когда необходимо явно освободить память, делать это придется вручную.
Язык РуФ)топ предоставляет два решения проблемы неопределенности. Одно из них состоит в использовании конструкции тгу ... т!ла11у, которая гарантирует освобождение памяти, а другое заключается в использовании инструкции и!тп, с которой мы познакомимся в главе 8. Когда инструкция бе1 применяется к коллекциям, таким как кортежи или списки, удаляется только ссылка на эти коллекции. Коллекция и ее элементы (а также элементы, которые сами являются коллекциями для своих элементов, рекурсивно) помечаются как готовые к утилизации, если не осталось других ссылок, указывающих на эти коллекции. Для изменяемых коллекций, таких как списки, инструкция бе1 может применяться к отдельным элементам или срезам — в любом из этих случаев используется оператор извлечения среза []. Если для удаления предназначен элемент или элементы коллекции и в программе не осталось ссылок, указывающих на эти элементы, они помечаются, как готовые к утилизации.
140 Глаза 3. Типы коллекций Отдельные элементы можно добавлять в конец списка с помощью метода 1!в!.аррепб(). Элементы могут вставляться в любую позицию в списке с помощью метода 1[в!,1пвег!() или посредством обращения к срезу с нулевой длиной.
Например, допустим, что имеется список хообв = [ "Себаг", ттех", "Е!г", "Вргосе" ]; тогда вставить новый элемент в позицию с индексом 2 (то есть сделать этот элемент третьим элементом списка) можно одним из двух способов: хообв[2:2] = ["Р1пе"] хоабв.гпвог![2, "Репе") В обоих случаях в результате будет получен список ['Себаг', 'Тех', 'Р~пе', 'Е!г', 'Вргосе']. Отдельные элементы списка можно изменять, выполняя присваивание определенной позиции в списке, например, хообв = 'Вебхооб'.
Путем присваивания итерируемых объектов можно изменять целые срезы всписке, например,хообв[1:3] = [ "Зргосе", "Вор~", "Вззо" ].Срезиитерируемый объект не обязательно должны иметь одинаковую длину. В любом случае элементы, попавшие в срез, будут удалены, а на их место будут вставлены элементы итерируемого объекта. Если длина итерируемого объекта короче замещаемого среза, список уменьшится, а если длина итерируемого объекта больше замещаемого среза, то список увеличится. Чтобы прояснить, что именно происходит в результате присваивания итерируемого объекта срезу списка, рассмотрим еще один пример. Представим, что имеется список С = [ "А", "В", "С", "В", "Е", "Е" ] и что выполняется присваивание итерируемого объекта (в данном случае — списка) срезу: 1[2:5] = [ "Х", "У" ].
Впервуюочередьпроизводится удаление элементов среза, то есть за кулисами список принимает вид ['А', ' В', 'Е']. А затем все элементы итерируемого объекта вставляются в позицию первого элемента среза, и в результате получается список ['А', 'В', 'Х', 'У', 'Е']. Существует еще ряд других способов удаления элементов списка. Чтобы удалить самый правый элемент списка, можно воспользоваться методом 1[в!. рор() без аргументов — удаленный элемент возвращается в качестве результата. Аналогично можно использовать метод 1!вг, рор( ) с целочисленным значением индекса — для удаления (и возвращения) элемента с определенным индексом.
Еще один способ удаления элемента заключается в использовании метода 1[в!. гевоее[), которому передается удаляемый элемент. Также для удаления отдельных элементов или целых срезов можно использовать инструкцию бе1 — например, бе1 хообв[4]. Кроме того, срезы могут удаляться путем присваивания пустого списка, так следующие два фрагмента являются эквивалентными: хообв[2:4] = [] бе1 хообв[2:4] В левом фрагменте выполняется присваивание итернруемого объекта (пустого списка) срезу, то есть здесь сначала удаляются элементы сре- Последовательности 141 за, а так как итерируемый объект, предназначенный для вставки, пуст, вставка не производится.
Когда мы впервые обсуждали операцию извлечения разИзвлечение реженного среза, мы делали это в контексте строк, где срееоене извлечение разреженных срезов выглядит не очень ин- строк,стр.89 тересно. Но в случае списков операция извлечения раз- реженного среза позволяет получить доступ к каждому и-му элементу, что может оказаться очень удобно. На- пример, предположим, что имеется список х = [1, 2, 3, 4, 5, 5, ?, 8, 9, 10] и необходимо все элементы с нечет- ными индексами (то есть х[1], х[3] и т.
д.) установить в значение О. Получить доступ к каждому второму эле- менту можно, используя оператор среза с шагом, напри- мер, х[:: 2]. Но такой оператор даст доступ к элементам с индексами О, 2, 4 и т. д. Мы можем исправить положе- ние, указав начальный индекс, использовав выражение х[1:;2], которое возвращает срез, содержащий нужные нам элементы. Чтобы установить все элементы среза в значение О, нам необходим список нулей, и этот список должен содержать то же самое число нулей, сколько эле- ментов содержится в срезе. Полное решение задачи выглядит так: х[1:: 2] = [О] * 1ел(х[1::2]). Те- перь список х имеет следующий вид [1, О, 3, О, 5, О, 7, О, 9, 0].
Мы вос- пользовались оператором дублирования * для создания списка, содер- жащего необходимое число нулей, основываясь на длине (то есть коли- честве элементов) среза. Особенно интересно, что при присваивании списка [О, О, О, О, 0] разреженному срезу, интерпретатор корректно за- мещает первым нулем значение х [1], вторым нулем значение х [3] и т. д. Списки могут упорядочиваться в обратном порядке и сорФункция тироваться, так же как и другие итерируемые объекты„еогсее() с помощью встроенных функций геоегвес() и всгтес(), стр.164,170 описываемых в подразделе «Итераторы, функции и опе- раторы для работы с итерируемыми объектамие (стр.
163). Списки также имеют эквивалентные методы 11вт. геоегве() и 1[зг. вогс(), которые работают непосред- ственно с самим списком (поэтому они ничего не возвра- щают); последний из них принимает те же необязатель- ные аргументы, что и функция вогтеэ(). Для сортировки списков строк без учета регистра символов используется распространенный прием — например, список исссв мож- но было бы отсортировать так: ивова. всгт(хеу=втг.
1оиег). Аргумент кеу используется, чтобы определить функцию, которая будет применяться к каждому элементу, и воз- вращать значение, участвующее в сравнении в процессе сортировки. Как уже отмечалось в предыдущей главе, 142 Глава 3. Типы коллекций в разделе, где рассматривались вопросы сравнения строк (стр. 63), для языков, отличных от английского, сортировка строк в подразумеваемом людьми порядке может оказаться непростым делом. Что касается вставки элементов, списки обеспечивают лучшую производительность при добавлении или удалении элементов в конце списка (1!а!.аррепс(), 11а!.рор()). Падение производительности происходит, когда приходится отыскивать элементы в списке, например, с помощью методов 1!а!, гекоуе() или 1!а!.1псех(), а также при использовании оператора 1п проверки на вхождение.
Когда необходимо обеспечить высокую скорость поиска или проверки на вхождение, возможно, более удачным выбором будут множества и словари (оба типа коллекций описываются ниже, в этой же главе). Однако и для списков можно обеспечить высокую скорость поиска, если хранить их в отсортированном виде — в языке Ру!)коп алгоритм сортировки особенно хорошо оптимизирован для случая сортировки частично отсортированных списков — и отыскивать элементы методом дихотомии (реализован в модуле 0!аес!). (В главе б мы создадим свой класс списков, которые хранятся в отсортированном виде.) Генераторы списков Небольшие списки часто создаются как литералы, но длинные списки обычно создаются программным способом.
Списки целых чисел могут создаваться с помощью выражения 1!вг(галде(п)); когда необходим итератор целых чисел, достаточно функции галде(); а для создания списков других типов часто используется оператор цикла Гог ... 1п. Предположим, например, что нам требуется получить список високосных годов в определенном диапазоне. Для начала мы могли бы использовать такой цикл: 1еара = [) Гог уеаг кп гапде(1900, 1940): 1г (уеаг % 4 == 0 апп уааг % 100 != О) ог (уааг % 400 == О): 1еара.аррепп(уааг) а Когда функции галде() передаются два целочисленных Функция „апра() аргумента л и т, итератор возвращает последовательсгр.187 ность целых чисел и, и+ 1, ..., т — 1.
Конечно, если диапазон известен заранее, можно было бы использовать литерал списка, например, 1вара = [1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936]. Генератор сииснов — это выражение и цикл с дополнительным условием, заключенное в квадратные скобки, в котором цикл используется для создания элементов списка, а условие используется для исключения нежелательных элементов. В простейшем виде генератор списков записывается, как показано ниже: [4Геи Гог 41еш 1п атегап1е) Последовательности Это выражение вернет список всех элементов объекта 1(егаЫе и семантически ничем не отличается от выражения 1!ат(1(егаО)е). Интересными генераторы списков делают две особенности — они могут использоваться как выражения и они допускают включение условной инструкции, вследствие чего мы получаем две типичные синтаксические конструкции использования генераторов списков: [ехргеаатрп грг тгеа ш тгегаые] [ехргеаюоп гог !геа !и тгегаые !т солю!топ] Вторая форма записи эквивалентна циклу: тепр = [ 1 Гог ттеа тп ттегаЫе.
тт српш стоп: тавр.аррепд(ехргеаю рп) Обычно выражение екргеаыол является либо самим элементом 1(ез, либо некоторым выражением с его участием. Конечно, генератору списков не требуется временная переменная тезр[], которая необходима в версии с циклом гог ... [п. Теперь можно переписать программный код создания списка високосных годов с использованием генератора списка. Мы сделаем это в три этапа. Сначала создадим список, содержащий все годы в указанном диапазоне: 1аара = [у гог у !и гапце(1900, 1940)) То же самое можно было бы сделать с помощью выражения 1еарз 1!ат( гарце(1900, 1940) ).
Теперь добавим простое условие, которое будет оставлять в списке только каждый четвертый год: 1еара = [у тог у !п гапца(1900, 1940) тт у % 4 == О] И, наконец, получаем окончательную версию: 1еара = [у грг у тп гарца(1900, 1940) !Г (у % 4 == 0 апа у % 100 != 0) ог (у % 400 == 0)] Использование генератора списков в данном случае позволило уменьшить объем программного кода с четырех строк до двух — не так много, но в крупных проектах суммарная зкономия может оказаться весьма существенной. Так как генераторы списков воспроизводят списки, то есть итерируемые объекты, и сами генераторы списков используют итерируемые объекты, имеется возможность вкладывать генераторы списков друг в друга.