Изучаем Python — страница 8 из 61

4. Работа со списками


В главе 3 вы научились создавать простые списки и работать с отдельными элементами списков. В этой главе вы узнаете, как перебрать весь список всего в нескольких строках кода (независимо от длины списка). Механизм перебора позволяет выполнить одно действие или набор действий с каждым элементом в списке. С его помощью вы сможете эффективно работать со списками любой длины, даже состоящими из тысяч и миллионов элементов.

Перебор всего списка


Типичная задача из области программирования — перебрать все элементы списка и выполнить с каждым элементом одну и ту же операцию. Например, в компьютерной игре все экранные объекты могут смещаться на одинаковую величину, или в списке чисел к каждому элементу может применяться одна и та же статистическая операция. А может быть, вам нужно вывести все заголовки из списка статей на ­сайте. В ситуациях, требующих применения одного действия к каждому элементу списка, можно воспользоваться циклами for.

Допустим, имеется список с именами фокусников, и вы хотите вывести каждое имя из списка. Конечно, можно обратиться к каждому элементу по отдельности, но такой подход создает ряд проблем. Во-первых, для очень длинных списков все сведется к однообразным повторениям. Во-вторых, при любом изменении длины списка в программу придется вносить изменения. Цикл for решает обе проблемы: Python будет следить за всеми техническими деталями в своей внутренней реализации.

В следующем примере цикл for используется для вывода имен фокусников:

magicians.py

(1) magicians = ['alice', 'david', 'carolina']

(2)for magician in magicians:

(3)print(magician)

Все начинается с определения списка (1) , как и в главе 3. В точке (2) определяется цикл for. Эта строка приказывает Python взять очередное имя из списка и сохранить его в переменной magician. В точке (3) выводится имя, только что сохраненное в переменной magician. Затем строки (1) и (2) повторяются для каждого имени в списке. Этот код можно описать так: «Для каждого фокусника в списке вывести его имя». Результат представляет собой простой перечень имен из списка:

alice

david

carolina

Подробнее о циклах


Концепция циклов очень важна, потому что она представляет один из основных способов автоматизации повторяющихся задач компьютером. Например, в простом цикле, использованном в magicians.py, Python сначала читает первую строку цикла:

for magician in magicians:

Эта строка означает, что нужно взять первое значение из списка magicians и сохранить его в переменной magician. Первое значение в списке — 'alice'. Затем Python читает следующую строку:

. .print(magician)

Python выводит текущее значение magician, которое все еще равно 'alice'. Так как в списке еще остались другие значения, Python возвращается к первой строке цикла:

for magician in magicians:

Python берет следующее значение из списка — 'david' — и сохраняет его в magician. Затем выполняется строка:

. .print(magician)

Python снова выводит текущее значение magician; теперь это строка 'david'. Весь цикл повторяется еще раз с последним значением в списке, 'carolina'. Так как других значений в списке не осталось, Python переходит к следующей строке в программе. В данном случае после цикла for ничего нет, поэтому программа просто завершается.

Помните, что все действия повторяются по одному разу для каждого элемента в списке независимо от их количества. Если список содержит миллион элементов, Python повторит эти действия миллион раз — обычно это происходит очень быстро.

Также помните, что при написании собственных циклов for временной переменной для текущего значения из списка можно присвоить любое имя. Однако на практике рекомендуется выбирать осмысленное имя, описывающее отдельный элемент списка. Несколько примеров:

for cat in cats:

for dog in dogs:

for item in list_of_items:

Выполнение этого правила поможет вам проследить за тем, какие действия выполняются с каждым элементом в цикле for. В зависимости от того, какое число используется — одиночное и множественное, вы сможете понять, работает ли данная часть кода с отдельным элементом из списка или со всем списком.

Более сложные действия в циклах for


В цикле for с каждым элементом списка может выполняться практически любое действие. Дополним предыдущий пример, чтобы программа выводила для каждого фокусника отдельное сообщение:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

(1) . .print(magician.title() + ", that was a great trick!")

Единственное отличие этого кода от предыдущего заключается в том, что в точке (1) для каждого фокусника строится сообщение с его именем. При первом проходе цикла переменная magician содержит значение 'alice', поэтому Python начинает первое сообщение с имени 'Alice'. При втором проходе сообщение будет начинаться с имени 'David', а при третьем — с имени 'Carolina':

Alice, that was a great trick!

David, that was a great trick!

Carolina, that was a great trick!

