Мэтиз. Изучаем Python. Crash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g
Скачать 6.2 Mb.
|
74 Глава 4 • Работа со списками Простая статистика с числовыми списками Некоторые функции Python предназначены для работы с числовыми списками. Например, вы можете легко узнать минимум, максимум и сумму числового списка: >>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] >>> min(digits) 0 >>> max(digits) 9 >>> sum(digits) 45 ПРИМЕЧАНИЕ В примерах этой главы используются короткие списки чисел, но это делается только для того, чтобы данные помещались на странице . Примеры также бу- дут работать и в том случае, если список содержит миллионы чисел . Генераторы списков Описанный выше способ генерирования списка squares состоял из трех или четырех строк кода. Генератор списка (list comprehension) позволяет сгенерировать тот же список всего в одной строке. Генератор списка объединяет цикл for и создание новых элементов в одну строку и автоматически присоединяет к списку все новые элемен- ты. Учебники не всегда рассказывают о генераторах списка начинающим программи- стам, но я привожу этот материал, потому что вы с большой вероятностью встретите эту конструкцию, как только начнете просматривать код других разработчиков. В следующем примере список квадратов, знакомый вам по предыдущим примерам, строится с использованием генератора списка: squares.py squares = [value**2 for value in range(1,11)] print(squares) Чтобы использовать этот синтаксис, начните с содержательного имени списка, например squares . Затем откройте квадратные скобки и определите выражение для значений, которые должны быть сохранены в новом списке. В данном приме- ре это выражение value**2 , которое возводит значение во вторую степень. Затем напишите цикл for для генерирования чисел, которые должны передаваться вы- ражению, и закройте квадратные скобки. Цикл for в данном примере — for value in range(1,11) — передает значения с 1 до 10 выражению value**2 . Обратите вни- мание на отсутствие двоеточия в конце команды for Результатом будет уже знакомый вам список квадратов: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] Чтобы успешно писать собственные генераторы списков, необходим опреде- ленный опыт. Тем не менее, как только вы освоитесь с созданием обычных Работа с частью списка 75 списков, вы оцените возможности генераторов. Когда после очередного трех- четырехстрочного блока вам это надоест, подумайте о написании собственных генераторов списков. УПРАЖНЕНИЯ 4.3. Считаем до 20: используйте цикл for для вывода чисел от 1 до 20 включительно. 4.4. Миллион: создайте список чисел от 1 до 1 000 000, затем воспользуйтесь циклом for для вывода чисел. (Если вывод занимает слишком много времени, остановите его нажатием Ctrl+C или закройте окно вывода.) 4.5. Суммирование миллиона чисел: создайте список чисел от 1 до 1 000 000, затем вос- пользуйтесь функциями min() и max() и убедитесь в том, что список действительно начи- нается с 1 и заканчивается 1 000 000. Вызовите функцию sum() и посмотрите, насколько быстро Python сможет просуммировать миллион чисел. 4.6. Нечетные числа: воспользуйтесь третьим аргументом функции range() для создания списка нечетных чисел от 1 до 20. Выведите все числа в цикле for. 4.7. Тройки: создайте список чисел, кратных 3, в диапазоне от 3 до 30. Выведите все числа своего списка в цикле for 4.8. Кубы: результат возведения числа в третью степень называется кубом. Например, куб 2 записывается в языке Python в виде 2**3 . Создайте список первых 10 кубов (то есть кубов всех целых чисел от 1 до 10) и выведите значения всех кубов в цикле for. 4.9. Генератор кубов: используйте конструкцию генератора списка для создания списка первых 10 кубов. Работа с частью списка В главе 3 вы узнали, как обращаться к отдельным элементам списка, а в этой главе мы занимались перебором всех элементов списка. Также можно работать с конкрет- ным подмножеством элементов списка; в Python такие подмножества называются сегментами (slices). Создание сегмента Чтобы создать сегмент на основе списка, следует задать индексы первого и послед- него элементов, с которыми вы намереваетесь работать. Как и в случае с функцией range() , Python останавливается на элементе, предшествующем второму индексу. Скажем, чтобы вывести первые три элемента списка, запросите индексы с 0 по 3, и вы получите элементы 0, 1 и 2. В следующем примере используется список игроков команды: players.py players = ['charles', 'martina', 'michael', 'florence', 'eli'] ❶ print(players[0:3]) 76 Глава 4 • Работа со списками В точке выводится сегмент, включающий только первых трех игроков. Вывод сохраняет структуру списка, но включает только первых трех игроков: ['charles', 'martina', 'michael'] Подмножество может включать любую часть списка. Например, чтобы ограничить- ся вторым, третьим и четвертым элементами списка, создайте сегмент, который начинается с индекса 1 и заканчивается на индексе 4: players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[1:4]) На этот раз сегмент начинается с элемента 'martina' и заканчивается элементом 'florence' : ['martina', 'michael', 'florence'] Если первый индекс сегмента не указан, то Python автоматически начинает сегмент от начала списка: players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[:4]) Без начального индекса Python берет элементы от начала списка: ['charles', 'martina', 'michael', 'florence'] Аналогичный синтаксис работает и для сегментов, включающих конец списка. На- пример, если вам нужны все элементы с третьего до последнего, начните с индекса 2 и не указывайте второй индекс: players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[2:]) Python возвращает все элементы с третьего до конца списка: ['michael', 'florence', 'eli'] Этот синтаксис позволяет вывести все элементы от любой позиции до конца спи- ска независимо от его длины. Вспомните, что отрицательный индекс возвращает элемент, находящийся на заданном расстоянии от конца списка; следовательно, вы можете получить любой сегмент от конца списка. Например, чтобы отобрать последних трех игроков из списка, используйте сегмент players[-3:] : players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[-3:]) Программа выводит имена трех последних игроков, причем продолжает работать даже при изменении размера списка. Работа с частью списка 77 ПРИМЕЧАНИЕ В квадратные скобки, определяющие сегмент, также можно включить третье значение . Это значение, если оно присутствует, сообщает Python, сколько эле- ментов следует пропускать при выборе элементов в заданном диапазоне . Перебор содержимого сегмента Если вы хотите перебрать элементы, входящие в подмножество элементов, исполь- зуйте сегмент в цикле for . В следующем примере программа перебирает первых трех игроков и выводит их имена: players = ['charles', 'martina', 'michael', 'florence', 'eli'] print("Here are the first three players on my team:") ❶ for player in players[:3]: print(player.title()) Вместо того чтобы перебирать весь список игроков , Python ограничивается первыми тремя именами: Here are the first three players on my team: Charles Martina Michael Сегменты приносят огромную пользу во многих ситуациях. Например, при соз- дании компьютерной игры итоговый счет игрока может добавляться в список после окончания текущей партии. После этого программа может получить три лучших результата игрока, отсортировав список по уменьшению и получив сег- мент, включающий только три элемента. При работе с данными сегменты могут использоваться для обработки данных блоками заданного размера. Или при по- строении веб-приложения сегменты могут использоваться для постраничного вывода информации так, чтобы на каждой странице выводился соответствующий объем информации. Копирование списка Часто разработчик берет существующий список и создает на его основе совершенно новый список. Посмотрим, как работает копирование списков, и рассмотрим одну ситуацию, в которой копирование списка может принести пользу. Чтобы скопировать список, создайте сегмент, включающий весь исходный список без указания первого и второго индекса ( [:] ). Эта конструкция создает сегмент, который начинается с первого элемента и завершается последним; в результате создается копия всего списка. Представьте, что вы создали список своих любимых блюд и теперь хотите создать отдельный список блюд, которые нравятся вашему другу. Пока вашему другу 78 Глава 4 • Работа со списками нравятся все блюда из нашего списка, поэтому вы можете создать другой список простым копированием нашего: foods.py ❶ my_foods = ['pizza', 'falafel', 'carrot cake'] ❷ friend_foods = my_foods[:] print("My favorite foods are:") print(my_foods) print("\nMy friend's favorite foods are:") print(friend_foods) В точке создается список блюд с именем my_foods . В точке создается другой список с именем friend_foods . Чтобы создать копию my_foods , программа запра- шивает сегмент my_foods без указания индексов и сохраняет копию в friend_foods При выводе обоих списков становится видно, что оба списка содержат одинаковые данные: My favorite foods are: ['pizza', 'falafel', 'carrot cake'] My friend's favorite foods are: ['pizza', 'falafel', 'carrot cake'] Чтобы доказать, что речь в действительности идет о двух разных списках, добавим новое блюдо в каждый список: my_foods = ['pizza', 'falafel', 'carrot cake'] ❶ friend_foods = my_foods[:] ❷ my_foods.append('cannoli') ❸ friend_foods.append('ice cream') print("My favorite foods are:") print(my_foods) print("\nMy friend's favorite foods are:") print(friend_foods) В точке исходные элементы my_foods копируются в новый список friend_foods , как было сделано в предыдущем примере. Затем в каждый список добавля- ется новый элемент: 'cannoli' в my_foods , а в точке 'ice cream' добавляется в friend_foods . После этого вывод двух списков наглядно показывает, что каждое блюдо находится в соответствующем списке. My favorite foods are: ❹ ['pizza', 'falafel', 'carrot cake', 'cannoli'] My friend's favorite foods are: ❺ ['pizza', 'falafel', 'carrot cake', 'ice cream'] Работа с частью списка 79 Вывод в точке показывает, что элемент 'cannoli' находится в списке my_foods , а элемент 'ice cream' в этот список не входит. В точке видно, что 'ice cream' вхо- дит в список friend_foods , а элемент 'cannoli' в этот список не входит. Если бы эти два списка просто совпадали, то их содержимое уже не различалось бы. Например, вот что происходит при попытке копирования списка без использования сегмента: my_foods = ['pizza', 'falafel', 'carrot cake'] # Не работает: ❶ friend_foods = my_foods my_foods.append('cannoli') friend_foods.append('ice cream') print("My favorite foods are:") print(my_foods) print("\nMy friend's favorite foods are:") print(friend_foods) Вместо того чтобы сохранять копию my_foods в friend_foods в точке , мы зада- ем friend_foods равным my_foods . Этот синтаксис в действительности сообщает Python, что новая переменная friend_foods должна быть связана со списком, уже хранящимся в my_foods , поэтому теперь обе переменные связаны с одним списком. В результате при добавлении элемента 'cannoli' в my_foods этот элемент также появляется в friend_foods . Аналогичным образом элемент 'ice cream' появляется в обоих списках, хотя на первый взгляд он был добавлен только в friend_foods Вывод показывает, что оба списка содержат одинаковые элементы, а это совсем не то, что требовалось: My favorite foods are: ['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream'] My friend's favorite foods are: ['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream'] ПРИМЕЧАНИЕ Если какие-то подробности в этом примере кажутся непонятными, не огорчайтесь . В двух словах, если при работе с копией списка происходит что-то непред- виденное, убедитесь в том, что список копируется с использованием сегмента, как это делается в нашем первом примере . УПРАЖНЕНИЯ 4.10. Сегменты: добавьте в конец одной из программ, написанных в этой главе, фрагмент, который делает следующее: • Выводит сообщение «The irst three items in the list are:», а затем использует сегмент для вывода первых трех элементов из списка. • Выводит сообщение «Three items from the middle of the list are:», а затем использует сегмент для вывода первых трех элементов из середины списка. • Выводит сообщение «The last three items in the list are:», а затем использует сегмент для вывода последних трех элементов из списка. 80 Глава 4 • Работа со списками 4.11. Моя пицца, твоя пицца: начните с программы из упражнения 4.1. Создайте копию списка с видами пиццы, присвойте ему имя friend_pizzas . Затем сделайте следующее: • Добавьте новую пиццу в исходный список. • Добавьте другую пиццу в список friend_pizzas • Докажите, что в программе существуют два разных списка. Выведите сообщение «My favorite pizzas are:», а затем первый список в цикле for . Выведите сообщение «My friend’s favorite pizzas are:», а затем второй список в цикле for. Убедитесь в том, что каждая новая пицца находится в соответствующем списке. 4.12. Больше циклов: во всех версиях foods .py из этого раздела мы избегали использования цикла for при выводе для экономии места. Выберите версию foods .py и напишите два цик- ла for для вывода каждого списка. Кортежи Списки хорошо подходят для хранения наборов элементов, которые могут из- меняться на протяжении жизненного цикла программы. Например, возможность модификации списков жизненно необходима при работе со списками пользовате- лей сайта или списками персонажей игры. Однако в некоторых ситуациях требу- ется создать список элементов, который не может изменяться. Кортежи (tuples) предоставляют именно такую возможность. В языке Python значения, которые не могут изменяться, называются неизменяемыми (immutable), а неизменяемый список называется кортежем. Определение кортежа Кортеж выглядит как список, не считая того, что вместо квадратных скобок ис- пользуются круглые скобки. После определения кортежа вы можете обращаться к его отдельным элементам по индексам точно так же, как это делается при работе со списком. Допустим, имеется прямоугольник, который в программе всегда должен иметь строго определенные размеры. Чтобы гарантировать неизменность размеров, мож- но объединить размеры в кортеж: dimensions.py ❶ dimensions = (200, 50) ❷ print(dimensions[0]) print(dimensions[1]) В точке определяется кортеж dimensions , при этом вместо квадратных скобок используются круглые. В точке каждый элемент кортежа выводится по отдельно- сти с использованием того же синтаксиса, который использовался для обращения к элементу списка: 200 50 Кортежи 81 Посмотрим, что произойдет при попытке изменения одного из элементов в кортеже dimensions : dimensions = (200, 50) ❶ dimensions[0] = 250 Код в точке пытается изменить первое значение, но Python возвращает ошибку типа. По сути, так как мы пытаемся изменить кортеж, а эта операция недопустима для объектов этого типа, Python сообщает о невозможности присваивания нового значения элементу в кортеже: Traceback (most recent call last): File "dimensions.py", line 3, in dimensions[0] = 250 TypeError: 'tuple' object does not support item assignment И это хорошо, потому что мы хотим, чтобы Python сообщал о попытке изменения размеров прямоугольника в программе, выдавая сообщение об ошибке. ПРИМЕЧАНИЕ Формально кортеж определяется наличием запятой; круглые скобки просто делают запись более аккуратной и понятной . Если вы хотите определить кортеж, состоящий из одного элемента, включите завершающую запятую: my_t = (3,) Обычно создание кортежа из одного элемента не имеет особого смысла . Тем не менее это может произойти при автоматическом генерировании кортежей . Перебор всех значений в кортеже Для перебора всех значений в кортеже используется цикл for , как и при работе со списками: dimensions = (200, 50) for dimension in dimensions: print(dimension) Python возвращает все элементы кортежа по аналогии с тем, как это делается со списком: 200 50 Замена кортежа Элементы кортежа не могут изменяться, но вы можете присвоить новое значение переменной, в которой хранится кортеж. Таким образом, для изменения размеров прямоугольника следует переопределить весь кортеж: 82 Глава 4 • Работа со списками ❶ dimensions = (200, 50) print("Original dimensions:") for dimension in dimensions: print(dimension) ❷ dimensions = (400, 100) ❸ print("\nModified dimensions:") for dimension in dimensions: print(dimension) Блок, начинающийся в точке , определяет исходный кортеж и выводит исходные размеры. В точке в переменной dimensions сохраняется новый кортеж, после чего в точке выводятся новые размеры. На этот раз Python не выдает сообщений об ошибке, потому что замена значения переменной является допустимой операцией: Original dimensions: 200 50 Modified dimensions: 400 100 По сравнению со списками структуры данных кортежей относительно просты. Используйте их для хранения наборов значений, которые не должны изменяться на протяжении жизненного цикла программы. УПРАЖНЕНИЯ 4.13. Шведский стол: меню «шведского стола» в ресторане состоит всего из пяти пунктов. Придумайте пять простых блюд и сохраните их в кортеже. • Используйте цикл for для вывода всех блюд, предлагаемых рестораном. • Попробуйте изменить один из элементов и убедитесь в том, что Python отказывает- ся вносить изменения. • Ресторан изменяет меню, заменяя два элемента другими блюдами. Добавьте блок кода, который заменяет кортеж, и используйте цикл for для вывода каждого элемен- та обновленного меню. Стиль программирования Итак, вы постепенно начинаете писать более длинные программы, и вам стоит по- знакомиться с некоторыми рекомендациями по стилевому оформлению кода. Не жалейте времени на то, чтобы ваш код читался как можно проще. Понятный код помогает следить за тем, что делает ваша программа, и упрощает изучение вашего кода другими разработчиками. Программисты Python выработали ряд соглашений по стилю, чтобы весь код имел хотя бы отдаленно похожую структуру. Научившись писать «чистый» код Python, |