Лутц М. - Изучаем Python (1077325), страница 76
Текст из файла (страница 76)
Чтобы получить каждый второй символ из строки 8, можно извлечь срез с шагом 2: »> Гог х (п 3[::2]: рг1п1 х Изменение списков: гапяе Еще одно место, где можно использовать комбинацию функции гапбе и цикла бог, — это циклы, изменяющие список в процессе его обхода. Например, предположим, что по тем или иным причинам нам необходимо прибавить 1 к каждому элементу списка. Можно попытаться использовать для этой цели простейшую форму цикла Го г, но скорее всего это не то, что нам нужно: »> 1 = [1, 2, 3, 4, б] Приемы программирования циклов »> бог х 1п Ы х+=1 »> [ 1, 2, 3, 4, 5] >» х б Чтобы действительно изменить список во время его обхода, нам необходимо использовать операцию присваивания по индексу и изменить значения во всех позициях, по которым осуществляется цикл. Необходимые нам индексы можно воспроизвести с помощью комбинации функций гапбе/1епг »> 1 = [1, 2, 3, 4, 5] »> Гог 1 1п галсе(1еп(к)); 1[1] += 1 р ррибавига г к кехдрку эпекенту в списке ( р бг ([г] = ([г) к г »> [2, 3, 4, 5, б] При такой реализации список изменяется в процессе обхода.
Простой цикл (ог х 1п [: такого результата дать не может, потому что в таком цикле выполняется обход фактических элементов, а не позиций в списке. А возможно ли создать эквивалентный цикл ил11е? Для этого нам потребуется приложить немного больше усилий, и такой цикл наверняка будет работать медленнее; »> г = 0 »> ел11е 1 < 1еп(1): Ц1] += 1 1+=1 >» [3, 4, 5, б, 7] В данном случае решение на базе функции галсе может быть неидеальным. Генератор списка в виде [ хе1 (с г х г п [] также даст желаемый результат, но первоначальный список при этом не изменится (мы могли бы присвоить получившийся новый список обратно переменной [, но это выражение не изменит другие ссылки на первоначальный список).
Поскольку эта концепция циклов занимает такое важное положение, Такое решение вообще ничего не дает — здесь изменяется переменная цикла х, а не список [. Причину такого поведения трудно заметить. Всякий раз, когда цикл выполняет очередную итерацию, переменная х ссылается на очередное целое число, которое уже было извлечено из списка. В первой итерации, например, переменная х является целым числом 1. На следующей итерации в переменную х будет записана ссылка на другой объект — целое число 2, но это никак не повлияет на список, откуда было взято число 1.
Глава 13. Цихль~ хх)16е и (ог мы еще раз вернемся к ней, когда будем рассматривать генераторы спи- сков ниже в этой главе. Параллельный обход: л!р и псар Как было показано выше, встроенная функция гапре позволяет выполнять обход отдельных частей последовательностей. В том же духе встроенная функция 21р позволяет использовать цикл (сг для обхода нескольких последовательностей параллельно. Функция 21р принимает одну или несколько последовательностей в качестве аргументов и возвращает список кортежей, составленных из соответствующих элементов этих последовательностей.
Например, предположим, что мы выполняем обработку двух списков: »> [1 [1,2,3,41 »> [2 = [5,6,7,81 Для объединения элементов этих списков можно использовать функ- цию 21р, которая создаст список кортежей из пар элементов: »> Нр((1, [2) [(1, 5), (2, 6), (3, 7), (4, 8)1 Такой результат может пригодиться в самых разных ситуациях, но применительно к циклу Гог он обеспечивает возможность выполнения параллельных итераций: »> Гог (х, у) 1л 41р(х1, (2): рг(п1 х, у, ' — ', х+у 1 5 -- 6 26--8 37--10 48-12 Здесь выполняется обход результата обращения к функции 21р, т. е.
пар, составленных из элементов двух списков. Обратите внимание, что в этом цикле используется операция присваивания кортежей для получения элементов каждого кортежа, полученного от функции 21р. На первой итерации она будет выглядеть, как если бы была выполнена инструкция (х, у) = ((, 5). Благодаря этому мы можем сканировать оба списка [1 и [2 в одном цикле.
Тот же эффект можно получить с помощью цикла ив11е, в котором доступ к элементам производится вручную, но такой цикл будет сложнее в реализации и наверняка медленнее, чем прием, основанный на использовании Гсг/21р. Функция 21р на самом деле более универсальна, чем можно было бы представить на основе этого фрагмента. Например, она принимает последовательности любого типа (в действительности — любые итерируемые объекты, включая и файлы) и позволяет указывать более двух аргументов: 355 Приемы программирования циклов >» Т1, Т2, ТЗ = (1,2,3), (4,5,6), (7,8,9) »> ТЗ (7, 6, 9) >» хтр(Т1, Т2, ТЗ) [(1, 4, 7), (2, 5. 8), (3, 6, 9)! Длина списка, возвращаемого функцией 210, равна длине кратчайшей из последовательностей, если аргументы имеют разную длину: »> 81 = 'вЬо' »> 82 = 'ху2123' »> »> 210(81, 82) [('а', 'х'), ('Ь', у'), ('с'.
'г')] Родственная (и более старая) встроенная функция пар объединяет элементы последовательностей похожим образом, но она дополняет недостающие элементы значениями йопе, если аргументы имеют разную длину: »> вар(Иоле, 81, 82) [('а', 'х'), ('Ь', 'у'), ('с', 'г'), (йопе, '1'), (йояе, '2'), (Иоле,'3')] В этом примере фактически используется простейшая форма обращения к встроенной функции вар.
Обычно она принимает функцию и одну или более последовательностей и собирает результаты вызова функции с соответствующими элементами, извлеченными из последовательностей. Когда в первом аргументе передается значение йопе (как в данном случае), она просто объединяет элементы подобно функции 21р. Функция вар и прочие родственные ей инструменты будут рассматриваться в главе 17.
Конструирование словаря с помощью функции Нр В главе 8 я говорил, что функцию 2[р, используемую здесь, удобно применять для создания словарей, когда ключи и значения вычисляются во время выполнения программы. Теперь, когда мы поближе познакомились с этой функцией, я объясню, какое отношение она имеет к конструированию словарей. Как вы уже знаете, словарь всегда можно создать с помощью литерала словаря или присваивая значения ключам: »> О1 = ('ваав':1, 'е99в':3, '1оав(':5) »> 01 ( Чаав( . 5, 'е99е'. 3, 'враз 1) »> 01 = () »> 01['враз'] = 1 »> 01['еззв'] = 3 »> 01['1оавг'] = 5 Но как быть, если программа получает ключи и значения для словаря в виде списков во время выполнения, уже после того, как сценарий 366 Глава)3.
Циклы чгИе и (ог был иаписаи"г Например, предположим, что имеются следующие списки ключей и зиачеиий: »> Квуз = ['зрва', 'вдов', 'грвз!') »> чв1з = [1, 3, 5) Один из способов превратить их в словарь состоит в том, чтобы передать списки функции 210 и затем выполнить обход полученного результата в цикле гог: »> з10(авув, чв1в) [('эрза', 1), ('е99з', 3), ('Гоав(', 5)) >» 02 = () »> Гог (К, ч) 1о 21р(хауз, чв1з): 02[К) = ч »> 02 ( Човв!': 5, 'вддв' 3, 'врва': 1) Однако, начиная с версии Руд)гоп 2.2, можно обойтись без цикла рог и просто передать результат вызова функции 2! р встроенному конструктору с>с!: »> кзув = ['зрва', 'вддв', 'говвг') »> чв)в = [1, 3, 5) »> 03 = о1ог(з10(кзуз, чв1з)) »> 03 ('говвг': 5, 'вддв'.
3, 'врва'; 1) Встроенное имя О!с! в языке Ру[Ьоп в действительности является имеием типа (больше об именах типов и о создаиии подтипов вы узнаете в главе 26). Этот вызов производит преобразование списка в словарь, ио в действительиости это вызов конструктора объекта. Далее в этой главе мы рассмотрим родственное, ио более широкое понятие генераторов списков, которые позволяют создавать списки с помощью единствеиного выражения. Генерирование индексов и элементов: епогпегате Ранее мы рассматривали использование функции галде для генерации индексов (смещепий) элементов в строке вместо получения самих элементов с этими индексами. Однако в некоторых программах иеобходимо получить и то, и другое: и элемент, и его индекс. При традициоииом подходе можно было бы использовать простой цикл гог, в котором вести счетчик текущего индекса: »> 8 = 'эрза' »> оугвзг = 0 »> рог 1!за 1о 8: рг1лг 1!за, 'вррввгв зг оугвзг', оууввв оггзвг += ! в вррввгв в( о(Гав( О 557 Генераторы списков: первое знакомство р арреагв ат оттает 1 а арреагв ат отсвет 2 а арреагв ат оттает 3 Этот способ вполне работоспособен, ио в последних версиях яаыка Ру(йоп те же самые действия можно выполнить с помощью встроенной функции с именем епсаегате1 »> 8 = 'враз' »> Гог (оттает, 1теа) 1п епеаегате(8): рг1пт 1теа, 'арреагз ат оттает', от(зев в арреагв ат оттает 0 р арреагв ат а(гает 1 а арреагв ат оггвет 2 а арреагв ат о[гает 3 Функция епиаегате возвращает объект-генератор — разновидность объекта, который поддерживает итерационный протокол, с которым мы встретились выше в этой главе и более подробно будем осуждать в следующей части книги.