Тело цикла for может содержать сколько угодно строк кода. Каждая строка с начальным отступом после строки for magician in magicians считается находящейся в цикле и выполняется по одному разу для каждого значения в списке. Таким образом, с каждым значением в списке можно выполнить любые операции на ваше усмотрение.

Включим в сообщение для каждого фокусника вторую строку:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

. .print(magician.title() + ", that was a great trick!")

(1) . .print("I can't wait to see your next trick, " + magician. title() + ".\n")

Так как обе команды print снабжены отступами, каждая строка будет выполнена по одному разу для каждого фокусника в списке. Символ новой строки ("\n") во второй команде print (1) вставляет пустую строку после каждого прохода цикла. В результате будет создан набор сообщений, аккуратно сгруппированных для каждого фокусника в списке:

Alice, that was a great trick!

I can't wait to see your next trick, Alice.


David, that was a great trick!

I can't wait to see your next trick, David.


Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

Тело цикла for может содержать сколько угодно строк кода. На практике часто требуется выполнить в цикле for несколько разных операций для каждого элемента списка.

Выполнение действий после цикла for


Что происходит после завершения цикла for? Обычно программа выводит сводную информацию или переходит к другим операциям.

Каждая строка кода после цикла for, не имеющая отступа, выполняется без повторения. Допустим, вы хотите вывести сообщение для всей группы фокусников и поблагодарить их за превосходное представление. Чтобы вывести общее сообщение после всех отдельных сообщений, разместите его после цикла for без отступа:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

. .print(magician.title() + ", that was a great trick!")

. .print("I can't wait to see your next trick, " + magician.title() + ".\n")

. .

(1) print("Thank you, everyone. That was a great magic show!")

Первые две команды print повторяются по одному разу для каждого фокусника в списке, как было показано ранее. Но поскольку строка (1) отступа не имеет, это сообщение выводится только один раз:

Alice, that was a great trick!

I can't wait to see your next trick, Alice.


David, that was a great trick!

I can't wait to see your next trick, David.


Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.


Thank you, everyone. That was a great magic show!

При обработке данных в циклах for завершающее сообщение позволяет подвести итог операции, выполненной со всем набором данных. Например, цикл for может инициализировать игру, перебирая список персонажей и изображая каждого персонажа на экране. После цикла выполняется блок без отступа, который выводит кнопку Начать игру после того, как все персонажи появятся на экране.

Предотвращение ошибок с отступами


В Python связь одной строки кода с предшествующей строкой обозначается отступами. В приведенных примерах строки, выводившие сообщения для отдельных фокусников, были частью цикла, потому что они были снабжены отступами. Применение отступов в Python сильно упрощает чтение кода. Фактически отступы заставляют разработчика писать аккуратно отформатированный код с четкой визуальной структурой. В более длинных программах Python могут встречаться блоки кода с отступами нескольких разных уровней. Эти уровни способствуют пониманию общей структуры программы.

Когда разработчики только начинают писать код, работа которого зависит от ­правильности отступов, в их коде нередко встречаются распространенные ошибки. Например, иногда они расставляют отступы в коде, в котором эти отступы не нужны, или наоборот — забывают ставить отступы в блоках, где это необходимо. Несколько примеров помогут вам избежать подобных ошибок в будущем и успешно исправлять их, когда они встретятся в ваших программах.

Итак, рассмотрим несколько типичных ошибок при использовании отступов.

Пропущенный отступ


Строка после команды for в цикле всегда должна снабжаться отступом. Если вы забудете поставить отступ, Python напомнит вам об этом:

magicians.py

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

(1) print(magician)

Команда print в точке (1) должна иметь отступ, но здесь его нет. Когда Python ожидает увидеть блок с отступом, но не находит его, появляется сообщение с указанием номера строки:

. File "magicians.py", line 3

. .print(magician)

. . . .^

IndentationError: expected an indented block

Обычно для устранения подобных ошибок достаточно поставить отступ в строке (или строках), следующей непосредственно после команды for.

Пропущенные отступы в других строках


Иногда цикл выполняется без ошибок, но не выдает ожидаемых результатов. Такое часто происходит, когда вы пытаетесь выполнить несколько операций в цикле, но забываете снабдить отступом некоторые из строк.

Например, вот что происходит, если вы забудете снабдить отступом вторую строку в цикле:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

. .print(magician.title() + ", that was a great trick!")

(1) print("I can't wait to see your next trick, " + magician.title() + ".\n")

