June 5, 2020

Python: структуры данных

Списки (list)

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

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

Т.к. мы можем добавлять и удалять данные в списках — то они называются изменяемыми (mutable) типов данных.

Краткий обзор объектов и классов

Списки являются отличными примером использования объектов и классов. Когда мы используем переменную i и присваиваем ей значение, например целочисленное 5 — вы можете представлять себе это как создание объекта/интанса с именем i класса integer.

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

Например — Python предоставляет метод append класса list, который позволяет добавить элемент в список. Так, mylist.append('item') добавит строку «item» к списку mylist.

Кроме того — класс может так же иметь поля/атрибуты, которые являются ничем иным, как переменными этого класса.

Пример списков в Python

Возьмём следующий скрипт:

#!/usr/bin/env python

shoplist = ['apple', 'mango', 'carrot', 'banana']

print('I have', len(shoplist), 'items to purchase.')

print('These items are:', end=' ')
for item in shoplist:
    print(item, end=' ')

print('\nI also have to buy rice.')
shoplist.append('rice')
print('My shopping list is now', shoplist)

print('I will sort my list now')
shoplist.sort()
print('Sorted shopping list is', shoplist)

print('The first item I will buy is', shoplist[0])
olditem = shoplist[0]
del shoplist[0]
print('I bought the', olditem)
print('My shopping list is now', shoplist)

Результат его выполнения:

Как это работает

Переменная shoplist представляет собой «список покупок». В ней мы храним строки — названия того, что требуется купить, но вы можете добавить в список любой тип объекта, включая числа и даже другие списки (вложенные списки):

>>> shoplist = ['apple', 'mango', 'carrot', 'banana']
>>> otherlist = ['item1', 'item2']
>>> shoplist.append(otherlist)
>>> print(shoplist)
['apple', 'mango', 'carrot', 'banana', ['item1', 'item2']]

Кроме того — мы можем использовать цикл for..in для того, что бы получить все элементы списка:

>>> for i in shoplist:
...   print(i)
... 
apple
mango
carrot
banana
['item1', 'item2']

Кроме метода append() выше — мы можем использовать оператор del, что бы удалить элемент списка.

Например — удалить последний элемент (вложенный список otherlist, добавленный с помощью append() в конец списка):

>>> del shoplist[-1]
>>> for i in shoplist:
...   print(i)
... 
apple
banana
carrot
mango

Мы так же использовали метод sort для сортировки элементов списка:

>>> shoplist.sort()
>>> for i in shoplist:
...   print(i)
... 
apple
banana
carrot
mango

Кортежи (tuple)

Кортежи используются для хранения нескольких объектов. По сути кортежи схожи со списками, с той разницей что они не предоставляют такой функциональности, как списки. Главное их отличие — кортежи явлются неизменямым (immutable) типом данных.

Кортежи задаются через запятую и опционально — с добавлением круглых скобочек:

>>> tuple = 'item1', 'item2'
>>> type(tuple)
<class 'tuple'>
>>> tuple = ('item1', 'item2')
>>> type(tuple)
<class 'tuple'>

Пример кортежей в Python

Рассмотрим такой скрипт:

#!/usr/bin/env python

# I would recommend always using parentheses
# to indicate start and end of tuple
# even though parentheses are optional.
# Explicit is better than implicit.
zoo = ('python', 'elephant', 'penguin')
print('Number of animals in the zoo is', len(zoo))

new_zoo = 'monkey', 'camel', zoo
print('Number of cages in the new zoo is', len(new_zoo))
print('All animals in new zoo are', new_zoo)
print('Animals brought from old zoo are', new_zoo[2])
print('Last animal brought from old zoo is', new_zoo[2][2])
print('Number of animals in the new zoo is',
      len(new_zoo)-1+len(new_zoo[2]))

Результат его выполнения:

Как это работает

Переменная zoo является кортежем элементов.

Мы можем использовать метод len() для получения длины кортежа, что свидельствуюет о том, что как и списки — кортежи являются последовательностями (см. ниже).

>>> zoo = ('python', 'elephant', 'penguin')
>>> len(zoo)
3

По аналогии со списками — мы можем так же использовать вложенные кортежи:

>>> newnew_zoo = 'monkey', 'camel', zoo
>>> print(newnew_zoo)
('monkey', 'camel', ('python', 'elephant', 'penguin'))

Как и со списками — мы можем использовать срезы (slice) для доступа к элементам кортежа. При этом помните, что нумерация элементов начинается с 0:

>>> print(newnew_zoo[0])
monkey
>>> print(newnew_zoo[-1])
('python', 'elephant', 'penguin')
>>> print(newnew_zoo[-1][0])
python

Словари (dictionary)

Словарь представлояет собой что-то вроде «записной книги» — адрес человека зная только его имя: в словаре ключ (key) ассоциируется со значением (value).

Поните, что все ключи в одном словаре должны быть уникальными.

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

Пара ключ:значение в словаре указываются с помощью нотации dict = {key1:value1, key2: valu2}.

Все ключи в словаре хранятся в неупорядоченном состоянии.

Словари являются объекатами класса dict.

Пример словаря в Python

Рассмотрим такой скрипт:

#!/usr/bin/env python

# 'ab' is short for 'a'ddress'b'ook

ab = {
    'Swaroop': '[email protected]',
    'Larry': '[email protected]',
    'Matsumoto': '[email protected]',
    'Spammer': '[email protected]'
}

