# Python ## Типы данных-коллекции ### Раздел 1. Типы данных-последовательности Тип данных, реализующий последовательность, это такой тип данных, для которого доступен оператор вхождения, функция получения длины (len()), получения среза и к нему можно доступаться как к итератору. Мы уже рассмотрели тип str, в будущем будут рассмотрены bytearray и bites, а на данный момент сосредоточимся на tuple и лист. Все эти типы являются встроенными. #### Кортежи Кортежем (tuple) называется упорядоченная последовательность из 0 или более ссылок на объекты. Кортежи поддерживают тот же оператор извлечения среза, что и строки. Кортежи являются неизменяемыми, так что если в ходе работы вам необходима изменяемая последовательность — обратитесь к списком, либо преобразуйте кортеж в список при помощи вызова list(). Как и всегда, tuple может быть вызван как функция. Без передачи аргумента, будет возвращен пустой объект. При передачи в качестве аргумента объекта типа tuple, будет создана его поверхностная копия. Во всех остальных случаях будет совершена попытка привести аргумент к типу tuple. Данная функция принимает не более одного аргумента. Кортежи могут быть созданы заключением элементов в скобки (даже если элементов нет () — пустой кортеж) или перечислением элементов кортежа через запятую (только не при передачи таким образом кортежа в качестве аргумента функции. Для кортежей доступны всего два метода: count и index. t.count(x) возвращает число вхождений x в t, а t.index(x) возвращает индекс самого левого вхождения x в t, либо возбуждает ValueError, если x не входит в t. К тому же, для кортежей доступны операторы + (конкатенация), \* (повторение), [] (взятие среза), in и not in (проверка на вхождение). Комбинированные версии присваивания += и \*= могут использоваться, несмотря на то, что кортежи неизменяемы. Просто будет создан новый объект, который будет присвоен переменной (то же происходит и со строками). Для кортежей доступны операции сравнения: < <= == != >= >. Они выполняют сравнение поэлементно и рекурсивно. Кортежи позволяют выполнить один из самых известных трюков в Python: ```python x = 1 y = 2 x, y = y, x print(x, y) ``` 2 1 Также, если элементами итератора являются кортежи, то вы их сразу можете распаковывать: ```python for hero, villain in (('Batman', 'Joker'), ('Superman', 'Lex Luthor'), ('Green Lantern', 'Sinestro')): print(hero, 'vs', villain) ``` Batman vs Joker Superman vs Lex Luthor Green Lantern vs Sinestro #### Списки Список есть упорядоченная последовательность из нуля или более ссылок на объекты. Как и кортежи, для списков доступно взятие среза. Но в отличие от кортежей, списки изменяемы. Как следствие, мы можем изменять не только список целиком или его отдельные элементы, но и целые срезы в нем. Тип list может быть вызван как функция. Без аргументов list() вернет пустой список. Если в качестве аргумента был передан объект типа list, то будет создана его поверхностная копия. Если аргумент дан, но не является объектом класса list, то будет произведена попытка приведения его к типу list. Данная функция принимает не более одного аргумента. Списки могут быть созданы также перечислением элементов через запятую в квадратных скобках или при помощи генераторов списков. Для типа list доступны операции сравнения, работают аналогично случаю с кортежами. Списки могут быть вложены в друг друга, проитерированы, от них можно взять срез, — все то же, что и для кортежей. Можно использовать операторы in, not int, +, +=, *, *=, функцию len() и конструкцию del. Также есть оператор \*, который может использоваться для получения остальной части списка в случае выбора нескольких элементов: ```python heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash'] batman, *rest = heroes print(batman, rest) batman, *mid, flash = heroes print(batman, mid, flash) ``` Batman ['Superman', 'Green Lantern', 'Flash'] Batman ['Superman', 'Green Lantern'] Flash Также есть оператор распаковки \*, который используется для передачи элементов списка по отдельности в качестве аргументов функции: ```python heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash'] def print4(a, b, c, d): print(a) print(b) print(c) print(d) print4(*heroes) ``` Batman Superman Green Lantern Flash Для списков доступны следующие методы: | Метод | Описание | | ----- | :------- | | L.append(x) | Добавляет элемент x в конец списка L | | L.count(x) | Подсчитывает число вхождений элемента x в список L | | L.extend(m) | Добавляет элементы m, к которому должен быть обеспечен доступ как к итератору, в конец списка L (то же, что и L += m | | L.index(x, start, end) | Возвращает индекс самого левого вхождения x в L. Если start и end заданы, то поиск происходит в срезе L[start:end]. Если элемент не найден, то возбуждается исключение ValueError | | L.insert(i, x) | Вставляет элемент x на позицию с индексом i в список L | | L.pop() | Удаляет последний элемент списка L и возвращает его | | L.pop(i) | Удаляет элемент списка L с индексом i и возвращает его | | L.remove(x) | Удаляет самое левое вхождение элемента x из списка L. Если элемент не найден, то возбуждается исключение ValueError | | L.reverse() | Разворачивает список | | L.sort(...) | Сортирует список. Аргумент key отвечает за функцию порядка, а reversed за способ сортировки (по невозрастанию / неубыванию) | #### Генераторы списков Для получения списка как результат какой-то операции над итератором используются генераторы списков. Есть две формымы генератора списков: [выражение for элемент in итератор] [выражение for элемент in итератор if условие] В самом простом случае выражение и есть элемент: ```python print([x for x in range(10)]) print([2*x for x in range(10)]) print([2*x for x in range(10) if x % 3 == 0]) ``` [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] [0, 6, 12, 18] Последняя форма в разы удобнее, чем запись: ```python lst = [] for x in range(10): if x % 3 == 0: lst.append(2*x) print(lst) ``` [0, 6, 12, 18] ### Раздел 2. Типы-множества Типы данных-множества — типы, для которых доступны оператор вхождения in, функция len и доступ, как к итератору. В Python есть два типа множеств — set и frozenset. Тип frozenset является неизменяемым, поэтому для него недоступны методы для типа set, которые изменяют объект. В остальном они идентичны. Только хешируемые объекты могут быть добавлены в множество. Все встроенные неизменяемые типы являются хешируемыми. #### Множества Множество типа set есть неупорядоченная коллекция из нуля или более ссылок на хешируемые объекты. Множества изменяемы, так что в них можно добавлять и удалять элементы. Будучи неупорядоченными, понятие индекса или позиции для них не имеет смысла. Для создания множества используется либо вызов set(), который без аргументов создает пустое множество. С аргументом типа set создается поверхностная копия множества, а с другим аргументом выполняется попытка приведения к множеству. Данная функция не принимает более одного аргумента. Множество может быть также создано путем заключения элементов в фигурные скобки, но стоит учитывать, что пустое множество не может быть создано таким образом, так как {} создаст пустой словарь. Множество всегда хранит уникальные элементы: ```python set('hello world') == {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'} ``` True Множества поддерживают функцию len(), которое возвращает число элементов множества. Для типа set доступны следующие методы: | Метод | Описание | | ----- | :------- | | s.add(x) | Добавляет элемент x во множество s, если такого там нет | | s.clear() | Удаляет все элементы множества s | | s.copy() | Создает неглубокую копию множества s | | s.difference(t) | Возвращает новое множество, которое содержит все элементы множества s, которые не входят во множество t. Эквивалент s - t | | s.differenct_update(t) | Удаляет все элементы t из множества s. Эквивалент s -= t | | s.discard(x) | Удаляет элемент x из множества s | | s.intersection(t) | Возвращает новое множество, которое содержит элементы множества s, которые в то же время являются элементами множества t. Эквивалент s & t | | s.intersection_update(t) | Удаляет из множества s все элементы, которые не входят в t. Эквивалент s &= t | | s.isdisjoint(t) | Возвращает True, если у множеств s и t нет общих элементов | | s.issubset(t) | Возвращает True, если s есть подмножество (несобственное) t. Эквивалент s <= t | | s.issuperset(t) | Возвращает True, если t есть подмножество (несобственное) s. Эквивалент t <= s | | s.pop() | Удаляет случайный элемент s и возвращает его. Возбуждает исключение KeyError, если множесво пустое | | s.remove(x) | Удаляет элемент x из множества s. Возбуждает исключение KeyError, если x не входит в s | | s.symmetric_difference(t) | Возвращает новое множество, которое содержит элементы, которые либо содержатся в s и не содержатся в t, либо содержатся в t и не содержатся в s. Эквивалент s ^ t | | s.symmetric_difference_update(t) | Записывает в s симметрическую разность s и t. Эквивалент s ^= t | | s.union(t) | Возвращает новое множество, которое содержит все элементы множеств s и t | | s.update(t) | Добавляет все элементы множества t во множество s | Последние два метода эквивалентны s | t и s |= t соответственно. #### Генераторы множеств В целом то же, что и для списков, но сам генератор обозначается фигурными скобками: ```python print({x for x in range(10)}) print({2*x for x in range(10)}) print({2*x for x in range(10) if x % 3 == 0}) ``` {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 2, 4, 6, 8, 10, 12, 14, 16, 18} {0, 18, 12, 6} ### Раздел 3. Типы-отображения Типы отображения — наборы пар "ключ - значение". Единственным встроенным типом-отображением является dictionary, который отображает хешируемые объекты на ссылки на другие объекты. #### Словари Тип dict (словарь) это упорядоченная коллекция пар "ключ - значение". Раньше dict был неупорядоченным типом, поэтому во избежания ошибок при исполнении кода более старыми интерпретаторами, советую вам придерживаться отношения к словорям как к неупорядоченным последовательностям. Словари поддерживают оператор вхождения in, функцию len(), и доступ как к итератору. Среди операций сравнения поддерживаются только == и !=. Как и обычно, тип dict может быть вызван как функция. Если ей не переданы аргументы, то создается пустой словарь. Если передан аргумент с типом-отображением, то создается словарь на основе этого аргумента. Например, если аргумент типа dict, то будет создана его поверхностная копия. Можно также передать аргумент-последовательность, каждый элемент в котором — последовательность из двух элементов: ключа и значения. Также пустой словарь можно создать при помощи конструкции {}, а словарь с элементами — перечислив пары ключ - значение в фигурных скобках через запятую, отделяя ключи от значений двоеточием: {ключ1 : значение1, ключ2 : значение2} К тому же доступны генераторы словарей. Мы можем доступаться к элементам словаря, используя ключ: d[ключ] вернет значение элемента с ключем *ключ*. Для добавления новых элементов используется оператор присваивания: d[новый\_ключ] = новое\_значение. Для типа dict доступны следующие методы: | Метод | Описание | | ----- | :------- | | d.clear() | Удаляет все элементы словаря d | | d.copy() | Возвращает поверхностную копию словаря d | | d.fromkeys(s, v) | Возвращает словарь, где ключами являются элементы последователности s, а значения равны v или None, если v не указано | | d.get(k) | Возвращает значение, которое соответствует ключу k или None, если такого нет | | d.get(k, v) | Возвращает значение, которое соответствует ключу k или v, если такого нет | | d.items() | Возвращает view (read-only итератор), содержащий пары (ключ, значение) словаря d | | d.keys() | Возвращает view, содержащий ключи словаря d | | d.pop(k) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возбуждается исключение KeyError | | d.pop(k, v) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возвращается значение v | | d.popitem() | Удаляет из словаря d и возвращает некоторую пару (ключ, значение). Возбуждает исключение KeyError, если d пуст | | d.setdefault(k, v) | ТТо же, что и d.get(k), но если такого элемента нет, то создается новый со значением None или v, если указано | | d.values() | Возвращает view всех значений словаря d | #### Генераторы словарей Генераторы словарей имеют две уже знакомые нам формы: [выражение\_ключа : выражение\_значения for ключ, значение in итератор] [выражение\_ключа : выражение\_значения for ключ, значение in итератор if условие] ### Раздел 4. Итераторы. Функции и операци для работы с итераторами Тип данных называется итерируемым, если он может возвращать свои элементы по одному. Технически, любой объект, реализующий метод \_\_iter\_\_() является итерируемым и может предоставить итератор. Итератор — объект, реализуюзий метод \_\_next()\_\_, возвращающий следующее значение и возбуждающий исключение StopIteration, если элементов больше нет. Для итерируемых объектов доступны следующие функции и операторы: | Функция или оператор | Описание | | -------------------- | :------- | | s + t | Возвращает конкатенацию последовательностей s и t | | s * n | Возвращает последовательность, которая является результатом повторения последовательности s n раз | | x in t | Проверяет, есть ли в последовательности i элемент x | | all(i) | Возвращает True, если все элементы i при приведении к типу bool имеют значение True | | any(i) | Возвращает True, если один из элементов i при приведении к типу bool имеет значение True | | enumerate(i, start) | Возвращает последовательность элементов (индекс, значение) для итератора i, начиная с индекса 0 или start, если данный аргумент задан | | len(x) | Возвращает число элементов в x | | max(i, key) | Возвращает максимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key максимален | | min(i, key) | Возвращает минимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key минимален | | range(start, stop, step) | Возвращает итератор, содержащий целые числа от start до stop с шагом step. Аргумент start включен в последовательность, а stop — нет | | reversed(i) | Возвращает итератор, в котором элементы идут в обратном порядке | | sorted(i, key, reverse) | Возвращает отсортированный по неубыванию итератор. Если задан аргумент key, то сортировка идет по значением key(x) для каждого элемента x. Если reversre указан как True, то сортировка идет по невозрастанию | | sum(i, start) | Возвращает сумму элементов в итераторе i плюс start. Если start не задан, то принимается равным 0 | | zip(i1, ..., iN) | Возвращает итератор из кортежей, состоящий из элементов i1, ..., iN. Количество кортежей определяется минимальной длиной i1, ..., iN | ### Раздел 5. Копирование коллекций При присваивании объектов будет выполнено присваивание по ссылке: ```python x = ['Nightwing', 'Robin', 'Catwoman'] y = x x[0] = 'Alfred' print(y) ``` ['Alfred', 'Robin', 'Catwoman'] Для создания поверхностной копии можно использовать оператор среза: ```python x = ['Nightwing', 'Robin', 'Catwoman'] y = x[:] x[0] = 'Alfred' print(y) ``` ['Nightwing', 'Robin', 'Catwoman'] Но поверхностная копия имеет ограничения, связанные с тем, что при ее создании создаются копии лишь самих элементов. Если они в свою очередь являются ссылками, то мы снова столкнемся с проблемой: ```python dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']] villains = dc_villains[:] dc_villains[1][1] = 'Pinguin' print(villains) ``` [['Lex Luther', 'General Zod'], ['Joker', 'Pinguin']] Чтобы избежать этого, необходимо использовать функцию deepcopy() модуля copy, которая создает глубокую копию объекта: ```python from copy import deepcopy dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']] villains = deepcopy(dc_villains) dc_villains[1][1] = 'Pinguin' print(villains) ``` [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']] ### Раздел 6. Практика: vedis и matplotlib Есть достаточно интересный модуль vedis, который предоставляет доступ к key-value store типа Redis на диске, но при этом API этого доступа совпадает с доступом к словарю. Решим с использованием модуля vedis [задачу о гипотезе Колатца](https://lambda-it.ru/post/26) и сделаем визуализацию при помощи matplotlib. Для начала напишем функцию, которая из текущего числа получает следующее в последовательности: ```python def next_kolatz(x): if x & 1: return 3*x + 1 return x >> 1 ``` Если число x является нечетным (последний разряд в двоичной системе равен единице), то мы возвращаем 3x + 1. В противном случае, мы делим число на 2 (соответствует сдвигу вправо на один разряд в двоичной системе счисления). Теперь импортируем модуль vedis. При помощи него мы будем хранить длину цепочки для чисел, которые мы уже встречали. Также напишем функцию, которая вычисляет и возвращает длину цепочки для заданного числа x, записывает ее в хранилище, а также записывает в хранилище длины цепочек для всех промежуточных чисел: ```python def get_kolatz_length(x, storage): cur = x try: return int(storage.get(cur)) except KeyError as e: pass stack = [cur] while True: cur = next_kolatz(cur) try: length = int(storage.get(cur)) break except KeyError as e: pass stack.append(cur) for item in stack[::-1]: length += 1 storage.set(item, length) return int(storage.get(x)) ``` Теперь напишем функцию, которая возвращает число, меньшее заданного, для которого данная цепочка максимальна и длину цепочки: ```python def get_max_kolatz_length(n, storage): num = max(range(1, n+1), key=lambda x: get_kolatz_length(x, storage)) return num, get_kolatz_length(num, storage) ``` Теперь остается только инициализировать хранилище и испытать наши функции: ```python from vedis import Vedis db = Vedis('storage.db') db.set(1, 1) print(get_max_kolatz_length(100000, db)) db.close() ``` (77031, 351) True Теперь построим обычный scatter plot при помощи matplotlib: ```python import matplotlib.pyplot as plt db = Vedis('storage.db') X = range(1, 101) Y = [get_kolatz_length(x, db) for x in X] db.close() plt.scatter(X, Y) plt.title('Визуализация длин цепочек Колатца') plt.xlabel('Число') plt.ylabel('Длина цепочки') plt.show() ```  ### Раздел 7. Домашнее задание Решить задачи после третьей главы Саммерфилда Загуглить гипотезу Гольдбаха Загуглить динамическое программирование Написать функцию, которая принимает в качестве аргумента множество и функцию и при помощи matplotlib изображает график заданной функции на множестве значений. Добавьте несколько дополнительных аргументов: например, возможность сделать одну из осей логарифмической, выбрать разные цветовые схемы, разные типы графиков и т.д. Решить задачу, которая будет опубликована в субботу