Контейнеры в Python

Множества. Класс Set

Set - это множество, в котором элементы не повторяются. Элементы множества никак не упорядочены. Создание множества:

In [1]:
a = set()
a.add(3)  # добавление элемента, O(1)
b = {5}
print(a, b)
{3} {5}
In [2]:
c = {4, 4, 2, 6}  # элементы не повторяются
print(c)  # порядок элементов не важен
{2, 4, 6}
In [4]:
lst = [7, 1, 3, 3, 8]
a = set(lst)  # приведение списка к множеству (выкинули повторения)
print(a)

print(list(a))  # приведение множества к списку
{8, 1, 3, 7}
[8, 1, 3, 7]

Операции над множествами

Чтобы проверить, находится ли элемент в множестве, можно использовать оператор in. В среднем такая проверка занимает O(1).

In [5]:
b = {4, 5}
print(b)
print(5 in b)
if 8 in b:
    print("YES")
else:
    print("NO")
print(len(b))  # размер (мощность) множества - количество элементов
{4, 5}
True
NO
2

Удаление элемента из множества, в среднем за O(1):

In [6]:
a = {1, 2, 4, 5, 6}
a.discard(5)
print(a)
a.remove(4)
print(a)

a.discard(8000)
print(a)
a.remove(8000)
print(a)
{1, 2, 4, 6}
{1, 2, 6}
{1, 2, 6}
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-6-5f2bf569c4f6> in <module>()
      7 a.discard(8000)
      8 print(a)
----> 9 a.remove(8000)
     10 print(a)

KeyError: 8000

Над двумя множествами A и B можно производить операции:

  • Объединение. В объединение входят элементы, которые есть или в первом, или во втором множестве. В среднем работает за O(len(A) + len(B)).
  • Разность. Из элементов первого множества вычёркиваются элементы второго множества. В среднем работает за O(len(A)).
  • Пересечение. В пересечение входят только те элементы, которые есть и в первом, и во втором множестве. В среднем работает за O(min(len(A), len(B))).
  • Симметрическая разность. Элементы входят либо только в первое, либо только во второе множество. В среднем работает за O(len(A) + len(B)).
In [7]:
a = {2, 3, 4}
b = {5, 2}
print(a | b)  # объединение (or)
print(a - b)  # разность
print(a & b)  # пересечение (and)
print(a ^ b)  # симметричная разность (xor)
{2, 3, 4, 5}
{3, 4}
{2}
{3, 4, 5}

В питоне два множества можно сравнить. Множество A меньше множества B, если все элементы множества A содержатся в множестве B, и при этом A != B. Сравнения выполняются за O(len(A) + len(B)).

In [11]:
a = {2, 3, 4}
b = {4, 3, 2}

print(a, b)

# Сравнения
print(a < b)
print(a > b)
print(a >= b)
print(a == b)
{2, 3, 4} {2, 3, 4}
False
False
True
True

Проход по множеству

In [12]:
a = {1, 2, 6, 9, 3}

for elem in a:
    print(elem, end=" ")
print()

while a:
    elem = a.pop()
    print(elem, end=" ")
9 2 3 1 6 
9 2 3 1 6 

Так же, как и для списков, существуют генераторы множеств:

In [13]:
a = {i ** 2 for i in range(10)}
print(a)
{0, 1, 64, 4, 36, 9, 16, 49, 81, 25}

Map, zip и другие полезные фишки

Map применяет функцию (первый параметр) к каждому элементу из списка (второй параметр).

In [19]:
lst = list(map(min, [[4, 0], [5, 7], [7]]))
print(lst, type(lst[0]))
[0, 5, 7] <class 'int'>

Оператор * распаковывает список в параметры функции.

In [20]:
lst = [[1], [3], [5], [3]]
print(*lst)
[1] [3] [5] [3]

Распаковка кортежей

Тут, вроде бы, ничего нового:

In [21]:
a, b, c = 1, 2, 3
print(a, b, c)

a, b, c = [1, 2, 3]
print(a, b, c)

a, b, c = (2 * i + 1 for i in range(3))
print(a, b, c)
1 2 3
1 2 3
1 3 5

Но в 3-м питоне можно ещё и так:

In [24]:
a, (b, c), d = [1, (2, 3), 4]
print(a, b, c, d)

a, b, *c = [1, 2, 3, 4, 5]
print(a, b, c)
1 2 3 4
1 2 [3, 4, 5]

Zip

Zip берёт первые элементы списка и объединяет их в один кортеж. Потом берёт вторые и тоже объединяет. И так далее. Если где-то есть лишние элементы, которые нельзя "запаковать", то они игнорируются.

In [25]:
a = [1, 2, 4, 8]
b = [5, 6, 3]
print(list(zip(a, b)))
print(list(zip(b, a)))

for x, y in zip(a, b):
    print(x, y)

c = [3, 4]
print(list(zip(a, b, c)))
[(1, 5), (2, 6), (4, 3)]
[(5, 1), (6, 2), (3, 4)]
1 5
2 6
4 3
[(1, 5, 3), (2, 6, 4)]

О, но так же можно и матрицу повернуть!