Команда print в точке (1) должна быть снабжена отступом, но, поскольку Python находит хотя бы одну строку с отступом после команды for, сообщение об ошибке не выдается. В результате первая команда print будет выполнена для каждого элемента в списке, потому что в ней есть отступ. Вторая команда print отступа не имеет, поэтому она будет выполнена только один раз после завершения цикла. Так как последним значением magician является строка 'carolina', второе сообщение будет выведено только с этим именем:

Alice, that was a great trick!

David, that was a great trick!

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

Это пример логической ошибки. Код имеет действительный синтаксис, но он не приводит к желаемому результату, потому что проблема кроется в его логике. Если некоторое действие должно повторяться для каждого элемента в списке, но выполняется только один раз, проверьте, не нужно ли добавить отступы в строке или нескольких строках кода.

Лишние отступы


Если вы случайно поставите отступ в строке, в которой он не нужен, Python сообщит об этом:

hello_world.py

message = "Hello Python world!"

(1) . . print(message)

Отступ команды print в точке (1) не нужен, потому что эта строка не подчинена предшествующей; Python сообщает об ошибке:

. File "hello_world.py", line 2

. .print(message)

. .^

IndentationError: unexpected indent

Чтобы избежать непредвиденных ошибок с отступами, используйте их только там, где для этого существуют конкретные причины. В тех программах, которые вы ­пишете на этой стадии изучения Python, отступы нужны только в строках действий, повторяемых для каждого элемента в цикле for.

Лишние отступы после цикла


Если вы случайно снабдите отступом код, который должен выполняться после завершения цикла, то этот код будет выполнен для каждого элемента. Иногда Python выводит сообщение об ошибке, но часто дело ограничивается простой логической ошибкой.

Например, что произойдет, если случайно снабдить отступом строку с выводом завершающего приветствия для группы фокусников?

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

. .print(magician.title() + ", that was a great trick!")

. .print("I can't wait to see your next trick, " + magician.title() + ".\n")

. .

(1) . .print("Thank you everyone, that was a great magic show!")

Так как строка (1) имеет отступ, сообщение будет продублировано для каждого фокусника в списке (2):

Alice, that was a great trick!

I can't wait to see your next trick, Alice.


(2)Thank you everyone, that was a great magic show!

David, that was a great trick!

I can't wait to see your next trick, David.


(2)Thank you everyone, that was a great magic show!

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.


(2)Thank you everyone, that was a great magic show!

Это еще один пример логической ошибки, наподобие описанной в разделе «Пропущенные отступы в других строках» на с. 66. Python не знает, что вы пытаетесь сделать в своем коде, поэтому он просто выполняет весь код, не нарушающий правил синтаксиса. Если действие, которое должно выполняться один раз, выполняется многократно, проверьте, нет ли лишнего отступа в соответствующей строке кода.

Пропущенное двоеточие


Двоеточие в конце команды for сообщает Python, что следующая строка является началом цикла.

magicians = ['alice', 'david', 'carolina']

(1) for magician in magicians

. .print(magician)

Если вы случайно забудете поставить двоеточие, как в примере (1) , произойдет синтаксическая ошибка, так как полученная команда нарушает правила языка. И хотя такие ошибки легко исправляются, найти их бывает достаточно трудно. Вы не поверите, сколько времени тратят программисты на поиск подобных «односимвольных» ошибок. Поиск таких ошибок усложняется еще и тем, что человек склонен видеть то, что он ожидает увидеть.

Упражнения

4-1. Пицца: вспомните по крайней мере три ваши любимые разновидности пиццы. Сохраните их в списке и используйте цикл for для вывода всех названий.

• Измените цикл for так, чтобы вместо простого названия пиццы выводилось сообщение, включающее это название. Таким образом, для каждого элемента должна выводиться строка с простым текстом вида «I like pepperoni pizza».

• Добавьте в конец программы (после цикла for) строку с завершающим сообщением. Таким образом, вывод должен состоять из трех (и более) строк с названиями пиццы и дополнительного сообщения, скажем, «I really love pizza!».

4-2. Животные: создайте список из трех (и более) животных, обладающих общей характеристикой. Используйте цикл for для вывода названий всех животных.

• Измените программу так, чтобы вместо простого названия выводилось сообщение, включающее это название, например «A dog would make a great pet».

• Добавьте в конец программы строку с описанием общего свойства. Например, можно вывести сообщение «Any of these animals would make a great pet!».

Создание числовых списков