Книга: Простой Python. Современный стиль программирования. 2-е изд.
Назад: Глава 6. Создаем циклы с помощью ключевых слов while и for
Дальше: Глава 8. Словари и множества

Глава 7. Кортежи и списки

Человек отличается от низших приматов страстью к составлению списков.

Гарри Аллен Смит

В предыдущих главах мы говорили о базовых типах данных Python, таких как булевы значения, целочисленные значения, числа с плавающей точкой и строки. Если представлять их как атомы, то структуры данных, которые мы рассмотрим в этой главе, можно назвать молекулами. Так и есть: мы объединим базовые типы в более сложные структуры, которые вы будете использовать каждый день. Бо'льшая часть работы программиста состоит из «разрезания» данных и «склеивания» их в конкретные формы, поэтому сейчас вы узнаете, как пользоваться ножовками и клеевыми пистолетами.

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

В Python есть еще две структуры-последовательности: кортежи и списки. Они могут содержать ноль и более элементов. В отличие от строк в кортежах и списках допускаются элементы разных типов: по факту каждый элемент может быть любым объектом Python. Это позволяет создавать структуры любой сложности и глубины.

Почему же в Python имеются как списки, так и кортежи? Кортежи неизменяемы. Когда вы включаете в кортеж элемент (всего один раз), он «запекается» и больше не изменяется. Списки же можно изменять — добавлять и удалять элементы в любой удобный момент. Я покажу вам множество примеров использования обоих типов, сделав акцент на списках.

Кортежи

Давайте сразу же рассмотрим один очевидный вопрос. Вы могли слышать два возможных варианта произношения слова tuple (кортеж). Какой же из них является правильным? Гвидо ван Россум, создатель языка Python, написал в Twitter (): «Я произношу слово tuple как too-pull по понедельникам, средам и пятницам и как tub-pull — по вторникам, четвергам и субботам. В воскресенье я вообще об этом не говорю :)».

Создаем кортежи с помощью запятых и оператора ()

Синтаксис создания кортежей несколько необычен, что вы и увидите в следующих примерах.

Начнем с создания пустого кортежа с помощью оператора ():

>>> empty_tuple = ()

>>> empty_tuple

()

Чтобы создать кортеж, содержащий один элемент и более, после каждого элемента надо ставить запятую. Это вариант для кортежей с одним элементом:

>>> one_marx = 'Groucho',

>>> one_marx

('Groucho',)

Вы можете поместить элемент в круглые скобки и получить такой же кортеж:

>>> one_marx = ('Groucho',)

>>> one_marx

('Groucho',)

Однако следует иметь в виду: если в круглые скобки вы поместите один объект и опустите при этом запятую, в результате вы получите не кортеж, а тот же самый объект (в этом примере строку 'Groucho'):

>>> one_marx = ('Groucho')

>>> one_marx

'Groucho'

>>> type(one_marx)

<class 'str'>

Если в вашем кортеже более одного элемента, ставьте запятую после каждого из них, кроме последнего:

>>> marx_tuple = 'Groucho', 'Chico', 'Harpo'

>>> marx_tuple

('Groucho', 'Chico', 'Harpo')

При отображении кортежа Python выводит на экран скобки. Как правило, они не нужны для определения кортежа, но с ними более безопасно, так как они делают кортеж более заметным:

>>> marx_tuple = ('Groucho', 'Chico', 'Harpo')

>>> marx_tuple

('Groucho', 'Chico', 'Harpo')

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

>>> one_marx = 'Groucho',

>>> type(one_marx)

<class 'tuple'>

>>> type('Groucho',)

<class 'str'>

>>> type(('Groucho',))

<class 'tuple'>

Кортежи позволяют присваивать значение нескольким переменным одновременно:

>>> marx_tuple = ('Groucho', 'Chico', 'Harpo')

>>> a, b, c = marx_tuple

>>> a

'Groucho'

>>> b

'Chico'

>>> c

'Harpo'

Иногда это называется распаковкой кортежа.

Вы можете использовать кортежи для обмена значениями с помощью одного выражения, не применяя временную переменную:

>>> password = 'swordfish'

>>> icecream = 'tuttifrutti'

>>> password, icecream = icecream, password

>>> password

'tuttifrutti'

>>> icecream

'swordfish'

>>>

Создаем кортежи с помощью функции tuple()

Функция преобразования tuple() создает кортежи из других объектов:

>>> marx_list = ['Groucho', 'Chico', 'Harpo']

>>> tuple(marx_list)

('Groucho', 'Chico', 'Harpo')

Объединяем кортежи с помощью оператора +

Это похоже на объединение строк:

>>> ('Groucho',) + ('Chico', 'Harpo')

('Groucho', 'Chico', 'Harpo')

Размножаем элементы с помощью оператора *

Принцип похож на многократное использование оператора +:

