Перейти к основному содержимому

Коллекции - списки, кортежи, словари, множества

Разработчику Архитектору

Коллекции

Последовательности

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

Последовательности — это упорядоченные коллекции элементов, доступные по индексу. К ним относятся строки (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() возвращают итераторы, а не списки.