Коллекции - списки, кортежи, словари, множества
Коллекции
Последовательности
Типы данных не всегда единичны, и порой их нужно перечислять в форме коллекции, собирая несколько элементов.
Последовательности — это упорядоченные коллекции элементов, доступные по индексу. К ним относятся строки (str), списки (list), кортежи (tuple), а также объекты range, bytes и другие. Все последовательности поддерживают общие операции: индексирование, срезы, конкатенацию, повторение и проверку на вхождение.
Базовые операции:
- Индексирование:
seq[i]— получение элемента по индексу (от 0). - Срезы:
seq[start:stop:step]— извлечение подпоследовательности. - Длина:
len(seq)— количество элементов. - Принадлежность:
x in seq— возвращаетTrue, если x содержится в последовательности. - Конкатенация:
seq1 + seq2— объединение (если типы совместимы). - Повторение:
seq * n— повторение последовательности n раз.
s = "hello"
print(s[1]) # 'e'
print(s[1:4]) # 'ell'
print('h' in s) # True
print(s * 2) # 'hellohello'
lst = [1, 2, 3]
print(lst + [4, 5]) # [1, 2, 3, 4, 5]
Циклы for естественно работают с последовательностями:
for item in [10, 20, 30]:
print(item)
Особенность строк — неизменяемость.
При попытке модификации через индекс возникнет TypeError. Для списков и кортежей это ограничение действует только для кортежей (они тоже неизменяемы). Но, собственно, давайте по порядку.
Коллекции — это типы данных, предназначенные для хранения упорядоченных или неупорядоченных множеств элементов. В языке Python коллекции реализованы как встроенные классы (встроенные типы), обеспечивающие различные семантики доступа, модификации и организации данных.
Основные встроенные коллекции включают:
list— список;tuple— кортеж;set— множество;dict— словарь.
Каждая из этих структур имеет свои особенности с точки зрения изменяемости, порядка элементов, уникальности и способов индексации. Выбор конкретной коллекции определяется требованиями к производительности, безопасности данных и логике алгоритма.
Списки (list).
Список — это упорядоченная, изменяемая коллекция элементов произвольных типов. Элементы списка хранятся последовательно и доступны по целочисленным индексам, начиная с нуля. Списки реализованы как динамические массивы с автоматическим управлением памятью.
Создание:
lst = [1, 2, 3]
Шаблон:
<список> = [<элемент>, <элемент>, ..., <элемент>]
<список>— имя переменной типаlist.- Элементы могут быть любого типа и не обязаны быть однородными.
Добавление элемента в список:
<список>.append(<значение>)
- Добавляет
<значение>в конец списка. - Метод изменяет исходный список.
Расширение списка итерируемым объектом:
<список>.extend(<итерируемый_объект>)
- Добавляет все элементы из
<итерируемый_объект>(например, другого списка) в конец текущего списка.
Вставка элемента по индексу:
<список>.insert(<индекс>, <значение>)
- Вставляет
<значение>в позицию<индекс>, сдвигая остальные элементы вправо.
Удаление элемента по значению:
<список>.remove(<значение>)
- Удаляет первое вхождение
<значение>из списка. - Если значение отсутствует — вызывается исключение
ValueError.
Удаление и получение элемента по индексу:
<элемент> = <список>.pop([<индекс>])
- Удаляет и возвращает элемент по указанному
<индекс>. По умолчанию — последний элемент (-1).
Сортировка списка:
<список>.sort(key=<функция>, reverse=<булево_значение>)
- Сортирует список на месте.
key— функция для получения ключа сравнения.reverse=True— сортировка по убыванию.
Обратный порядок элементов:
<список>.reverse()
- Переворачивает порядок элементов в списке на месте.
Индексация и срезы.
Доступ к элементам осуществляется по индексу:
lst[0] # первый элемент
lst[-1] # последний элемент
Срезы позволяют извлекать подпоследовательности:
lst[1:3] # элементы с индексами 1 и 2
lst[::2] # каждый второй элемент
Срезы возвращают новый объект list. Они поддерживают шаг, отрицательные индексы и обратный порядок.
Списки имеют следующие методы:
append(x)- Добавляет элемент x в конец списка.extend(iterable)- Добавляет все элементы итерируемого объекта в конец.insert(i, x)- Вставляет элемент x на позицию i.remove(x)- Удаляет первое вхождение элемента x.pop([i])- Удаляет и возвращает элемент по индексу (по умолчанию — последний).count(x)- Возвращает количество вхождений элемента x.sort(key=None, reverse=False)- Сортирует список in-place.reverse()- Переворачивает список in-place.copy()- Создаёт поверхностную копию списка.
numbers = [5, 2, 8, 1]
numbers.sort()
print(numbers) # [1, 2, 5, 8]
numbers.reverse()
print(numbers) # [8, 5, 2, 1]
Примечание: методы sort и reverse модифицируют исходный объект и возвращают None.
Списки гетерогенны (могут быть разных типов), изменяемы (допускается добавление, удаление, замена элементов), и представляют собой массив указателей на объекты. При расширении происходит перераспределение памяти с запасом.
Бинарные списки: array.array используются для эффективного хранения числовых данных большого объёма. Этот тип представляет собой массив примитивных значений (например, целых или вещественных чисел) фиксированного размера.
import array
arr = array.array('i', [1, 2, 3]) # массив 32-битных целых
Такие списки занимают меньше памяти по сравнению с обычным списком, быстрее при последовательном доступе и поддерживают те же операции, что и списки (индексация, срезы), но только для однотипных данных. Но - не поддерживают гетерогенные данные.
Если же требуется получить и использовать именно индекс, а не элемент, тогда используется функция enumerate():
my_list = ["яблоко", "банан", "апельсин", "груша"]
for index, item in enumerate(my_list):
print(f"Индекс: {index}, Элемент: {item}")
Кортежи (tuple)
Кортеж — это упорядоченная, неизменяемая коллекция элементов произвольных типов. После создания кортеж нельзя модифицировать: нельзя добавлять, удалять или заменять элементы. Кортежи неизменяемые (чтобы гарантировать целостность данных), хэшируемые (сам кортеж может использовать в качестве ключа в словаре или элементе множества), производительны и применяются часто для представления составных данных, например, координат (x, y) или возврат нескольких значений из функции.
Пример:
t = (1, 2, 3)
t = 1, 2, 3 # скобки опциональны
Кортежи поддерживают ограниченный набор методов:
count(x)— количество вхождений элемента x.index(x[, start[, end]])— индекс первого вхождения x.
Поскольку кортежи неизменяемы, методы модификации отсутствуют.
Создание кортежа:
<кортеж> = (<элемент>, <элемент>, ..., <элемент>)
или
<кортеж> = <элемент>, <элемент>, ..., <элемент>
Получение количества вхождений значения в кортеж:
<число> = <кортеж>.count(<значение>)
- Возвращает количество элементов, равных
<значение>.
Получение индекса первого вхождения значения:
<индекс> = <кортеж>.index(<значение>[, <начало>[, <конец>]])
- Возвращает индекс первого совпадения
<значение>в пределах[начало:конец].
Множества (set)
Множество — это неупорядоченная, изменяемая коллекция уникальных элементов. Элементы множества должны быть хэшируемыми (т.е. иметь метод __hash__ и быть сравнимыми).
Создание:
s = {1, 2, 3}
s = set([1, 2, 3]) # из итерируемого
# Список с дубликатами
items = ["тигр", "лев", "панда", "тигр", "заяц", "лев"]
# Превращаем в множество — дубликаты исчезнут!
unique_items = set(items)
print(unique_items) # {'тигр', 'лев', 'панда', 'заяц'}
Пустое множество создаётся только через set(), так как {} интерпретируется как пустой словарь.
Множества автоматически устраняют дубликаты:
{1, 1, 2} # → {1, 2}
Множества бывают изменяемыми и неизменяемыми.
Методы изменяемых множеств (set):
add(x)— добавить элемент.remove(x)— удалить элемент; ошибка, если нет.discard(x)— удалить, если есть; ошибки не возникает.pop()— удалить и вернуть произвольный элемент.clear()— очистить множество.
frozenset — неизменяемая версия множества. Поддерживает все операции чтения и теоретико-множественные операции, но не позволяет модифицировать содержимое. Полезен как ключ в словаре или элемент другого множества.
# set - изменяемый
mutable_set = {1, 2, 3}
mutable_set.add(4) # Работает
mutable_set.remove(2) # Работает
# frozenset - неизменяемый
frozen = frozenset({1, 2, 3})
# frozen.add(4) # Ошибка: AttributeError
# frozen.remove(2) # Ошибка: AttributeError
# frozenset хэшируемый
hash(frozenset({1, 2, 3})) # Работает
# set не хэшируемый
# hash({1, 2, 3}) # Ошибка: TypeError
# frozenset можно использовать как ключ
d = {frozenset({1, 2}): "value"} # Работает
# set нельзя использовать как ключ
# d = {{1, 2}: "value"} # Ошибка: TypeError
Объединение: | или .union()
first = {"тигр", "панда", "заяц"}
second = {"лев", "заяц", "медведь"}
all_animals = first | second
print(all_animals) # Все звери без повторов
Создание множества:
<множество> = {<элемент>, <элемент>, ..., <элемент>}
или
<множество> = set(<итерируемый_объект>)
- Все элементы должны быть хэшируемыми.
- Автоматически удаляются дубликаты.
Добавление элемента в множество:
<множество>.add(<значение>)
- Добавляет
<значение>в множество, если оно ещё не присутствует.
Удаление элемента из множества (с ошибкой при отсутствии):
<множество>.remove(<значение>)
- Удаляет
<значение>. Если его нет — возникает исключениеKeyError.
Удаление элемента из множества (без ошибки):
<множество>.discard(<значение>)
- Удаляет
<значение>, если оно есть. Не вызывает исключение при отсутствии.
Объединение двух множеств:
<новое_множество> = <множество_1> | <множество_2>
или
<новое_множество> = <множество_1>.union(<множество_2>)
- Возвращает новое множество, содержащее все уникальные элементы из обоих исходных.
Преобразование:
# Преобразование set в frozenset
frozen = frozenset({1, 2, 3})
# Преобразование frozenset в set
mutable = set(frozenset({1, 2, 3}))
Словари (dict)
Словарь — это изменяемая коллекция пар «ключ-значение», где ключи уникальны и хэшируемы, а значения могут быть любыми объектами. Доступ к значениям осуществляется по ключу. Словари реализованы как хеш-таблицы с открытой адресацией.
Создание:
d = {'a': 1, 'b': 2}
d = dict(a=1, b=2)
Ключи должны быть хэшируемыми (числа, строки, кортежи из хэшируемых объектов и т.д.). Значения же произвольные объекты, включая изменяемые. Словари сохраняют порядок вставки с Python 3.7+ (в 3.6 — как особенность CPython, с 3.7 — как гарантия языка).
Методы словарей:
keys()- Итератор ключей;values()- Итератор значений;items()- Итератор пар (ключ, значение);get(key, default=None)- Значение по ключу или значение по умолчанию;pop(key, default)- Удаляет и возвращает значение; если ключа нет — возвращает default;update(other)- Обновляет словарь парами из другого словаря или итерируемого;setdefault(key, default)- Возвращает значение, если ключ есть; иначе устанавливает и возвращает default;clear()- Очищает словарь;copy()- Поверхностная копия.
Безопасный доступ: .get()
print(person.get("рост")) # None (ничего)
print(person.get("рост", 140)) # 140 — значение по умолчанию
Пример - классический словарь-переводчик:
translator = {
"cat": "кошка",
"dog": "собака",
"house": "дом",
"sun": "солнце",
"book": "книга"
}
word = input("Введите слово на английском: ")
translation = translator.get(word, "Не знаю такого слова")
print(f"Перевод: {translation}")
Создание словаря:
<словарь> = {<ключ>: <значение>, <ключ>: <значение>, ..., <ключ>: <значение>}
или
<словарь> = dict(<ключ>=<значение>, ...)
- Ключи должны быть хэшируемыми; значения — произвольными.
Получение значения по ключу (с безопасным доступом):
<значение> = <словарь>.get(<ключ>[, <значение_по_умолчанию>])
- Возвращает значение, связанное с
<ключ>, или<значение_по_умолчанию>, если ключ отсутствует.
Установка значения по умолчанию при отсутствии ключа:
<значение> = <словарь>.setdefault(<ключ>, <значение_по_умолчанию>)
- Если
<ключ>отсутствует, добавляет пару<ключ>: <значение_по_умолчанию>и возвращает это значение. - Если ключ есть — возвращает существующее значение.
Обновление словаря парами из другого словаря:
<словарь>.update(<другой_словарь>)
- Добавляет или заменяет пары «ключ-значение» из
<другой_словарь>в текущий словарь.
Итерация по ключам, значениям и парам:
for <ключ> in <словарь>.keys():
...
for <значение> in <словарь>.values():
...
for <ключ>, <значение> in <словарь>.items():
...
.keys(),.values(),.items()возвращают итераторы, а не списки.