>>> ('yada',) * 3

('yada', 'yada', 'yada')

Сравниваем кортежи

Сравнение кортежей похоже на сравнение списков:

>>> a = (7, 2)

>>> b = (7, 2, 9)

>>> a == b

False

>>> a <= b

True

>>> a < b

True

Итерируем по кортежам с помощью for и in

Итерирование по кортежам выполняется так же, как и итерирование по другим типам:

>>> words = ('fresh','out', 'of', 'ideas')

>>> for word in words:

...     print(word)

...

fresh

out

of

ideas

Изменяем кортеж

Этого сделать вы не можете! Как и строки, кортежи неизменяемы. Но ранее вы уже видели на примере строк, что можно сконкатенировать (объединить) кортежи и создать таким образом новый кортеж:

>>> t1 = ('Fee', 'Fie', 'Foe')

>>> t2 = ('Flop',)

>>> t1 + t2

('Fee', 'Fie', 'Foe', 'Flop')

Это означает, что вы можете изменить кортеж следующим образом:

>>> t1 = ('Fee', 'Fie', 'Foe')

>>> t2 = ('Flop',)

>>> t1 += t2

>>> t1

('Fee', 'Fie', 'Foe', 'Flop')

Это уже не тот же самый кортеж t1. Python создал новый кортеж из исходных t1 и t2 и присвоил ему имя t1. С помощью id() вы можете увидеть, когда имя переменной будет указывать на новое значение:

>>> t1 = ('Fee', 'Fie', 'Foe')

>>> t2 = ('Flop',)

>>> id(t1)

4365405712

>>> t1 += t2

>>> id(t1)

4364770744

Списки

Списки особенно удобны для хранения в них объектов в определенном порядке, особенно если порядок или содержимое нужно будет изменить. В отличие от строк список изменяем: вы можете добавить новые элементы, перезаписать существующие и удалить ненужные. Одно и то же значение может встречаться в списке несколько раз.

Создаем списки с помощью скобок []

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

>>> empty_list = [ ]

>>> weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']

>>> big_birds = ['emu', 'ostrich', 'cassowary']

>>> first_names = ['Graham', 'John', 'Terry', 'Terry', 'Michael']

>>> leap_years = [2000, 2004, 2008]

