Сайт вебмастера

Структуры данных Python: списки, кортежи, словари, множества

31-03-2023Время чтения ~ 6 мин.Python 5173

Массивы данных в Python реализованы достаточно своеобразно, что сильно отличает его от других языков программирования. Если вы изучаете Python как второй язык, то его структуры мягко говоря, вызывают недоумение. Если же вы начнёте изучать другой язык после Python'а, то сильно удивитесь отсутствию списков, кортежей, словарей и множеств.

В других языках, да и вообще в программировании, такие структуры называются одним словом: массивы. Но в Python это приводит к путанице, поскольку используя синтаксис массивов, применяются другие названия, а под словом «массив» подразумевается вообще отдельная библиотека. Попробуем разобраться во всех этих хитростях.

Общее понятие массива данных в программировании

Предположим у нас есть несколько клиентов, которые различаются по имени. Без массива пришлось бы сделать примерно так (код на PHP):

$client1 = 'Маша';
$client2 = 'Вася';
$client3 = 'Аня';

Под массивом обычно принимается набор элементов. В большинстве языков для массивов используются квадратные скобки.

$clients = ['Маша', 'Вася', 'Аня'];

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

print($clients[0]);

Точно также можно изменить элемент, обратившись к нему по индексу.

$clients[1] = 'Петя';

То есть элементы массива представляют собой упорядоченное расположение элементов в памяти.

Есть ещё одна разновидность массивов, которая называется ассоциативным массивом. Он использует пару «ключ-значение».

$client = [
    'name' => 'Маша', 
    'city' => 'Киев', 
    'year' => '1998'
];

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

print($client['city']);

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

Отличия Python: список и словарь

Python не использует слово «массив» и это часто вносит путаницу. Вместо этого Python разделяет понятие массива на список (list) и словарь (dict).

Список задаётся точно также, как и обычный массив и с ним можно делать все те же операции:

Clients = ['Маша', 'Вася', 'Аня']

print(Clients[0])
Clients[1] = 'Петя'

print(Clients) # ['Маша', 'Петя', 'Аня']

Но список не может быть ассоциативным массивом. Ассоциативный массив в Python — это словарь:

Client = {
    'name': 'Маша', 
    'city': 'Киев', 
    'year': '1998'
}

print(Client['city']) # Киев

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

Таким образом первое отличие Python — вместо слова «массив» следует использовать «список» или «словарь», чтобы показать его устройство.

Неизменяемый список - кортеж

Python выделяет особый вид списка, который будет неизменяемым — кортеж (tuple). Если в списке можно изменить любой элемент, то кортеж задаётся только один раз и больше не меняется.

t = ('Маша', 'Вася', 'Аня')

t[1] = 'Петя' # TypeError: 'tuple' object does not support item assignment

Для создания кортежа используются уже круглые скобки.

Зачем нужен кортеж? Для меня — это одна из загадок, поскольку для своих задач обычного списка более чем достаточно. Но официальное объяснение — использование там, где требуется постоянное значение хэша — это т.н. неизменяемые объекты. Если мы не планируем операции по изменению элементов, то можно обойтись более простым объектом кортежа, чем список с многочисленными методами.

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

t = 'Маша', 'Вася', 'Аня'

print(t[1]) # 'Вася'

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

t = 'Маша',

print(t[0]) # 'Маша'

Если запятую убрать, то мы получим, что переменная содержит обычную строку. Из-за этой особенности, желательно для всех кортежей добавлять запятую в конце перечисления значений.

t = 'Маша', 'Вася', 'Аня',

Поэтому круглые скобки используют только для того, чтобы исключить неопределённость.

Множество set

Ещё одной отличительной чертой Python является набор уникальных элементов множество (set). По своей сути — это тот же словарь, только состоящий из ключей.

st = {'Маша', 'Вася', 'Аня', 'Маша'}

print(st) # {'Маша', 'Аня', 'Вася'}

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

inElement = ['planet', 'sputnik', 'planet']
inElement = list(set(inElement))
print(inElement) # {'sputnik', 'planet'}

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

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

d1 = {'sputnik', 'planet'}
d1.add('comet')
print(d1)  # {'comet', 'sputnik', 'planet'}

d1.remove('planet')
print(d1)  # {'sputnik', 'comet'}

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

fset = frozenset({1, 2, 3})
fset.add(4) # AttributeError: 'frozenset' object has no attribute 'add'

Возникает вопрос зачем вообще нужны множества, если с ними столько нюансов? Дело в том, что Python предлагает много операций над множествами. Это очень похоже на SQL-команды UNION или даже JOIN, где происходит объединение результатов по разным алгоритмам.

# комбинации множеств
a = {1, 2}
b = {2, 3}

# пересечение — элементы в обоих множествах
print(a & b)  # {2}
print(a.intersection(b))  # {2} — или так

# объединение
print(a | b)  # {1, 2, 3}
print(a.union(b))  # {1, 2, 3} — или так

# разность (те, что в первом, но нет во втором множестве)
print(a - b)  # {1}
print(a.difference(b))  # {1} — или так

# исключающие ИЛИ
print(a ^ b)  # {1, 3}
print(a.symmetric_difference(b))  # {1, 3} — или так

Модуль array

Чтобы мы окончательно запутались, Python предлагает ещё и модуль array с помощью которого можно создавать те же списки, только определенного типа данных.

Зачем это нужно? Это некая имитация того, что есть в языках со строгой типизацией. Например в Паскале (Java и многие другие языки) следует указывать из какого типа данных будет состоять массив. Это сделано для того, чтобы можно было определить сколько памяти нужно выделить под переменную.

var a: array[1..10] of integer;

Поскольку в Python динамическая типизация, то сделать подобное можно так:

import array

ar = array.array('i', [1, 2, 3, 4, 5])

Дальше работа с происходит с помощью методов модуля array.

Итого

Массивы часто используются в программах. Нужно просто запомнить, что в Python они делятся на разные варианты. В 99% случаев хватает списков и словарей.

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

Множества — самый «капризный» вариант — их следует использовать только там где требуется уникальность элементов. В Python нет нормальной встроенной функции для удаления дублей из списков, поэтому приходится использовать set(). Поэтому кажется, что списки постоянно используются, но нет, это скорее вынужденная мера, чтобы не изобретать свой велосипед или использовать дополнительные модули, вроде itertools.

На собеседованиях часто пытаются проверить знания этих особенностей Python. Например подсовывают код, где пытаться изменить кортеж или получить элемент множества по индексу. Это 100% уловка на внимательность. К реальному программированию это имеет мало отношения — просто достаточно знать особенности массивов Python, чтобы их полноценно использовать.

Похожие записи