## **Тема 4**<br>Складені та динамічні структури даних
(c) Яценко Р.М., 2020-2021
[ХНЕУ ім. С. Кузнеця](https://www.hneu.edu.ua/)
<!-- https://habr.com/ru/post/319164/ -->
---
## 1. Основные понятия
----
### Определение
Коллекция
: программный объект (переменная-контейнер), хранящая набор значений одного или различных типов, позволяющий обращаться к этим значениям, а также применять специальные функции и методы, зависящие от типа коллекции
----
### Классификация

----
### Свойства
Индексированность
: каждый элемент коллекции имеет свой порядковый номер — индекс. Это позволяет обращаться к элементу индексу, выполнять срезы
Уникальность
: каждый элемент коллекции может встречаться в ней только один раз
Изменяемость коллекции
: позволяет добавлять в коллекцию новых членов или удалять их
----
### Сравнение свойств

----
### Примечание для словаря (dict)
- сам словарь изменяем — можно добавлять/удалять новые пары `ключ: значение`
- значения элементов словаря — изменяемые и не уникальные
- ключи — не изменяемые и уникальные, поэтому, например, мы не можем сделать ключом словаря список, но можем кортеж. Из уникальности ключей, так же следует уникальность элементов словаря — пар `ключ: значение`
---
## 2. Общие подходы к работе с любой коллекцией
----
### Печать элементов коллекции
``` python=
# Зададим исходные список и словарь
my_list = ['a', 'b', 'c', 'd', 'e', 'f']
my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
print(my_list)
# ['a', 'b', 'c', 'd', 'e', 'f']
print(my_dict)
# {'a': 1, 'c': 3, 'e': 5, 'f': 6, 'b': 2, 'd': 4}
# Не забываем, что порядок элементов
# в неиндексированных коллекциях не сохраняется
```
----
### Подсчёт количества элементов коллекции
``` python=
print(len(my_list)) # 6
print(len(my_dict))
# 6 - для словаря пара ключ-значение считаются одним элементом.
print(len('ab c')) # 4 - для строки элементом является 1 символ
```
----
### Проверка принадлежности элемента коллекции
- `x in s` — вернет `True`, если элемент входит в коллекцию `s` и `False` — если не входит
- Есть и вариант проверки не принадлежности: `x not in s`, где по сути просто добавляется отрицание перед булевым значением предыдущего выражения
----
### Для списка
``` python=
my_list = ['a', 'b', 'c', 'd', 'e', 'f']
print('a' in my_list) # True
print('q' in my_list) # False
print('a' not in my_list) # False
print('q' not in my_list) # True
```
----
### Для словаря и строки
``` python=
my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
print('a' in my_dict)
# True - без указания метода поиск по ключам
print('a' in my_dict.keys())
# True - аналогично примеру выше
print('a' in my_dict.values())
# False - так как 'а' — ключ, не значение
print(1 in my_dict.values())
# True
# Можно ли проверять пары? Можно!
print(('a',1) in my_dict.items()) # True
print(('a',2) in my_dict.items()) # False
# Для строки можно искать не только один символ, но и подстроку:
print('ab' in 'abc') # True
```
----
### Обход всех элементов коллекции в цикле for in
В данном случае, в цикле будут последовательно перебираться элементы коллекции, пока не будут перебраны все из них:
``` python=
for elm in my_list:
print(elm)
```
----
### Для словаря
``` python=
# При таком обходе словаря, перебираются только ключи
# равносильно for elm in my_dict.keys()
for elm in my_dict:
print(elm)
# При желании можно пройти только по значениям
for elm in my_dict.values():
print(elm)
# Но чаще всего нужны пары ключ(key) — значение (value)
for key, value in my_dict.items():
# Проход по .items() возвращает кортеж (ключ, значение),
# который присваивается кортежу переменных key, value
print(key, value)
```
----
### Возможная ошибка
*Не меняйте количество элементов коллекции в теле цикла в этой же коллекции!*
Чтобы этого избежать подобных побочных эффектов, можно, например, перебирать копию коллекции:
``` python=
for elm in list(my_list):
# Теперь можно удалять и добавлять элементы в my_list,
# так как цикл идет по его копии
```
----
### Функции min(), max(), sum()
- Функции `min()`, `max()` -- поиск минимального и максимального элемента соответственно -- работают не только для числовых, но и для строковых значений
- `sum()` -- суммирование всех элементов, если они все числовые
``` python=
print(min(my_list)) # a
print(sum(my_dict.values())) # 21
```
---
## 3. Индексирование
----
### Индексированные коллекции
*Индексированные коллекции* (их еще называют *последовательности* -- sequences):
- список (list)
- кортеж (tuple)
- строка (string)
Каждый элемент имеет свой индекс от 0 (то есть первый по счёту элемент имеет индекс не 1, а 0) до индекса на единицу меньшего длины коллекции (`len(mycollection)-1`)
----
### Получение значения по индексу
Для всех индексированных коллекций можно получить значение элемента по его индексу в квадратных скобках. Причем, можно задавать отрицательный индекс, это значит, что будем находить элемент с конца считая обратном порядке
При задании отрицательного индекса, последний элемент имеет индекс -1, предпоследний -2 и так далее до первого элемента индекс которого равен значению длины коллекции с отрицательным знаком, то есть `-len(mycollection)`
----
### Таблица индексов
| | | | | | |
|-------- |:-----:|:-----:|:-----:|:-----:|:-----:|
|Строка S | H | e | l | l | o |
|Индекс | S[0] | S[1] | S[2] | S[3] | S[4] |
|Индекс | S[-5] | S[-4] | S[-3] | S[-2] | S[-1] |
----
### Вложенные списки
Коллекции могут иметь несколько уровней вложенности, как список списков в примере ниже. Для перехода на уровень глубже ставится вторая пара квадратных скобок и так далее:
``` python=
my_2lvl_list = [[1, 2, 3], ['a', 'b', 'c']]
print(my_2lvl_list[0])
# [1, 2, 3] - первый элемент — первый вложенный список
print(my_2lvl_list[0][0])
# 1 — первый элемент первого вложенного списка
print(my_2lvl_list[1][-1])
# с — последний элемент второго вложенного списка
```
----
### Обращение к элементу списка по индексу
Поскольку кортежи и строки неизменяемые коллекции, то по индексу мы можем только брать элементы, но не менять их:
``` python=
my_tuple = (1, 2, 3, 4, 5)
print(my_tuple[0]) # 1
my_tuple[0] = 100
# TypeError: 'tuple' object does not support item assignment
```
----
### Изменение элемента списка по индексу
Для списка, если взятие элемента по индексу располагается в левой части выражения, а далее идёт оператор присваивания `=`, то мы задаём новое значение элементу с этим индексом:
``` python=
my_list = [1, 2, 3, [4, 5]]
my_list[0] = 10
my_list[-1][0] = 40
print(my_list) # [10, 2, 3, [40, 5]]
```
----
Для такого присвоения, элемент уже должен существовать в списке, нельзя таким образом добавить элемент на несуществующий индекс:
``` python=
my_list = [1, 2, 3, 4, 5]
my_list[5] = 6 # IndexError: list assignment index out of range
```
---
## 4. Срезы
----
### Синтаксис среза
Очень часто, надо получить не один какой-то элемент, а некоторый их набор ограниченный определенными простыми правилами удобнее использовать так называемый *срез* (slice, slicing)
Следует помнить, что взяв элемент по индексу или срезом (slice) мы не как не меняем исходную коллекцию, мы просто скопировали ее часть для дальнейшего использования. Поскольку сама коллекция не меняется -- это применимо ко всем последовательностям
----
Синтаксис среза похож на таковой для индексации, но в квадратных скобках вместо одного значения указывается 2-3 через двоеточие:
``` python=
my_collection[start:stop:step] # старт, стоп и шаг
```
----
### Особенности среза
- Отрицательные значения старта и стопа означают, что считать надо не с начала, а с конца коллекции
- Отрицательное значение шага -- перебор ведём в обратном порядке справа налево
- Если не указан старт `[:stop:step]` -- начинаем с самого края коллекции, то есть с первого элемента (включая его), если шаг положительный или с последнего (включая его), если шаг отрицательный
----
- Если не указан стоп `[start::step]` -- идем до самого края коллекции, то есть до последнего элемента (включая его), если шаг положительный или до первого элемента (включая его), если шаг отрицательный
- `step = 1`, то есть последовательный перебор слева направо указывать не обязательно — это значение шага по умолчанию. В таком случае достаточно указать `[start:stop]`
- Можно сделать даже так `[:]` -- это значит взять коллекцию целиком
----
**ВАЖНО**: При срезе, первый индекс входит в выборку, а второй нет:
```
[ <первый включаемый> : <первый НЕ включаемый> : <шаг> ]
```
----
### Примеры срезов в виде таблицы

----
### Именованные срезы
Чтобы избавится от «магических констант», особенно в случае, когда один и тот же срез надо применять многократно, можно задать константы с именованными срезами с пользованием специальной функции `slice()`
``` python=
person = ('Alex', 'Smith', "May", 10, 1980)
NAME, BIRTHDAY = slice(None, 2), slice(2, None)
# задаем константам именованные срезы
# данные константы в квадратных скобках
# заменятся соответствующими срезами
print(person[NAME]) # ('Alex', 'Smith')
print(person[BIRTHDAY]) # ('May', 10, 1980)
my_list = [1, 2, 3, 4, 5, 6, 7]
EVEN = slice(1, None, 2)
print(my_list[EVEN]) # [2, 4, 6]
```
----
### Изменение списка срезом
С помощью среза можно не только получать копию коллекции, но в случае списка можно также менять значения элементов, удалять и добавлять новые:
- Даже если хотим добавить один элемент, необходимо передавать итерируемый объект, иначе будет ошибка `TypeError: can only assign an iterable`
``` python=
my_list = [1, 2, 3, 4, 5]
# my_list[1:2] = 20 # TypeError: can only assign an iterable
my_list[1:2] = [20] # Вот теперь все работает
print(my_list) # [1, 20, 3, 4, 5]
```
- Для вставки одиночных элементов лучше использовать методы списка `.append()` и `.insert()`
----
- Можно менять части последовательности -- это применение выглядит наиболее интересным, так как решает задачу просто и наглядно:
``` python=
my_list = [1, 2, 3, 4, 5]
my_list[1:3] = [20, 30]
print(my_list) # [1, 20, 30, 4, 5]
my_list[1:3] = [0] # нет проблем заменить два элемента на один
print(my_list) # [1, 0, 4, 5]
my_list[2:] = [40, 50, 60] # или два элемента на три
print(my_list) # [1, 0, 40, 50, 60]
```
- Можно просто удалить часть последовательности:
``` python=
my_list = [1, 2, 3, 4, 5]
my_list[:2] = [] # или del my_list[:2]
print(my_list) # [3, 4, 5]
```
----
### Выход за границы индекса
- Обращение к несуществующему индексу коллекции вызывает ошибку:
``` python=
my_list = [1, 2, 3, 4, 5]
print(my_list[-10])
# IndexError: list index out of range
print(my_list[10])
# IndexError: list index out of range
```
- А в случае выхода границ среза за границы коллекции никакой ошибки не происходит:
``` python=
my_list = [1, 2, 3, 4, 5]
print(my_list[0:10])
# [1, 2, 3, 4, 5] — отработали в пределах коллекции
print(my_list[10:100]) # [] — вернули пустую коллекцию
print(my_list[10:11]) # [] - без ошибки
```
---
## 5. Общие методы для коллекций
----
### Таблица методов

----
### Метод count
`.count()` — метод подсчета определенных элементов для неуникальных коллекций (строка, список, кортеж), возвращает сколько раз элемент встречается в коллекции
``` python=
my_list = [1, 2, 2, 2, 2, 3]
print(my_list.count(2))
# 4 экземпляра элемента равного 2
print(my_list.count(5))
# 0 - то есть такого элемента в коллекции нет
```
----
### Метод index
`.index()` — возвращает минимальный индекс переданного элемента для индексированных коллекций (строка, список, кортеж)
``` python=
my_list = [1, 2, 2, 2, 2, 3]
print(my_list.index(2))
# первый элемент равный 2 находится по индексу 1
# (индексация с нуля!)
print(my_list.index(5))
# ValueError: 5 is not in list
# отсутствующий элемент выдаст ошибку!
```
----
### Метод copy
`.copy()` — метод возвращает неглубокую (не рекурсивную) копию коллекции (список, словарь, оба типа множества)
``` python=
my_set = {1, 2, 3}
my_set_2 = my_set.copy()
print(my_set_2 == my_set)
# True - коллекции равны - содержат одинаковые значения
print(my_set_2 is my_set)
# False - коллекции не идентичны, это разные объекты с разными id
```
----
### Метод clear
`.clear()` — метод изменяемых коллекций (список, словарь, множество), удаляющий из коллекции все элементы и превращающий её в пустую коллекцию
``` python=
my_set = {1, 2, 3}
print(my_set) # {1, 2, 3}
my_set.clear()
print(my_set) # set()
```
----
### Особые методы сравнения множеств (set, frozenset)
- `set_a.isdisjoint(set_b)` — истина, если `set_a` и `set_b` не имеют общих элементов
- `set_b.issubset(set_a)` — если все элементы множества `set_b` принадлежат множеству `set_a`, то множество `set_b` является его подмножеством
- `set_a.issuperset(set_b)` — соответственно, если условие выше справедливо, то `set_a` — надмножество
----
### Примеры
``` python=
set_a = {1, 2, 3}
set_b = {2, 1} # порядок элементов не важен!
set_c = {4}
set_d = {1, 2, 3}
print(set_a.isdisjoint(set_c))
# True - нет общих элементов
print(set_b.issubset(set_a))
# True - set_b целиком входит в set_a, set_b - подмножество
print(set_a.issuperset(set_b))
# True - set_b целиком входит в set_a, set_a - надмножество
# При равенстве множеств они одновременно
# и подмножество и надмножество друг для друга
print(set_a.issuperset(set_d)) # True
print(set_a.issubset(set_d)) # True
```
---
## 6. Добавление и удаление элементов изменяемых коллекций
----
### Удаление

----
### Добавление и обновление

----
### Примеры использования метода .insert(index, element)
``` python=
my_list = [1, 2, 3]
my_list.insert(0, 0) # index = 0 - вставляем в начало
print(my_list) # [0, 1, 2, 3]
my_list.insert(10, 4)
# Индекс выходит за границы списка - просто добавим в конец
print(my_list) # [0, 1, 2, 3, 4]
my_list.insert(-10, -1)
# Индекс выходит за границы в минус - добавим в начало
print(my_list) # [-1, 0, 1, 2, 3, 4]
my_list = [1, 2, 3]
my_list.insert(1, 1.5) # Вставим между 1 и 2 (индексация с нуля!)
print(my_list) # [1, 1.5, 2, 3]
```
----
### Примеры использования оператора del
``` python=
# Работает со списком
my_list = [1, 2, 3, 4, 5, 6, 7]
del my_list[1] # Удаление элемента по индексу
print(my_list) # [1, 3, 4, 5, 6, 7]
del my_list[-3:-1] # Удаление элементов выбранных срезом
print(my_list) # [1, 3, 4, 7]
# del my_list[10]
# IndexError: list assignment index out of range
# Работает со словарем
my_dict = {'a': 1, 'b': 2, 'c': 3}
del my_dict['b']
print(my_dict) # {'a': 1, 'c': 3}
# del my_dict['z']
# KeyError при попытке удалить несуществующий
```
---
## 7. Сортировка элементов коллекции
----
### Функция sorted()
- функция не меняет исходную коллекцию, а возвращает новый список из ее элементов
- не зависимо от типа исходной коллекции, вернётся список (list) ее элементов
- ее можно применять к неизменяемым коллекциям
- можно применять к неиндексированным коллекциям
----
### Дополнительные необязательные аргументы
- `reverse=True` -- сортировка в обратном порядке
- `key=funcname` -- сортировка с помощью специальной функции `funcname`, она может быть как стандартной функцией Python, так и специально написанной вами
----
``` python=
my_list = [2, 5, 1, 7, 3]
my_list_sorted = sorted(my_list)
print(my_list_sorted) # [1, 2, 3, 5, 7]
my_set = {2, 5, 1, 7, 3}
my_set_sorted = sorted(my_set, reverse=True)
print(my_set_sorted) # [7, 5, 3, 2, 1]
```
----
### Пример сортировки списка строк по длине len() каждого элемента
``` python=
my_files = ['somecat.jpg', 'pc.png', 'apple.bmp', 'mydog.gif']
my_files_sorted = sorted(my_files, key=len)
print(my_files_sorted)
# ['pc.png', 'apple.bmp', 'mydog.gif', 'somecat.jpg']
```
----
### Функция reversed()
Применяется для последовательностей и работает по другому:
- возвращает генератор списка, а не сам список
- если нужно получить готовый список, можно воспользоваться срезом `[: :-1]`
- она не сортирует элементы, а возвращает их в обратном порядке, то есть читает с конца списка
- эта функция не применима к неиндексированным коллекциям
----
### Примеры для функции reversed()
``` python=
my_list = [2, 5, 1, 7, 3]
my_list_sorted = reversed(my_list)
print(my_list_sorted)
# <listreverseiterator object at 0x7f8982121450>
print(list(my_list_sorted))
# [3, 7, 1, 5, 2]
print(my_list[::-1])
# [3, 7, 1, 5, 2] - тот же результат с помощью среза
```
----
### Методы списка .sort() и .reverse()
У списка (и только у него) есть особые методы `.sort()` и `.reverse()` которые делают тоже самое, что соответствующие функции `sorted()` и `reversed()`, но при этом:
- меняют сам исходный список, а не генерируют новый
- возвращают `None`, а не новый список
- поддерживают те же дополнительные аргументы
- в них нельзя передавать сам список первым параметром
----
### Прмеры методов .sort() и .reverse()
``` python=
my_list = [2, 5, 1, 7, 3]
my_list.sort()
print(my_list)
# [1, 2, 3, 5, 7]
my_list = my_list.sort()
print(my_list) # None
```
----
### Особенности сортировки словаря
- `sorted(my_dict)` -- идёт перебор только ключей, возвращается сортированный список ключей
- `sorted(my_dict.keys())` -- тот же результат, что в предыдущем примере
- `sorted(my_dict.items())` -- возвращается сортированный список кортежей (ключ, значение), сортированных по ключу
- `sorted(my_dict.values())` -- возвращается сортированный список значений
----
### Примеры сортировки словаря
``` python=
my_dict = {'a': 1, 'c': 3, 'e': 5, 'f': 6, 'b': 2, 'd': 4}
mysorted = sorted(my_dict)
print(mysorted)
# ['a', 'b', 'c', 'd', 'e', 'f']
mysorted = sorted(my_dict.items())
print(mysorted)
# [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)]
mysorted = sorted(my_dict.values())
print(mysorted)
# [1, 2, 3, 4, 5, 6]
```
---
## 8. Объединение последовательностей и словарей без изменения исходных
----
### Объединение строк (string) и кортежей (tuple)
Используется оператор сложения "+"
``` python=
str1 = 'abc'
str2 = 'de'
str3 = str1 + str2
print(str3) # abcde
tuple1 = (1, 2, 3)
tuple2 = (4, 5)
tuple3 = tuple1 + tuple2
print(tuple3) # (1, 2, 3, 4, 5)
```
----
### Объединение списков
Используется оператор сложения "+"
``` python=
a = [1, 2, 3]
b = [4, 5]
c = a + b
print(a, b, c)
# [1, 2, 3] [4, 5] [1, 2, 3, 4, 5]
c = a + [b]
print(a, b, c)
# [1, 2, 3] [4, 5] [1, 2, 3, [4, 5]]
```
----
### Объединение словарей (dict)
Сложить два словаря чтобы получить третий оператором `+` Python не позволяет «TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
Это можно сделать комбинируя методы `.copy()` и `.update()`:
``` python=
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
dict3 = dict1.copy()
dict3.update(dict2)
print(dict3)
# {'a': 1, 'c': 3, 'b': 2, 'd': 4}
```
---
## 9. Объединение множеств без изменения исходных
----
### Операции над множествами

----
### Объединение (union)
``` python=
# Зададим исходно два множества
a = {'a', 'b'}
b = {'b', 'c'}
c = a.union(b)
# c = b.union(a) даст такой же результат
# c = a + b
# Обычное объединение оператором + не работает
# TypeError: unsupported operand type(s) for +: 'set' and 'set'
c = a | b
# Альтернативная форма записи объединения
print(c)
# {'a', 'c', 'b'}
```
----
### Пересечение (intersection)
``` python=
c = a.intersection(b)
# c = b.intersection(a) даст такой же результат
c = a & b
# Альтернативная форма записи пересечения
print(c) # {'b'}
a = {'a', 'b'}
b = {'b', 'c'}
c = {'b', 'd'}
d = a.intersection(b, c)
# Первый вариант записи
d = set.intersection(a, b, c)
# Второй вариант записи (более наглядный)
print(d) # {'b'}
```
----
### Разница (difference)
Результат зависит от того, какое множество из какого вычитаем
``` python=
c = a.difference(b)
# c = a - b другой способ записи дающий тот же результат
print(c) # {'a'}
c = b.difference(a)
# c = b - a другой способ записи дающий тот же результат
print(c) # {'c'}
```
----
### Симметричная разница (symmetric_difference)
Это своего рода операция противоположная пересечению -- выбирает элементы из обеих множеств которые не пересекаются, то есть все кроме совпадающих:
``` python=
c = b.symmetric_difference(a)
# c = a.symmetric_difference(b)
# даст такой же результат
c = b ^ a
# Альтернативная форма записи симметричной разницы
print(c)
# {'a', 'c'}
```
---
## 10. Объединение с изменением исходной коллекции
----
### Список (list)
- Добавляем все элементы второго списка к элементам первого с изменением первого списка:
``` python=
a.extend(b)
# a += b эквивалентно a.extend(b)
print(a, b)
# [1, 2, 3, 4, 5] [4, 5]
```
- Добавляем второй список как один элемент с изменением первого списка:
``` python=
a.append(b)
# a += [b] эквивалентно a.append(b)
print(a, b)
# [1, 2, 3, [4, 5]] [4, 5]
```
----
### Объединение словарей
Для изменения словаря с добавления элементов другого словаря используется метод `.update()`
``` python=
dict1 = {'a': 1, 'b': 2}
dict2 = {'a': 100, 'c': 3, 'd': 4}
dict1.update(dict2)
print(dict1)
# {'a': 100, 'c': 3, 'b': 2, 'd': 4}
```
*Для совпадающих ключей словаря при этом обновляются значения*
----
### Изменяемое множество (set)
Для изменяемого множества (set) кроме обычных операций также возможны их аналоги, но уже с изменением исходного множества -- эти методы заканчиваются на `_update`. Результат зависит от того, какое множество каким обновляем
----
### .difference_update()
``` python=
a = {'a', 'b'}
b = { 'b', 'c'}
a.difference_update(b)
print(a, b)
# {'a'} {'b', 'c'}
a = {'a', 'b'}
b = { 'b', 'c'}
b.difference_update(a)
print(a, b)
# {'a', 'b'} {'c'}
```
----
### .intersection_update()
``` python=
a = {'a', 'b'}
b = { 'b', 'c'}
a.intersection_update(b)
print(a, b)
# {'b'} {'b', 'c'}
a = {'a', 'b'}
b = { 'b', 'c'}
b.intersection_update(a)
print(a, b)
# {'b', 'a'} {'b'}
```
----
### .symmetric_difference_update()
``` python=
a = {'a', 'b'}
b = { 'b', 'c'}
a.symmetric_difference_update(b)
print(a, b)
# {'c', 'a'} {'c', 'b'}
a = {'a', 'b'}
b = { 'b', 'c'}
b.symmetric_difference_update(a)
print(a, b)
# {'a', 'b'} {'c', 'a'}
```
---
## 11. Конвертация одного типа коллекции в другой
----
### Преобразование типов
Один тип коллекции можно конвертировать в другой тип коллекции. Для этого, как правило достаточно передать одну коллекцию в функцию создания другой:
``` python=
my_tuple = ('a', 'b', 'a')
my_list = list(my_tuple)
my_set = set(my_tuple)
# теряем индексы и дубликаты элементов!
my_frozenset = frozenset(my_tuple)
# теряем индексы и дубликаты элементов!
print(my_list, my_set, my_frozenset)
# ['a', 'b', 'a'] {'a', 'b'} frozenset({'a', 'b'})
```
----
### Возможна потеря данных
- При преобразовании в множество теряются дублирующие элементы, так как множество содержит только уникальные элементы!
- При конвертации индексированной коллекции в неиндексированную теряется информация о порядке элементов!
- После конвертации в неизменяемый тип, мы больше не сможем менять элементы коллекции!
----
### Конвертация в словарь
Способом выше не получится создать словарь, так как он состоит из пар `ключ: значение`
Это ограничение можно обойти, создав словарь комбинируя ключи со значениями с использованием `zip()`
``` python=
my_keys = ('a', 'b', 'c')
my_values = [1, 2]
# Если количество элементов разное
# будет отработано пока хватает на пары - лишние отброшены
my_dict = dict(zip(my_keys, my_values))
print(my_dict) # {'a': 1, 'b': 2}
```
----
### Создаем строку из другой коллекции
``` python=
my_tuple = ('a', 'b', 'c')
my_str = ''.join(my_tuple)
print(my_str) # abc
```
----
### Возможная ошибка
Если Ваша коллекция содержит изменяемые элементы (например, список списков), то ее нельзя конвертировать в неизменяемую коллекцию, так как ее элементы могут быть только не изменяемыми!
``` python=
my_list = [1, [2, 3], 4]
my_set = set(my_list)
# TypeError: unhashable type: 'list'
```
---
## Дякую за увагу!

(c) Яценко Р.М., 2020-2021
[ХНЕУ ім. С. Кузнеця](https://www.hneu.edu.ua/)
{"metaMigratedAt":"2023-06-15T05:21:03.020Z","metaMigratedFrom":"YAML","title":"Тема 4. Складені та динамічні структури даних","breaks":false,"slideOptions":"{\"allottedMinutes\":85,\"theme\":\"beige\",\"transition\":\"slide\",\"slideNumber\":\"c\",\"spotlight\":{\"enabled\":true}}","contributors":"[{\"id\":\"93a8c43f-1b5b-4461-9101-89b183ccbc1c\",\"add\":26795,\"del\":1734}]"}