>>> randomness = ['Punxsatawney", {"groundhog": "Phil"}, "Feb. 2"}

Список first_names показывает, что значения не должны быть уникальными.

108896.png

Если вы хотите размещать в последовательности только уникальные значения и вам неважен их порядок, множество (set) может оказаться более удобным вариантом, чем список. В предыдущем примере список big_birds вполне мог быть множеством. О множествах вы прочитаете в главе 8.

Создаем список или преобразуем в список с помощью функции list()

Вы также можете создать пустой список с помощью функции list():

>>> another_empty_list = list()

>>> another_empty_list

[]

Функция list() преобразует другие итерабельные типы данных (например, кортежи, строки, множества и словари) в списки. В следующем примере строка преобразуется в список, состоящий из односимвольных строк:

>>> list('cat')

['c', 'a', 't']

В этом примере кортеж преобразуется в список:

>>> a_tuple = ('ready', 'fire', 'aim')

>>> list(a_tuple)

['ready', 'fire', 'aim']

Создаем список из строки с использованием функции split()

Как я упоминал в разделе «Разделяем строку с помощью функции split()» главы 5, функцию split() можно использовать для преобразования строки в список, указав некую строку-разделитель:

>>> talk_like_a_pirate_day = '9/19/2019'

>>> talk_like_a_pirate_day.split('/')

['9', '19', '2019']

Что, если в оригинальной строке содержится несколько включений строки-разделителя подряд? В этом случае в качестве элемента списка вы получите пустую строку:

>>> splitme = 'a/b//c/d///e'

>>> splitme.split('/')

['a', 'b', '', 'c', 'd', '', '', 'e']

Если бы вы использовали разделитель //, состоящий из двух символов, то получили бы следующий результат:

>>> splitme = 'a/b//c/d///e'

>>> splitme.split('//')

>>>

['a/b', 'c/d', '/e']

Получаем элемент с помощью конструкции [смещение]

Как и в случае со строками, вы можете извлечь одно значение из списка, указав его смещение:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes[0]

'Groucho'

>>> marxes[1]

'Chico'

>>> marxes[2]

'Harpo'

Так же и отрицательные индексы отсчитываются с конца строки:

>>> marxes[-1]

'Harpo'

>>> marxes[-2]

'Chico'

>>> marxes[-3]

'Groucho'

>>>

108925.png

Смещение должно быть допустимым для этого списка — позицией, которой вы ранее присвоили значение. Если вы укажете позицию, которая находится перед списком или после него, будет сгенерировано исключение (ошибка). Вот что случится, если мы попробуем получить шестого брата Маркс (Marxes) (смещение равно 5, если считать от нуля) или же пятого перед списком:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes[5]

Traceback (most recent call last):

   File "<stdin>", line 1, in <module>

IndexError: list index out of range

>>> marxes[-5]

Traceback (most recent call last):

   File "<stdin>", line 1, in <module>

IndexError: list index out of range

Извлекаем элементы с помощью разделения

Можно извлечь из списка подсписок, использовав разделение (slice):

>>> marxes = ['Groucho', 'Chico,' 'Harpo']

>>> marxes[0:2]

['Groucho', 'Chico']

Такой фрагмент списка тоже является списком.

Как и в случае со строками, при разделении можно пропускать некоторые значения. В следующем примере мы извлечем каждый нечетный элемент:

>>> marxes[::2]

['Groucho', 'Harpo']

Теперь начнем с последнего элемента и будем смещаться влево на 2:

>>> marxes[::-2]

['Harpo', 'Groucho']

И наконец, рассмотрим прием инверсии списка:

>>> marxes[::-1]

['Harpo', 'Chico', 'Groucho']

Ни одно из этих разделений не затронуло сам список marxes, поскольку мы не выполняли присваивание. Чтобы изменить порядок элементов в списке, используйте функцию list.reverse():

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes.reverse()

>>> marxes

['Harpo', 'Chico', 'Groucho']

108972.png

Функция reverse() изменяет список, но не возвращает его значения.

Как и в случае со строками, если при разделении указать некорректный индекс, исключение не сгенерируется. Будет использован ближайший корректный индекс или же возвращено пустое значение:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes[4:]

[]

>>> marxes[-6:]

['Groucho', 'Chico', 'Harpo']

>>> marxes[-6:-2]

['Groucho']

>>> marxes[-6:-4]

[]

Добавляем элемент в конец списка с помощью функции append()

Традиционный способ добавления элементов в список — вызов метода append(), который один за одним добавит их в конец списка. В предыдущих примерах мы забыли о Zeppo, но ничего страшного не случилось, поскольку список можно изменить. Добавим его прямо сейчас:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes.append('Zeppo')

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Zeppo']

Добавляем элемент на определенное место с помощью функции insert()

Функция append() добавляет элементы только в конец списка. Когда вам нужно добавить элемент и поставить его на заданную позицию, используйте функцию insert(). Если вы укажете смещение 0, элемент будет добавлен в начало списка. Если значение смещения выходит за пределы списка, элемент будет добавлен в конец, как делает и функция append(): таким образом, вам не нужно беспокоиться о том, что Python сгенерирует исключение:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes.insert(2, 'Gummo')

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Gummo']

>>> marxes.insert(10, 'Zeppo')

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Gummo', 'Zeppo']

Размножаем элементы с помощью оператора *

В главе 5 вы видели, что можно размножить символы строки с помощью оператора *. Точно так же можно сделать и со списками:

>>> ["blah"] * 3

['blah', 'blah', 'blah']

Объединяем списки с помощью метода extend() или оператора +

Можно объединить один список с другим, используя extend(). Предположим, что некий добрый человек дал нам новый список братьев Маркс, который называется others, и мы хотим добавить его в основной список marxes:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> others = ['Gummo', 'Karl']

>>> marxes.extend(others)

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Zeppo', 'Gummo', 'Karl']

Можно также использовать операторы + или +=:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> others = ['Gummo', 'Karl']

>>> marxes += others

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Zeppo', 'Gummo', 'Karl']

Если бы мы использовали append(), список others был бы добавлен как один из элементов списка, а не дополнил бы своими элементами список marxes:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> others = ['Gummo', 'Karl']

>>> marxes.append(others)

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Zeppo', ['Gummo', 'Karl']]

Это еще раз показывает, что список может содержать элементы разных типов. В этом случае — четыре строки и список из двух строк.

Изменяем элемент с помощью конструкции [смещение]

Так же как значение какого-либо элемента из списка можно получить по смещению, его можно и изменить:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes[2] = 'Wanda'

>>> marxes

['Groucho', 'Chico', 'Wanda']

Опять же смещение должно быть корректным для заданного списка.

Вы не можете таким способом изменить символ в строке, поскольку строки, в отличие от списков, неизменяемы. В списке можно изменить как количество элементов, так и сами элементы.

Изменяем элементы с помощью разделения

В предыдущем разделе вы увидели, как получить подсписок с помощью разделе­ния. Помимо этого, с помощью разделения можно присвоить значения под­списку:

>>> numbers = [1, 2, 3, 4]

>>> numbers[1:3] = [8, 9]

>>> numbers

[1, 8, 9, 4]

То, что находится справа от = и что вы присваиваете списку, может содержать иное количество элементов, нежели список, указанный слева:

>>> numbers = [1, 2, 3, 4]

>>> numbers[1:3] = [7, 8, 9]

>>> numbers

[1, 7, 8, 9, 4]

 

>>> numbers = [1, 2, 3, 4]

>>> numbers[1:3] = []

>>> numbers

[1, 4]

На самом деле то, что находится справа от оператора присваивания, может даже не быть списком. Подойдет любой итерабельный объект, элементы которого можно сделать элементами списка:

>>> numbers = [1, 2, 3, 4]

>>> numbers[1:3] = (98, 99, 100)

>>> numbers

[1, 98, 99, 100, 4]

 

>>> numbers = [1, 2, 3, 4]

>>> numbers[1:3] = 'wat?'

>>> numbers

[1, 'w', 'a', 't', '?', 4]

Удаляем заданный элемент с помощью оператора del

Только что наши консультанты проинформировали нас о том, что Гуммо (Gummo) был одним из братьев Маркс, а Карл (Karl) — не был, и кто бы ни добавил его ранее, он поступил очень неосмотрительно. Давайте это исправим:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Gummo', 'Karl']

>>> marxes[-1]

'Karl'

>>> del marxes[-1]

>>> marxes

['Groucho', 'Chico', 'Harpo', 'Gummo']

Когда вы удаляете заданный с помощью смещения элемент, все идущие следом за ним элементы сдвигаются, чтобы занять место удаленного, а длина списка уменьшается на единицу. Если вы удалите Chico из последней версии списка marxes, то получите такой результат:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Gummo']

>>> del marxes[1]

>>> marxes

['Groucho', 'Harpo', 'Gummo']

109032.png

del является оператором Python, а не методом списка — нельзя написать marxes[–1].del(). Это своего рода обратное присваивание, так как del выполняет противоположную присваиванию (=) операцию: открепляет имя от объекта Python и может освободить место объекта в памяти, если это имя являлось последней ссылкой на него.

Удаляем элемент по значению с помощью функции remove()

Если вы не знаете точно или вам все равно, в какой позиции находится элемент, используйте функцию remove(), чтобы удалить его по значению. Прощай, Groucho:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes.remove('Groucho')

>>> marxes

['Chico', 'Harpo']

Если у вас были повторяющиеся элементы списка с одинаковым значением, функция remove() удалит только первый найденный элемент.

Получаем и удаляем заданный элемент с помощью функции pop()

Получить элемент из списка и так же удалить его можно с помощью функции pop(). Если вызвать функцию pop() и указать смещение, она возвратит элемент, находящийся в заданной позиции. Если аргумент не указать, будет использовано значение –1. Так, вызов pop(0) вернет начальный элемент списка, а вызов pop() или pop(−1) — конечный, как показано далее:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> marxes.pop()

'Zeppo'

>>> marxes

['Groucho', 'Chico', 'Harpo']

>>> marxes.pop(1)

'Chico'

>>> marxes

['Groucho', 'Harpo']

109038.png

Пришло время для компьютерного жаргона! Не волнуйтесь, этого не будет на итоговом экзамене. Если вы используете функцию append(), чтобы добавить новые элементы в конец списка, и функцию pop(), чтобы удалить что-либо из конца того же списка, вы работаете со структурой данных, известной как очередь LIFO (last in, first out — «последним пришел, первым ушел»). Ее чаще называют стеком. Вызов pop(0) создает очередь FIFO (first in, first out — «первым пришел, первым ушел»). Это удобный способ собирать данные по мере их поступления и работать либо с самыми старыми (FIFO), либо с самыми новыми (LIFO).

Удаляем все элементы с помощью функции clear()

В Python 3.3 появился метод, позволяющий удалить все элементы из списка:

>>> work_quotes = ['Working hard?', 'Quick question!', 'Number one priorities!']

>>> work_quotes

['Working hard?', 'Quick question!', 'Number one priorities!']

>>> work_quotes.clear()

>>> work_quotes

[]

Определяем смещение по значению с помощью функции index()

Если вы хотите узнать смещение элемента в списке по его значению, используйте функцию index():

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> marxes.index('Chico')

1

Если значение встречается в списке более одного раза, возвращается смещение только первого найденного элемента:

>>> simpsons = ['Lisa', 'Bart', 'Marge', 'Homer', 'Bart']

>>> simpsons.index('Bart')

1

Проверяем на наличие элемента в списке с помощью оператора in

В Python наличие элемента в списке проверяется с помощью оператора in:

>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']

>>> 'Groucho' in marxes

True

>>> 'Bob' in marxes

False

Одно и то же значение может находиться в списке на нескольких позициях. Пока оно присутствует хотя бы в единственном экземпляре, оператор in будет возвращать значение True:

>>> words = ['a', 'deer', 'a' 'female', 'deer']

>>> 'deer' in words

True

109045.png

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

Подсчитываем количество включений значения с помощью функции count()

Чтобы подсчитать, сколько раз какое-либо значение встречается в списке, используйте функцию count():

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> marxes.count('Harpo')

1

>>> marxes.count('Bob')

0

 

>>> snl_skit = ['cheeseburger', 'cheeseburger', 'cheeseburger']

>>> snl_skit.count('cheeseburger')

3

Преобразуем список в строку с помощью функции join()

В разделе «Объединяем строки с помощью функции join()» главы 5 функция join() рассматривается более подробно, но вот еще один пример того, что можно сделать с ее помощью:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> ', '.join(marxes)

'Groucho, Chico, Harpo'

На первый взгляд это выглядит неправильным. Ведь функция join() предназначается для строк, а не для списков. Вы не можете написать marxes.join(','), даже если интуиция вам так подсказывает. Аргументом для функции join() является строка или любая итерабельная последовательность строк (в том числе и список), а выводом — строка. Если бы функция join() была только методом списка, ее нельзя было бы использовать для других итерабельных объектов, таких как кортежи и строки. Если вы хотите, чтобы она работала с любым итерабельным типом, нужно написать особый код для каждого типа и таким образом обработать подобное объединение. Это поможет вам запомнить: join()противоположнаsplit(), как показано здесь:

>>> friends = ['Harry', 'Hermione', 'Ron']

>>> separator = ' * '

>>> joined = separator.join(friends)

>>> joined

'Harry * Hermione * Ron'

>>> separated = joined.split(separator)

>>> separated

['Harry', 'Hermione', 'Ron']

>>> separated == friends

True

Меняем порядок элементов с помощью функций sort() или sorted()

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

функцию списка sort(), которая сортирует сам список;

общую функцию sorted(), которая возвращает отсортированную копиюсписка.

Если элементы списка являются числами, они по умолчанию сортируются по возрастанию. Если строками — сортируются в алфавитном порядке:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> sorted_marxes = sorted(marxes)

>>> sorted_marxes

['Chico', 'Groucho', 'Harpo']

sorted_marxes — это копия, ее создание не изменило оригинальный список:

>>> marxes

['Groucho', 'Chico', 'Harpo']

Но вызов функции списка sort() для marxes список изменит:

>>> marxes.sort()

>>> marxes

['Chico', 'Groucho', 'Harpo']

Если все элементы списка одного типа (в списке marxes находятся только строки), функция sort() отработает корректно. Иногда можно даже смешать типы, например, целые числа и числа с плавающей точкой, поскольку в рамках выражений они автоматически преобразуются друг в друга:

>>> numbers = [2, 1, 4.0, 3]

>>> numbers.sort()

>>> numbers

[1, 2, 3, 4.0]

По умолчанию список сортируется по возрастанию, но можно добавить аргумент reverse=True, чтобы отсортировать список по убыванию:

>>> numbers = [2, 1, 4.0, 3]

>>> numbers.sort(reverse=True)

>>> numbers

[4.0, 3, 2, 1]

Получаем длину списка с помощью функции len()

Функция len() возвращает количество элементов списка:

>>> marxes = ['Groucho', 'Chico', 'Harpo']

>>> len(marxes)

3

Присваиваем с помощью оператора =

Когда вы присваиваете один список более чем одной переменной, изменение списка в одном месте также меняет его и в других, как показано далее:

>>> a = [1, 2, 3]

>>> a

[1, 2, 3]

>>> b = a

>>> b

[1, 2, 3]

>>> a[0] = 'surprise'

>>> a

['surprise', 2, 3]

Что же собой представляет переменная b? Ее значение все еще равно [1,2,3] или изменилось на ['surprise',2,3]? Проверим:

>>> b

['surprise', 2, 3]

Помните аналогию с коробкой (объектом) и этикеткой (именем переменной) из главы 2? b просто ссылается на тот же список объектов, что и а (обе этикетки указывают на одну и ту же коробку с объектом), поэтому, независимо от того, с помощью какого имени мы изменяем содержимое списка, изменение отражается на обеих переменных:

>>> b

['surprise', 2, 3]

>>> b[0] = 'I hate surprises'

>>> b

['I hate surprises', 2, 3]

>>> a

['I hate surprises', 2, 3]

Копируем списки с помощью функций copy() и list() или путем разделения

Вы можете скопировать значения в отдельный новый список с помощью любого из этих методов:

функции copy();

• функции преобразования list();

разделения списка [:].

Оригинальный список снова будет присвоен переменной а. Мы создадим b с помощью функции списка copy(), c — с помощью функции преобразования list(), а d — с помощью разделения списка:

>>> a = [1, 2, 3]

>>> b = a.copy()

>>> c = list(a)

>>> d = a[:]

Опять же b, c и d являются копиямиa — новыми объектами со своими собственными значениями и не связанными с исходным списком объектов [1,2,3], на который ссылается a. Изменение aне влияет на копии b, c и d:

>>> a[0] = 'integer lists are boring'

>>> a

['integer lists are boring', 2, 3]

>>> b

[1, 2, 3]

>>> c

[1, 2, 3]

>>> d

[1, 2, 3]

Копируем все с помощью функции deepcopy()

Функция copy() хорошо работает, если все элементы списка являются неизменяемыми. Как вы видели ранее, изменяемые значения (например, списки, кортежи или словари) являются ссылками. Изменение оригинала или копии отразится на каждом из них.

Воспользуемся предыдущим примером, но в качестве последнего элемента списка a вместо целого числа 3 используем список [8,9]:

>>> a = [1, 2, [8, 9]]

>>> b = a.copy()

>>> c = list(a)

>>> d = a[:]

>>> a

[1, 2, [8, 9]]

>>> b

[1, 2, [8, 9]]

>>> c

[1, 2, [8, 9]]

>>> d

[1, 2, [8, 9]]

Пока все хорошо. Теперь изменим элемент нашего подсписка из списка а:

>>> a[2][1] = 10

>>> a

[1, 2, [8, 10]]

>>> b

[1, 2, [8, 10]]

>>> c

[1, 2, [8, 10]]

>>> d

[1, 2, [8, 10]]

Значение элемента a[2] теперь является списком, и его элементы могут быть изменены. Все методы копирования списков, которые мы уже рассмотрели, были поверхностными: это не оценочное суждение — речь идет именно о том, насколько глубоко распространяется копирование.

Для того чтобы это исправить, нужно использовать функцию deepcopy():

>>> import copy

>>> a = [1, 2, [8, 9]]

>>> b = copy.deepcopy(a)

>>> a

[1, 2, [8, 9]]

>>> b

[1, 2, [8, 9]]

>>> a[2][1] = 10

>>> a

[1, 2, [8, 10]]

>>> b

[1, 2, [8, 9]]

Функция deepcopy() может работать с вложенными списками, словарями и другими объектами.

Подробнее о директиве import вы узнаете из главы 11.

Сравниваем списки

Сравнить списки можно с помощью операторов сравнения, таких как ==, < и другие. Операторы проходят по обоим спискам, сравнивая элементы с одинаковым смещением. Если список a короче списка b и все элементы списка a присутствуют в списке b, будет считаться, что список a меньше списка b:

>>> a = [7, 2]

>>> b = [7, 2, 9]

>>> a == b

False

>>> a <= b

True

>>> a < b

True

Итерируем по спискам с помощью операторов for и in

Из главы 6 вы узнали, как можно проитерировать по строке с помощью цикла for. Однако гораздо чаще доводится выполнять итерирование по спискам:

>>> cheeses = ['brie', 'gjetost', 'havarti']

>>> for cheese in cheeses:

...     print(cheese)

...

brie

gjetost

havarti

Как и раньше, ключевое слово break завершает выполнение цикла for, а ключевое слово continue позволяет перейти к следующей итерации:

>>> cheeses = ['brie', 'gjetost', 'havarti']

>>> for cheese in cheeses:

...     if cheese.startswith('g'):

...         print("I won't eat anything that starts with 'g'")

...         break

...     else:

...         print(cheese)

...

brie

I won't eat anything that starts with 'g'

Вы по-прежнему можете использовать опциональный блок else, если цикл for завершился без break:

>>> cheeses = ['brie', 'gjetost', 'havarti']

>>> for cheese in cheeses:

...     if cheese.startswith('x'):

...         print("I won't eat anything that starts with 'x'")

...         break

...     else:

...         print(cheese)

... else:

...     print("Didn't find anything that started with 'x'")

...

brie

gjetost

havarti

Didn't find anything that started with 'x'

Если цикл for не отработал ни разу, выполнение также продолжится в блоке else:

>>> cheeses = []

>>> for cheese in cheeses:

...     print('This shop has some lovely', cheese)

...     break

... else:  # отсутствие break означает отсутствие сыра

...     print('This is not much of a cheese shop, is it?')

...

This is not much of a cheese shop, is it?

Поскольку список cheeses в этом примере был пуст, цикл forcheeseincheeses ни разу не отработал, а оператор break не был применен.

Итерируем по нескольким последовательностям с помощью функции zip()

Есть еще один хороший прием — параллельное итерирование по нескольким последовательностям с помощью функции zip():

>>> days = ['Monday', 'Tuesday', 'Wednesday']

>>> fruits = ['banana', 'orange', 'peach']

>>> drinks = ['coffee', 'tea', 'beer']

>>> desserts = ['tiramisu', 'ice cream', 'pie', 'pudding']

>>> for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts):