In [26]:
matrix = [[1, 0, 0, 0],
          [0, 1, 0, 2],
          [0, 0, 1, 0],
          [4, 1, 1, 1]]

turned_matrix = [list(row) for row in zip(*matrix)]
print(turned_matrix)
print(list(map(list, zip(*matrix))))
[[1, 0, 0, 4], [0, 1, 0, 1], [0, 0, 1, 1], [0, 2, 0, 1]]
[[1, 0, 0, 4], [0, 1, 0, 1], [0, 0, 1, 1], [0, 2, 0, 1]]

Help, dir

Я, как всегда, забыл, как работает remove. Если элемента в списке нет, что произойдёт?

In [27]:
help(list.remove)
print(dir(set))
# help(set) - попробуйте сами
Help on method_descriptor:

remove(...)
    L.remove(value) -> None -- remove first occurrence of value.
    Raises ValueError if the value is not present.

['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

Словари. Класс dict

Словари - это пары (ключ, значение). Создание словаря:

In [30]:
a = {}
b = {1: 3, "4": 8}
c = dict()  
d = dict(((1, 2), ("a", 13)))
e = dict(one=1, two=2)
print(a, b, c, d, e, sep="\n", end="\n\n")

c['hello'] = 'world'  # добавление элемента, O(1)
e['one'] = 8          # изменение элемента, O(1)
e.pop('two')          # удаление элемента, O(1)
print(c, e, sep="\n")
print(b[1], d["a"], b.get("4"), b.get("8"), e.get("three", 0))  # e[0] - KeyError
{}
{1: 3, '4': 8}
{}
{1: 2, 'a': 13}
{'two': 2, 'one': 1}

{'hello': 'world'}
{'one': 8}
3 13 8 None 0
In [35]:
to_sort = [(3, 4), 1, 1, 6, 7, 1, 6, "5", "2", "2", "2", "1", "1", "1", "0", "3", "3", "2", "2", "5", "5", "5", "3", "1"]

count_sort = {}
for elem in to_sort:
    count_sort[elem] = count_sort.get(elem, 0) + 1

print(count_sort)
{1: 3, '1': 4, 6: 2, 7: 1, '3': 3, '2': 5, '0': 1, (3, 4): 1, '5': 4}

Стандартные методы

  • dict.items() - кортежи из пар (ключ, значение)
  • dict.keys() - список ключей
  • dict.values() - список значений
  • len(dict) - размер словаря
In [36]:
a = {"a": 18, "b": 10, "c": 13}

for x, y in a.items():
    print("%s: %s" % (x, y))
print()

for x in a.keys():
    print(x, a[x], end=" ")
print()

for x in a.values():
    print(x, end=" ")
print()

print(len(a))
a: 18
b: 10
c: 13

a 18 b 10 c 13 
18 10 13 
3

Лямбда-функции

Анонимные функции без названия

In [38]:
print(list(map(lambda x: int(x) - 1, input().split())))
print([int(x) - 1 for x in input().split()])
2 4 5
[1, 3, 4]
2 4 5
[1, 3, 4]
In [39]:
lst = [3, 5, 6]
func_array = [min, max, lambda x: x[0] * 4]
[func(lst) for func in func_array]
Out[39]:
[3, 6, 12]
In [45]:
lst = [1, 900, 6, 2, 332040, 0, 111111, 54]
lst = [(4, 5), (3, 8)]
lst.sort(key=lambda x: x[0])
print(lst)
[(3, 8), (4, 5)]
In [52]:
def f(func, x, y):
    return func(x, y)

vector_len = lambda x, y: (x ** 2 + y ** 2) ** 0.5
print(vector_len(3, 4))
print(vector_len(-3, 7))

# Можно функции использовать почти как обычные переменные
another_name = vector_len
print(another_name(1, 1))

print(f(lambda x, y: y, 1, 3))
5.0
7.615773105863909
1.4142135623730951
3

И другие фишки

In [53]:
# Пронумеровать список
a = ['Hello', 'world', '!']
for i, x in enumerate(a):
    print('{}: {}'.format(i, x))

# То же самое:
for i in range(len(a)):
    print('%d: %s' % (i, a[i]))
0: Hello
1: world
2: !
0: Hello
1: world
2: !
In [54]:
# Инвертировать словарь
m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict(zip(m.values(), m.keys()))
Out[54]:
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
In [55]:
# В генераторах можно писать и if
[x * 2 for x in range(20) if x % 3 == 1]
Out[55]:
[2, 8, 14, 20, 26, 32, 38]
In [ ]:
# if в одну строчку
print("YES" if 5 % 2 == 1 else "NO")
In [59]:
# Словарь со значением по умолчанию
from collections import defaultdict

d = defaultdict(int)
d["a"] += 5
print(d)

d = {}
d["a"] = d.get("a", 0) + 5
print(d)

d2 = defaultdict(lambda: 'abc')
d2[3] += 'a'
print(d2)
defaultdict(<class 'int'>, {'a': 5})
{'a': 5}
defaultdict(<function <lambda> at 0x7fa97cb62488>, {3: 'abca'})