Контейнерами называют типы, которые умеют содержать в себе другие типы (часто их еще называют «коллекциями»). Иногда используют названия «структуры данных». С помощью этих конструкций можно делать удобные конструкции, например, создавать не три переменные, таскать и помнить о них везде, а создать один контейнер, в котором есть все три, и обрабатывать их скопом.
Нас сейчас интересуют три таких сущности, с которыми мы начнем знакомиться в этом блоке:
list — список. В Python список — это упорядоченная коллекция объектов, при этом сами объекты не обязаны быть одного и того же типа. В Phyton списки создаются через квадратные скобки [ ].
tuple — кортеж. Эти ребята, по своей сути, почти ничем не отличаются от списков, кроме того, что их компоненты нельзя изменять после создания. Таплы создаются с помощью круглых скобок ( ).
dict — словарь. Словарь — это структура, которая дает возможность связывать пары ключ-значение и достать значение по ключу. Наверное, самая непростая для понимания из всех трех. В Python за словарями закреплены фигурные скобки { }.
Немного про особенности всех трёх
У всех этих трех типов есть свои нюансы, которые мы подробнее разберем позднее. Но, например, за счет того, что элементы tuple нельзя изменять, интерпретатор всегда знает, сколько они занимают места в памяти и как с ними работать. Иногда это может повлиять на скорость выполнения (в лучшую сторону). У таплов еще есть несколько удобных особенностей, которые мы подробнее рассмотрим позже. Списки гарантируют порядок элементов, которые они в себе хранят. При этом часто таплы и списки взаимозаменяемы (например, и те, и другие имеют механизм для извлечения только части последовательности). А словари позволяют строить сложные зависимости и предоставляют механизм их обхода. За счет особенности хранения, словари позволяют быстро получать необходимые элементы по ключу.
Списки и таплы
Как я уже говорил, сейчас мы рассмотрим только три основных типа контейнеров: списки, словари и tuples. Начнем со списков и кортежей.
Tuples иногда называют кортежами (хорошо хоть не крестражами), но это такое странное слово, что вы почти никогда его не услышите. Нормальные люди говорят "тапл", когда имеют в виду tuple (и даже "тупл", на мой взгляд, звучит лучше).
Таплы и списки очень похожи, чтобы их создать мы используем, соответственно, или круглые скобки, или квадратные:
t = (2, 12, 85, 7)
print("Тапл:", t)
print("Второй элемент тапла:", t[1])
L = [3, 4, 15, 16, 23, 42]
print("Список:", L)
print("Третий элемент списка:", L[2])
Разница между списками и таплами в том, что в списках можно менять элементы после создания, а в таплах нет. Типы данных, которые нельзя изменить, называют иммутабельными (immutable). Это полезное слово, которое стоит запомнить, оно нам еще пригодится.
Обратите внимание: чтобы посмотреть во второй элемент тапла, мы написали t[1], а чтобы посмотреть третий элемент списка, L[2]. В питоне нумерация начинается с нуля, есть некоторые исторические причины, почему так получилось, но пока просто запомним это как данность.
Но и это не все! Помимо указания положительного числа для доступа к элементам, можно указывать отрицательные, чтобы получать элементы с конца. Но так как «минус ноль» это все равно «ноль», то тут уже чуть привычнее: последний элемент будет L[-1], предпоследний L[-2] («второй с конца») и так далее.
Задание
Давайте разберем небольшой кусочек кода:
planets = ["Сатурн", "Марс", "Венера"]
preps = ["в", "из", "за"]
# отвечает на вопрос "в ком/чем"
signs = ["Водолее", "Весах", "Скорпионе", "Стрельце", "Змееносце", "Рыбах"]
Мы хотим поправить код выше, чтобы в конце вывести фразу 'Марс в Стрельце'. Какую строчку надо дописать, чтобы получить этот результат?
Добавление элементов в список
Чтобы расширять списки, их можно складывать, главное, складывать их с теми же типами (то есть list с list, а tuple с tuple, но не перемешивать):
t1 = (2, 12, 85, 7)
t2 = (32, 12, 6)
t = t1 + t2 print(t)
L1 = [2, 12]
L1.append(85)
L1 = L1 + [7]
L2 = [32, 12, 6]
L = L1 + L2 print(L)
Как видите, когда мы складываем два списка, то получаем новый список, состоящий из этих двух. Это можно использовать, чтобы добавить элемент в список. Еще списки можно расширять, добавляя им элементы с помощью метода append (но только поштучно). Код ниже иллюстрирует это:
planets = ["Земля", "Венера", "Меркурий"]
print(planets)
planets.append("Марс")
planets.append("Сатурн")
print(planets)
print(planets + ["Уран", "Нептун"])
Удаление элементов из списка
Чтобы убрать последний элемент из списка, используется метод pop(). Он вытащит этот элемент из списка (и его можно будет сохранить в отдельную переменную) и сократит его длину. Рассмотрим такой блок кода:
planets = ["Земля", "Венера", "Меркурий", "Марс", "Церера"]
print(planets[-1])
Такой код выведет последний элемент списка: это планета, о которой, как вы выяснили, никто из обычно читающих гороскопы не знает. Поэтому логично ее из этого списка убрать.
Словари
Третий важный тип контейнеров — это словари. Они позволяют сделать отображение одной переменной в другую переменную. Так как в питоне все — объект, то это может быть как соответствие "строка":"число", так и "число":"строка", "строка":"строка", "строка":"bool" и тому подобное. Пары, которые хранятся в словаре, называются "ключ-значение".
locations = {} # пустой словарь
locations["Валентин"] = "Москва"
locations["Андрей"] = "Санкт-Петербург"
locations["Светлана"] = "Казань"
print(locations)
print("Иван" in locations)
print("Валентин" in locations)
Обратите внимание на последние две строки: в них мы проверяем, есть ли в словаре такие ключи. Словари — это очень полезный контейнер, особенно если учесть, что они могут быть вложенными. Для примера: если вы представите себе адресную книгу, ключом в которой может быть имя, а значением адрес или телефон. Подробнее словари мы разберем в следующих модулях. По моему личному опыту, на понимание того, как ими пользоваться, уходит некоторое время, больше, чем необходимо, чтобы понять списки и таплы.
Строка как список
На самом деле, строка — это просто последовательность символов. Есть один нюанс: в отличие от элементов списка, конкретные символы строки нельзя переопределять. В этом смысле они, скорее, ведут себя как tuple, а не как list.
luck = "неожиданности"
print(luck[5]) # выведет шестой символ
luck[-2] = "Т" # такой код не выполнится, упадёт
luck[3] = "ш" # такой код тоже не выполнится
Но если строка — это список, то можно добавлять к ней элементы, как к списку? А как быть, если мы хотим к строке добавить число?
year = 2008
month = "December"
day = 3 year + month + day # ошибка
str(year) + month + str(day) # нормально
Таким образом, все, что не строка, надо явно превратить в строку, прежде чем дописывать ее к другой строке. Соединять строки через операцию сложения — это не единственный способ, но для небольшого числа строк вполне рабочий.
Довольно частой операцией является нахождение длины строки. Для того, чтобы узнать длину строки, используется функция len, например, в примере выше len(month) вернет 8, потому что столько символов в слове "December". Функция len работает и для коллекций (списков, словарей и таплов), в этом случае она возвращает общее число элементов.