...     print(day, ": drink", drink, "eat", fruit, "enjoy", dessert)

...

Monday : drink coffee — eat banana — enjoy tiramisu

Tuesday : drink tea — eat orange — enjoy ice cream

Wednesday : drink beer — eat peach — enjoy pie

Функция zip() прекратит свою работу, когда будет выполнена самая короткая последовательность. Один из списков (desserts) оказался длиннее остальных, поэтому никто не получит пудинг, пока мы не увеличим остальные списки.

В главе 8 показывается, как с помощью функции dict() можно создавать словари из двухэлементных последовательностей, таких как кортежи, списки или строки. Функция zip() позволяет пройти по нескольким последовательностям и создать кортежи из элементов с одинаковым смещением. Создадим два кортежа из соответствующих друг другу английских и французских слов:

>>> english = 'Monday', 'Tuesday', 'Wednesday'

>>> french = 'Lundi', 'Mardi', 'Mercredi'

Теперь используем функцию zip(), чтобы объединить эти кортежи с составлением пар. Значение, возвращаемое функцией zip(), само по себе не является списком или кортежем, но его можно преобразовать в любую из этих последовательностей:

>>> list(zip(english, french))

[('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')]

Передадим результат работы функции zip() непосредственно функции dict() — и у нас готов небольшой англо-французский словарь!