print("Swaroop's address is", ab['Swaroop'])

# Deleting a key-value pair
del ab['Spammer']

print('\nThere are {} contacts in the address-book\n'.format(len(ab)))

for name, address in ab.items():
    print('Contact {} at {}'.format(name, address))

# Adding a key-value pair
ab['Guido'] = '[email protected]'

if 'Guido' in ab:
    print("\nGuido's address is", ab['Guido'])

Результат его выполнения:

Как это работает

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

>>> ab = {
...     'Swaroop': '[email protected]',
...     'Larry': '[email protected]',
...     'Matsumoto': '[email protected]',
...     'Spammer': '[email protected]'
... }
>>> print(ab['Swaroop'])
[email protected]

Используя уже знакомый оператор del — мы можем удалять пару ключ-значение, при этом достаточно знать только ключ:

>>> del ab['Swaroop']
>>> print(ab['Swaroop'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Swaroop'
>>> print(ab)
{'Larry': '[email protected]', 'Matsumoto': '[email protected]', 'Spammer': '[email protected]'}

Далее — мы используем метод items(), который возвращает нам список кортежей, где каждый кортеж хранит пару ключ:значение:

>>> print(ab.items())
dict_items([('Larry', '[email protected]'), ('Matsumoto', '[email protected]'), ('Spammer', '[email protected]')])

Более того — мы можем использовать цикл for..in, присваивая ключ в переменную name а значение — в address:

>>> for name, address in ab.items():
...   print(name, address)
... 
Larry [email protected]
Matsumoto [email protected]
Spammer [email protected]

И так же мы может добfвить новую пару ключ:значение в словарь:

>>> print(ab['Guido'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Guido'
>>> ab['Guido'] = '[email protected]'
>>> print(ab['Guido'])
[email protected]

И проверять наличие ключа, используя оператор in:

>>> if 'Guido' in ab:
...   print(ab['Guido'])
... 
[email protected]

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

Списки, кортежи и строки являются примерами последовательностей, но что такое последовательность?

Главной отличительной возможностью последовательностей является проверка членства (membership test), т.е. применение выражений in и not in, а так же использование операций индексирования (indexing operations), которые позволяют получать определённые элементы из последовательности.

Три типа последовательностей, упомянутых выше — списки, кортежи и строки, так же имеют возможность применения слайсинга (slicing), которая позволяет получить часть последовательности.

Пример последовательностей в Python

Рассмотрим такой скрипт:

#!/usr/bin/env python

shoplist = ['apple', 'mango', 'carrot', 'banana']
name = 'swaroop'

# Indexing or 'Subscription' operation #
print('Item 0 is', shoplist[0])
print('Item 1 is', shoplist[1])
print('Item 2 is', shoplist[2])
print('Item 3 is', shoplist[3])
print('Item -1 is', shoplist[-1])
print('Item -2 is', shoplist[-2])
print('Character 0 is', name[0])

# Slicing on a list #
print('Item 1 to 3 is', shoplist[1:3])
print('Item 2 to end is', shoplist[2:])
print('Item 1 to -1 is', shoplist[1:-1])
print('Item start to end is', shoplist[:])

# Slicing on a string #
print('characters 1 to 3 is', name[1:3])
print('characters 2 to end is', name[2:])
print('characters 1 to -1 is', name[1:-1])
print('characters start to end is', name[:])

Результат его выполнения:

Как это работает

Индексирование

Сначала мы применяем индексы для получения элемента последовательности:

>>> shoplist = ['apple', 'mango', 'carrot', 'banana']
>>> print(shoplist[0])
apple

При этом помните, что индексы в Python начинаются с нуля, т.е. для получения первого элемента — используется индекс 0 ([0]), а для получения, например, четвёртого элемента — используется индекс 3 ([3]).

Кроме того — вы можете использовать отрицательные индексы — в таком сулчае отсчёт будет выполнен от конца последательности, т.е. [-1] вернёт последний элемент, [-2] — предпоследний и т.д.:

>>> print(shoplist[-1])
banana
Слайсинг

Операция слайсингасреза«) выполняется с указанием имени последовательности и опционально — пары чисел (индексов), разделённых двоеточием:

>>> print(shoplist[1:3])
['mango', 'carrot']

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

>>> print(shoplist[:3])
['apple', 'mango', 'carrot']
>>> print(shoplist[3:])
['banana']

Либо не указывать их вообще:

>>> print(shoplist[:])
['apple', 'mango', 'carrot', 'banana']
>>> print(shoplist)
['apple', 'mango', 'carrot', 'banana']

Как и с индексами — вы можете использовать отрицательные значения для получения среза:

>>> print(shoplist[:-1])
['apple', 'mango', 'carrot']
>>> print(shoplist[-1:-1])
[]
>>> print(shoplist[-1:])
['banana']

Более того — допустимо указание третьего аргумента, который задаёт шаг среза (по умолчанию равен единице):

>>> print(shoplist[::2])
['apple', 'carrot']

Множества (set)

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

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

Например:

>>> bri = set(['brazil', 'russia', 'india'])
>>> 'india' in bri
True
>>> 'usa' in bri
False
>>> bric = bri.copy()
>>> bric.add('china')
>>> bric.issuperset(bri)
True
>>> bri.remove('russia')
>>> bri & bric
{'india', 'brazil'}