Простой Python — страница 8 из 66

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

1. Сколько секунд в часе? Используйте интерактивный интерпретатор как калькулятор и умножьте количество секунд в минуте (60) на количество минут в часе (тоже 60).

2. Присвойте результат вычисления предыдущего задания (секунды в часе) переменной, которая называется seconds_per_hour.

3. Сколько секунд в сутках? Используйте переменную seconds_per_hour.

4. Снова посчитайте количество секунд в сутках, но на этот раз сохраните результат в переменной seconds_per_day.

5. Разделите значение переменной seconds_per_day на значение переменной seconds_per_hour. Используйте деление с плавающей точкой (/).

6. Разделите значение переменной seconds_per_day на значение переменной seconds_per_hour. Используйте целочисленное деление (//). Совпадает ли полученный результат с ответом на предыдущее упражнение, если не учитывать символы.0 в конце?

Глава 3. Наполнение Python: списки, кортежи, словари и множества

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

Списки и кортежи

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

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

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


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

Списки

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

Создание списков с помощью оператора [] или метода list()

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

>>> empty_list = []

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

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

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

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

>>> another_empty_list = list()

>>> another_empty_list

[]


В разделе «Включения» главы 4 показан еще один способ создать список, который называется включением списка.


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


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

Преобразование других типов данных в списки с помощью функции list()

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

>>> list('cat')

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

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

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

>>> list(a_tuple)

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

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

>>> birthday = '1/6/1952'

>>> birthday.split('/')

['1', '6', '1952']

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

>>> 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'

>>>


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

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

>>> marxes[5]

Traceback (most recent call last):

···File "", line 1, in 

IndexError: list index out of range

>>> marxes[-5]

Traceback (most recent call last):

···File "", line 1, in 

IndexError: list index out of range

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

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

>>> 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] — на первый элемент внутреннего списка.

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

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

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

>>> marxes[2] = 'Wanda'

>>> marxes

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

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

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

Отрежьте кусочек — извлечение элементов с помощью диапазона смещений

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

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

>>> marxes[0:2]

['Groucho', 'Chico']

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

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

>>> marxes[::2]

['Groucho', 'Harpo']

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

>>> marxes[::-2]

['Harpo', 'Groucho']

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

>>> marxes[::-1]

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

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

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

>>> marxes.append('Zeppo')

>>> marxes

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

Объединяем списки с помощью метода 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']]

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

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

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

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

>>> marxes

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

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

>>> marxes

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

Удаление заданного элемента с помощью функции del

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

>>> del marxes[-1]

>>> marxes

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

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

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

>>> marxes[2]

'Harpo'

>>> del marxes[2]

>>> marxes

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

>>> marxes[2]

'Gummo'


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

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

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

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

>>> marxes.remove('Gummo')

>>> marxes

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

Получение заданного элемента и его удаление с помощью функции 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']


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

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

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

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

>>> marxes.index('Chico')

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


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

Определяем количество включений значения с помощью функции 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()» раздела «Строки» главы 2 функция 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()

Вам часто нужно будет изменять порядок элементов по их значениям, а не по смещениям. Для этого 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

Присваивание с помощью оператора =, копирование с помощью функции copy()

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

>>> 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();

• разбиения списка [: ].

Оригинальный список снова будет присвоен переменной а. Мы создадим 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]

Кортежи