>>> dict(zip(english, french))

{'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}

Создаем список с помощью списковых включений

Вы уже знаете, что список можно создать с помощью квадратных скобок и функции list(). Сейчас мы рассмотрим создание списка с помощью списковых включений (их еще называют генераторами списков, представлением списков и т.д.): этот механизм использует итерирование с помощью ключевых слов for/in, которое мы только что рассмотрели.

Вы можете создать список целых чисел от 1 до 5, добавляя их по одному, например так:

>>> number_list = []

>>> number_list.append(1)

>>> number_list.append(2)

>>> number_list.append(3)

>>> number_list.append(4)

>>> number_list.append(5)

>>> number_list

[1, 2, 3, 4, 5]

Или же используя итератор и функцию range():

>>> number_list = []

>>> for number in range(1, 6):

...     number_list.append(number)

...

>>> number_list

[1, 2, 3, 4, 5]

Или же преобразуя в список сам результат работы функции range():

>>> number_list = list(range(1, 6))

>>> number_list

[1, 2, 3, 4, 5]

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

[выражение for элемент in итерабельныйобъект]

Вот так списковое включение может составить список целых чисел:

>>> number_list = [number for number in range(1,6)]

>>> number_list

[1, 2, 3, 4, 5]

Первая переменная number нужна для формирования значений для списка, то есть для размещения результата работы цикла в переменной number_list. Вторая переменная number является частью цикла for. Чтобы показать, что первая переменная number является выражением, попробуем такой вариант:

>>> number_list = [number-1 for number in range(1,6)]

>>> number_list

[0, 1, 2, 3, 4]

Списковое включение перемещает цикл в квадратные скобки. Этот пример включения не намного проще предыдущего, однако можно пойти дальше и добавить условное выражение, которое выглядит следующим образом:

[выражение for элемент in итерабельныйобъект if условие]

Используем новое включение для создания списка только из четных чисел, расположенных в диапазоне от 1 до 5 (помните, что выражение number%2 имеет значение True для четных чисел и False для нечетных):

>>> a_list = [number for number in range(1,6) if number % 2 == 1]

>>> a_list

[1, 3, 5]

Такое включение выглядит чуть более компактно, чем в обычном варианте:

>>> a_list = []

>>> for number in range(1,6):

...     if number % 2 == 1:

...         a_list.append(number)

...

>>>  a_list

[1, 3, 5]

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

>>> rows = range(1,4)

>>> cols = range(1,3)

>>> for row in rows:

...     for col in cols:

...         print(row, col)

...

1 1

1 2

2 1

2 2

3 1

3 2

Теперь воспользуемся включением и присвоим его переменной cells, создавая тем самым список кортежей (row,col):

>>> rows = range(1,4)

>>> cols = range(1,3)

>>> cells = [(row, col) for row in rows for col in cols]

>>> for cell in cells:

...     print(cell)

...

(1, 1)

(1, 2)

(2, 1)

(2, 2)

(3, 1)

(3, 2)

Кстати, вы можете воспользоваться распаковкой кортежа, чтобы получить значения row и col из каждого кортежа по мере итерирования по списку cells:

>>> for row, col in cells:

...     print(row, col)

...

1 1

1 2

2 1

2 2

3 1

3 2

Фрагменты forrow... и forcol... в списковом включении также могут иметь свои проверки if.

Списки списков

Списки могут содержать элементы различных типов, в том числе и другие списки, как показано далее:

>>> small_birds = ['hummingbird', 'finch']

>>> extinct_birds = ['dodo', 'passenger pigeon', 'Norwegian Blue']

>>> carol_birds = [3, 'French hens', 2, 'turtledoves']

>>> all_birds = [small_birds, extinct_birds, 'macaw', carol_birds]

Как же будет выглядеть список списков all_birds?

>>> all_birds

[['hummingbird', 'finch'], ['dodo', 'passenger pigeon', 'Norwegian Blue'], 'macaw',

[3, 'French hens', 2, 'turtledoves']]

Посмотрим на его первый элемент:

>>> all_birds[0]

['hummingbird', 'finch']

Первый элемент является списком — это список small_birds, который мы указали как первый элемент списка all_birds. Нетрудно догадаться, чем является второй элемент:

>>> all_birds[1]

['dodo', 'passenger pigeon', 'Norwegian Blue']

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

>>> all_birds[1][0]

'dodo'

Индекс [1] ссылается на второй элемент списка all_birds, а [0] — на первый элемент внутреннего списка.

Кортежи или списки?

Вместо списков можно использовать кортежи, однако у них меньше возможностей — нет функций append(), insert() и т.д., поскольку кортеж не может быть изменен после создания. Почему бы тогда не работать всегда со списками вместо кортежей?

Кортежи занимают меньше места.

• Вы не сможете уничтожить элементы кортежа по ошибке.

• Вы можете использовать кортежи в качестве словарных ключей (см. главу 8).

Именованные кортежи (см. раздел «Именованные кортежи» в главе 10) могут служить более простой альтернативой объектам.

Здесь я не буду вдаваться в подробности использования кортежей. Чаще при решении повседневных задач вам доведется обращаться именно к спискам и словарям.

Включений кортежей не существует

Изменяемые типы (списки, словари и множества) имеют включения. Неизменяемые типы, такие как строки и кортежи, нужно создавать другими методами, описанными в соответствующих разделах.

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

>>> number_thing = (number for number in range(1, 6))

Однако в круглых скобках находится что-то совершенно иное — включение генератора, и оно вернет объект генератора:

>>> type(number_thing)

<class 'generator'>

Генераторы мы рассмотрим более подробно в разделе «Генераторы» главы 9. Генератор — это один из способов предоставления данных итератору.

Читайте далее

Следующие темы — словари и множества — настолько объемные, что заслужили отдельную главу.

Упражнения

Используйте списки и кортежи из чисел (глава 3) и строки (глава 3), чтобы представить самые разные элементы реального мира.

7.1. Создайте список years_list, содержащий год, в который вы родились, и каждый последующий год вплоть до вашего пятого дня рождения. Например, если вы родились в 1980 году, список будет выглядеть так: years_list=[1980,1981,1982,1983,1984,1985].

Если вам меньше пяти лет, но вы уже читаете эту книгу, то я даже не знаю, что сказать.

7.2. В какой год из списка years_list был ваш третий день рождения? Учтите, в первый год вам было 0 лет.

7.3. В какой год из списка years_list вам было больше всего лет?

7.4. Создайте список things, содержащий три элемента: "mozzarella", "cinderella", "salmonella".

7.5. Напишите с большой буквы тот элемент списка things, который означает человека, а затем выведите список. Изменился ли элемент списка?

7.6. Переведите сырный элемент списка things в верхний регистр целиком и выведите список.

7.7. Удалите из списка things заболевание, получите Нобелевскую премию и затем выведите список на экран.

7.8. Создайте список с элементами "Groucho", "Chico" и "Harpo"; назовите его surprise.

7.9. Напишите последний элемент списка surprise со строчной буквы, затем выведите эту строку в обратном порядке и с прописной буквы.

7.10. Используйте списковое включение, чтобы создать список с именем even, в котором будут содержаться четные числа в промежутке range(10).

7.11. Попробуйте создать генератор рифм для прыжков через скакалку. Напе­чатайте последовательность двухстрочных рифм. Начните программу с этого фрагмента:

start1 = ["fee", "fie", "foe"]

rhymes = [

    ("flop", "get a mop"),

    ("fope", "turn the rope"),

    ("fa", "get your ma"),

    ("fudge", "call the judge"),

    ("fat", "pet the cat"),

    ("fog", "walk the dog"),

    ("fun", "say we're done"),

    ]

start2 = "Someone better"

Затем следуйте инструкциям.

Для каждого кортежа (first,second) в списке rhymes.

Для первой строки:

• выведите на экран каждую строку списка start1: начните ее с большой буквы, а закончите восклицательным знаком с пробелом;

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

• Для второй строки:

• выведите на экран значение переменной start2 и пробел;

• выведите на экран значение переменной second и точку.

Назад: Глава 6. Создаем циклы с помощью ключевых слов while и for
Дальше: Глава 8. Словари и множества

dofermerdat
этот. породы кроликов с фотографиями мясныефермер ру форум рассадакрупнейшие сельскохозяйственные районы зарубежной европыкрс мясо