Содержание

Списки


Определения

Список — упорядоченная изменяемая коллекция произвольных объектов.

Литералы списков:

 a = [1, 2, 3, 4]
 b = [1, 'string', [1, 2]]

Нумерация в списках начинается как и везде с 0.

Индексация списка работает также как и в строках.

Срезы работают со списками точно так же как и со строками.


Базовые операции над списками

  • Получение элемента списка по индексу:lst[2] — получение третьего элемента списка.
  • Присвоение элемента списка по индексу:lst[1] = 1 — присвоение второму элементу.
  • Добавление элемента в конец списка:lst.append('element') — добавление элемента в конец списка.
  • Расширение списка элементами другого итерируемого объекта:list.extend(iterable) — добавляет в конец списка lst элементы итерируемого объекта.
  • Сложение списков:result = lst1 + lst2 — список result будет содержать все элементы первого, а после второго списков.
  • Умножение списка на число:lst*3 — возвращает новый список, состоящий из элементов исходного списка, идущих три раза подряд.

Дополнительные методы для работы со списками

  • Оператор in — проверка вхождения элемента в список.
  • list.insert(i, x) — вставляет на i-ый элемент значение x.
  • list.remove(x) — удаляет первый элемент в списке, имеющий значение x. ValueError, если такого элемента не существует.
  • list.pop([i]) — удаляет i-ый элемент и возвращает его. Если индекс не указан, удаляется последний элемент.
  • list.index(x, [start [, end]]) — возвращает положение первого элемента со значением x (при этом поиск ведется от start до end).
  • list.count(x) — возвращает количество элементов со значением x.
  • list.sort([key=функция]) — сортирует список на основе функции.
  • list.reverse() — разворачивает список.
  • list.copy() — поверхностная копия списка.
  • list.clear() — очищает список.

Объектная модель Python

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

Объектную модель Python можно рассматривать как два пространства — объектов и имен.

Они подчиняются определенным правилам:

  • На объект может ссылаться любое количество имен и объектов.
  • Создание имени происходит при присвоении значения (assignment).
  • От каждого имени к объекту может идти ровно одна ссылка.
  • Copy и ее аналоги осуществляют копирование только самого объекта.
  • Deepcopy производит рекурсивное копирование объектов и всех объектов, на который ссылается данный.
  • del удаляет имя, и ссылку которая идет от имени на объект. Если del применяется к безымянному объекту (ссылка из спика), то он удаляет только ссылку. Удалением непосредственно объектов занимаются другие механизмы.

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


Code Snippets


Базовые Операции над списками


# Создание объекта списка.
lst = [1, 2, 3]
print(lst)
[1, 2, 3]

# Добавление элемента в конец списка.
lst.append('tr')
print(lst)
[1, 2, 3, 'tr']

# Расширение списка элементами другого списка.
lst.extend([1, 2, 3])
print(lst)
[1, 2, 3, 'tr', 1, 2, 3, [1, 2, 3], 1, 2, 3]

# Подходит любой итерируемый объект!
lst = [1,2,3]
other_iterable = 'asdf'
lst.extend(other_iterable)
print(lst)
[1, 2, 3, 'a', 's', 'd', 'f']

# Сложение списков.
lst1 = [1, 2, 3]
lst2 = [4, 5, 6]

result = lst1 + lst2
print(result)
[1, 2, 3, 4, 5, 6]

# Неявного приведения типов при сложении не происходит, это вызывает ошибку. 
lst = [1, 2, 3]
str_ = 'string'

result = lst1 + str_
print(result)

TypeError                                 Traceback (most recent call last)
<ipython-input-16-682c8006c970> in <module>()
      3 str_ = 'string'
      4 
----> 5 result = lst1 + str_
      6 print(result)

TypeError: can only concatenate list (not "str") to list

# Обратите внимание, что оператор `+=` работает иначе. 
# Он готов принимать любые итерируемые объекты.
# Фактически, он работает как extend выше.
lst = [1, 2, 3]
str_ = 'string'
lst += str_
print(lst)

# Умножение списка на целое число. 
lst1 = [1, 2, 3]
result = lst1*3
print(result)
[1, 2, 3, 1, 2, 3, 1, 2, 3]

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


# Изменяемые списки.
lst1 = lst2 = [1, 2, 3]
print('lst1 =', lst1, ', lst2 =',  lst2)
lst1[0] = []
print('lst1 =', lst1, ', lst2 =',  lst2)

# Незименяемые строки.
str1 = str2 = '123'
print('str1 =', str1, ', str2 =', str2)
str1 = str1.replace('1', '2')
print('str1 =', str1, ', str2 =', str2)
lst1 = [1, 2, 3] , lst2 = [1, 2, 3]
lst1 = [[], 2, 3] , lst2 = [[], 2, 3]

str1 = 123 , str2 = 123
str1 = 223 , str2 = 123

Copy, deepcopy


# 3 способа скопировать список
import copy
copy.copy(obj)

lst.copy()

lst[:]

# Copy осуществляет копирование переданного объекта. 
import copy
lst1 = [[], [1, 2], 3, 'str']
lst2 = copy.copy(lst1)
print('lst1 =', lst1, '\nlst2=',  lst2)
lst1 = [[], [1, 2], 3] 
lst2= [[], [1, 2], 3]

lst[0][0]

a = (1, )
b = (1, )
A = 123456788765434567898765434567876545678987654567898765456789876545678765678987656789876567898765678987678909876
if a == b:
  print("Equal", True)
else:
  print("Equal", False)

if a is b:
  print("Reference", True)
else:
  print("Reference", False)
Equal True
Reference False

# При копировании очевидно, что изменение объекта не будет влиять на его копию.
lst1[0] = 'other'
print('lst1 =', lst1, '\nlst2=',  lst2)
lst1 = ['other', [1, 2], 3] 
lst2= [[], [1, 2], 3]

# При копировании осуществляется копирование только самого списка, и объекты,
# являющиеся содержимым списка все еще будут изменяться для обоих списков.

# Обратите внимание, что мы можем точно также использовать slice или метод 
# lst.copy().

lst1 = [[], [1, 2], 3]
lst2 = copy.copy(lst1)
lst1[1][0] = 'internal'
print('lst1 =', lst1, '\nlst2 =',  lst2)
lst1 = [[], ['internal', 2], 3] 
lst2 = [[], ['internal', 2], 3]

# Функция deepcopy будет копировать не только исходный объект, но и все объекты
# входящие в него непосредственно или в его объекты.
import copy

lst1 = [1, [1, 2], 3]
lst2 = copy.deepcopy(lst1)
print('lst1 =', lst1, '\nlst2 =',  lst2)
print()

lst1[0] = 'other element'
print('lst1 =', lst1, '\nlst2 =',  lst2)
print()

lst1[1][0] = 'internal element'
print('lst1 =', lst1, '\nlst2 =',  lst2)
lst1 = [1, [1, 2], 3] 
lst2 = [1, [1, 2], 3]

lst1 = ['other element', [1, 2], 3] 
lst2 = [1, [1, 2], 3]

lst1 = ['other element', ['internal element', 2], 3] 
lst2 = [1, [1, 2], 3]

Некоторые дополнительные функции для работы со списками


# Подсчет количества вхождений элемента в список.
lst1 = [1, 2, 3, 2]
print(lst1.count(2))
2

# Очистка списка.
lst1 = [1, 2, 3, 2]
lst1.clear()
print(lst1)
[]

# Поиск первого вхождения элемента в список.
lst1 = [1, 2, 3, 2]
print(lst1.index(2))
1

# "Переворачивание" списка - изменения порядка следования объектов в обратном
# направлении.
lst1 = [1, 2, 3, 2]
lst1.reverse()
print(lst1)
[2, 3, 2, 1]

# Сортировка списка по значениям.
lst1 = ['a', '1', '2', '3', '2', ]
lst1.sort()
print(lst1)

Задачи для закрепления

  • Нaпишите прогрaмму, котoрая принимает на вход спиcок чисел и выводит на экран в oдну строкy значения, котoрые повторяются в нём бoлее одного раза. Выводимые числа не дoлжны повторяться, пoрядок их вывода может быть произвольным.
  • Нaпишите программу, на вход которой подаётся список чисел одной строкой. Программа должна для каждого элемента этого списка вывести сумму двух его cоседей. Для элeментов списка, являющиxся крайними, одним из соседей считается элемент, находящий на противоположном конце этого списка. Например, если на вход подаётся cписок 1 3 5 6 10, то на выход ожидается cписок 13 6 9 15 7. Если на вход пришло только однo число, надо вывести его же. Вывoд должен содержать одну строку с чиcлами новoго списка, разделёнными пробeлом.

lst = [2, 4, 6, 8, 8, 2, 5, 4]

duplicates = []

for element in lst:
  if lst.count(element) > 1 and element not in duplicates:
    duplicates.append(element)

print(*duplicates, sep=', ')
2, 4, 8

lst = [2, 4, 6, 8]
result_lst = []

if len(lst) == 1:
  print(lst[0])
else:
  for i in range(len(lst)):
    if i == len(lst)-1:
      result_lst.append(lst[0] + lst[-2])
    else:
      result_lst.append(lst[i-1] + lst[i+1])

print(*result_lst, sep=', ')
12 8 12 8

lst = [2, 4, 6, 8]
result_lst = []
lst_len = len(lst)

if lst_len == 1:
  print(lst[0])
else:
  for i in range(lst_len):
    result_lst.append(lst[i-1] + lst[(i+1) % lst_len])

print(*result_lst, sep=', ')
12 8 12 8

Кортежи


Определения

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

Литералы кортежей:

 a = (1, 2, 3, 4)
 b = (1, 'string', [1, 2])

Зачем нужны кортежи?

  • Защита как от произвольного, так и непроизвольного изменения.
  • Как неизменяемые объекты, кортежы хешируемые, что позволяет их использовать, например, как ключи словаря.
  • Они занимают немного меньше места.

Базовые операции над кортежами

От большего, в части, которая не касается изменения, работа с кортежами очень похожа на работу со списками!

  • Получение элемента кортежа по индексу:tpl[2] — получение третьего элемента кортежа.
  • Сложение кортежей:result = tpl1 + tpl2 — кортеж result будет содержать все элементы первого, а после второго кортежа. Тут не происходит изменение кортежей, а происходит создание нового!
  • Умножение кортежа на число:tpl*3 — возвращает новый кортеж, состоящий из элементов исходного кортежа, идущих три раза подряд.

Дополнительные методы работы с кортежами

  • Оператор in — проверка вхождения элемента в кортеж.
  • tpl.index(x, [start [, end]]) — возвращает положение первого элемента со значением x (при этом поиск ведется от start до end).
  • tpl.count(x) — возвращает количество элементов со значением x.

Code Snippets


Базовые операции над кортежами


# Создание кортежа.
a = (1+2, 2, 3)
print(a, type(a))
(3, 2, 3) <class 'tuple'>

# Создание кортежа из одного элемента. Обратите внимание на завершающую запятую!
b = (1, )
print(b, type(b))

b = ()
print(b, type(b))
() <class 'tuple'>

# Создание кортежа из списка и наоборот. Происходит явное преобразование типов.
lst1 = [1, [], 3]
b = tuple(lst1)
print(b)
(1, [], 3)

# Проверка вхождения элемента в кортеж.
if 'element' in (1, 2, 'element'):
  print(True)
True

# Сложение кортежей.
tpl1 = (1, 2, 3)
tpl2 = (4, )
print(tpl1 + tpl2)
(1, 2, 3, 4)

# Расширение с помощью += не изменяет кортеж! Создается новый.
tpl = (1, 2)
print("Id кортежа перед сложением: ", id(tpl))
print()

tpl += (3, 4)
print("Кортеж после +=: ", tpl, "\nId кортежа после сложения:", id(tpl))
# Идентификаторы разные, это различные кортежи.
Id кортежа перед сложением:  139895095320656

Кортеж после +=:  (1, 2, 3, 4) 
Id кортежа после сложения: 139895009004816

# Вызов операций, которые явно меняют объект, для кортежа очевидно недопустимы.
tpl = (1, 2)
tpl.append(3)

AttributeError                            Traceback (most recent call last)
<ipython-input-32-ea709ed9a685> in <module>()
      1 # Вызов операций, которые явно меняют объект, для кортежа очевидно недопустимы.
      2 tpl = (1, 2)
----> 3 tpl.append(3)

AttributeError: 'tuple' object has no attribute 'append'

# Итерирование по кортежу.
for i in (1, 2, 3, 4, 5):
  print(i)
1
2
3
4
5

# Метод Index, знакомый нам по спискам.
tpl = (1, 2, 3)
print(tpl.index(3))
2

Неявное применение кортежей

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


# Кортеж автоматически создастся и без скобок, потому что других вариантов
# интерпретации этого кода у интерпретатора нет.
var = 1, 2, 3
print(var, type(var))
(1, 2, 3) <class 'tuple'>

# Это будет также работать и с кортежами из одного элемента.
var = 1,
print(var, type(var))
(1,) <class 'tuple'>

# При указании в цикле for последовательности элементов произойдет тоже самое.
for element in 1, 2, 'rr', [1, 2]:
  print(element)

# Фактически тут происходит итерирование по кортежу.
1
2
rr
[1, 2]

# Присвоение нескольких переменных также происходит с помощью кортежей.
a, b = 1, 23
print(a, b)
1 23

# Код в примере выше аналогичен следующему.
(a, b) = (1, 23)
print(a, b)

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

# Быстро меняем значения переменных местами. Под капотом - опять кортежи.
a, b = 1, 23
print("a = ", a, "b = ", b)
print()

b, a = a, b
print("a = ", a, "b = ", b)

# И даже более сложные примеры. Тут главное - не переусердствовать и
# не потерять читаемость кода. 

a, b = 1, 23
a, b = a + b, a*10
print(a, b)

Задачи для закрепления

  • Создайте кортеж с пятью любыми целыми числами от 0 до 3 включительно.
  • Также создайте второй кортеж числами от -3 до 0.
  • Объедините два кортежа с помощью оператора +, создав тем самым третий кортеж.
  • С помощью метода кортежа count() определите в нем количество нулей. Выведите на экран третий кортеж и количество нулей в нем.

Словари


Определения

Словарь — коллекция, представляющая собой набор пар ключ-значения. Вообще словари в Python представляют собой такую структуру данных как хеш-таблица.

Хеш таблицы сами по себе — неупорядоченные структуры данных, однако в Python, начиная с версии 3.6 сохраняется порядок добавления элементов в словарь. Подробнее об этом в PEP468.

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

Литералы словарей:

 a = {1: 2, 3: 4}
 b = [1: 'string', 'key': [1, 2]]

Базовые операции со словарями

  • Получение значения по ключу:dct[key] — получение знаение, которое соответствует ключу key.
  • Присвоение значения ключу:dct[key] = object — присвоение ключу key элементу.
  • Удаление пары из словаря:del dct[key] — удаляет из словаря пару ключ-значение.

Дополнительные методы работы со словарями

  • fromkeys(iterable[, value]) — создает словарь, с ключами из iterable и значениями value, или None, если value не передается.
  • dict.update(other) — обновляет словарь парами ключ-значение из other, если ключ существует — обновляет их значения.
  • dict.items() — возвращает итерируемый объект, состоящий из кортежей, первый элемент которых ключ, а второй — значение.
  • dict.values() — возвращает итерируемый объект, элементами которого являются значения в словаре.
  • dict.clear() — очищает словарь.
  • dict.copy() — возвращает копию словаря.
  • dict.get(key[, default]) — получает элемент словаря по ключу, если он отсутствует в словаре, возвращает default, а если он не указан — None.
  • dict.pop(key[, default]) — возвращает значение соответствующее ключу key и удаляет пару из словаря.
  • dict.popitem() — возвращает кортеж содержащий ключ и значение (пара выбирается произвольно), удаляя их из словаря.
  • dict.setdefault(key[, default]) — возвращает значение ключа, но в случае его отсутствия, создает ключ с значением default (по умолчанию None).

Code Snippets


Создание словарей


# Создание словаря.
dct = {'a': 1, 2: 3}
print(dct)
{'a': 1, 2: 3}

# Еще несколько способов задания словарей.
dct = dict()
dct1 = {}
print("dct = ", dct, type(dct))
print("dct1 = ", dct1, type(dct1))
dct =  {} <class 'dict'>
dct1 =  {} <class 'dict'>

# Создание словаря с помощью метода fromkeys.
dct = dict.fromkeys(['a', 'b'])
print(dct)
{'a': None, 'b': None}

# Создание словаря из итерируемого объекта, содержащие кортежы пар.
dct = dict([(1,2), (3,4)])
print(dct)
{1: 2, 3: 4}

# Создание пары в словаре. Ключем может являться любой хешируемый объект.
# Создание пары происходит при присвоении ключу, очень похоже на присвоение
# переменных. 
dct = {}
dct[1] = [1, 2, 3, 4]
dct[(1, 2, 3)] = 't'
print(dct)
{1: [1, 2, 3, 4], (1, 2, 3): 't'}

# Кортежи могут быть ключами словаря, но для этого необходимо чтобы все их
# элементы были хешируемыми.
dct = {}
dct[(1,2 , [])] = 't'
print(dct)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-61a852716cba> in <module>
      2 # элементы были хешируемыми.
      3 dct = {}
----> 4 dct[(1,2 , [])] = 't'
      5 print(dct)

TypeError: unhashable type: 'list'

Изменение словарей


# Словари не поддерживают сложение в привычном нам понимании. 
dct1 = {1:2, 3:4}
dct2 = {5:6, 7:8}
dct1 += dct2
print(dct1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-9912d1aefbcb> in <module>
      2 dct1 = {1:2, 3:4}
      3 dct2 = {5:6, 7:8}
----> 4 dct1 += dct2
      5 print(dct1)

TypeError: unsupported operand type(s) for +=: 'dict' and 'dict'

# Вместо этого используется метод update, 
# а начиная с Python 3.9 операторы | и |=.
dct1 = {1:2, 3:4}
dct2 = {5:6, 7:8}
dct1.update(dct2)
print(dct1)
{1: 2, 3: 4, 5: 6, 7: 8}

# В случае совпадающих ключей, значение, соответствующее ключу, обновляется.
dct1 = {1:2, 3:4}
dct2 = {1: 'kawabungo', 2:'my_item'}
dct1.update(dct2)
print(dct1)
{1: 'kawabungo', 3: 4, 2: 'my_item'}

Итерирование по словарям


# Итерирование по словарю по умолчанию - итерация идет по ключам. 
dct = {1: 'kawabungo', 2:'my_item'}
for element in dct:
  print(str(element) + ': ' + str(dct[element]))
1: kawabungo
2: my_item

# С помощью метода items() можно получать кортежы состоящие из ключа и значения.
dct = {1: 'kawabungo', 2:'my_item'}
for element in dct.items():
  print(element)
(1, 'kawabungo')
(2, 'my_item')

# Используя свойства кортежей, их элементы можно сразу присваивать
# соответствующим переменным.
dct = {1: 'kawabungo', 2:'my_item'}
for key, value in dct.items():
  print(key, value)
1 kawabungo
2 my_item

# Для итерирования по значениям используется метод values().
dct = {1: 'kawabungo', 2:'my_item'}
for element in dct.values():
  print(element)
kawabungo
my_item

# Для явного указания что итерирование идет по ключам используется метод keys(),
# однако на практике он используется редко.
dct = {1: 'kawabungo', 2:'my_item'}
for element in dct.keys():
  print(element)
1
2

Получение элемента словаря.


# Получение элемента по ключу.
dct = {(1, ): 'kawabungo', 2:'my_item'}
print(dct[(1, )])
kawabungo

# Проблемы возникают, когда мы пытаемся получить значение по ключу, которого
# в словаре нет.
dct = {1: 'kawabungo', 2:'my_item'}
print(dct['1'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-17-5220d7b620a5> in <module>
      2 # в словаре нет.
      3 dct = {1: 'kawabungo', 2:'my_item'}
----> 4 print(dct['1'])

KeyError: '1'

# Метод get(key) позволяет получить либо значение, соответствующее ключу, либо 
# вернет None.
dct = {1: 'kawabungo', 2:'my_item'}
print(dct.get('1'))
None

# Вторым аргументом методу get можно передать значение по умолчанию, которое
# будет возвращаться в случае если ключ отсутствует в словаре.
dct = {1: 'kawabungo', 2:'my_item'}
print(dct.get('1', "THIS IS DEFAULT ELEMENT"))
THIS IS DEFAULT ELEMENT

# Метод popitem поочередно возвращает кортежи, состоящие из пар ключ-значение.
dct = {1:2, 3:4}
while dct:
  print(dct.popitem())
(3, 4)
(1, 2)

Задачи для закрепления

  1. Посчитать количество каждой буквы в введенной строке. Задача — опорная! Слышим в Python посчитать — вспоминаем словари. Решение должно быть O(n).
  2. Найти самый часто встречающийся элемент списка, состоящего из цифр (вводится строкой с разделением цифр пробелами).
  3. Ввести строку и сделать из нее словарь. Ключ от значения отделен ': ', пары разделены ', '.

def str_to_dict(str_):
  target_dict = {}
  for pair in str_.split(','):
    pair = pair.strip()
    key, value = pair.split(': ')
    target_dict[key] = value
  return target_dict


print(str_to_dict('a:1, b:2, c:3'))
{'a': '1', 'b': '2', 'c': '3'}

lst = '2 2 4 4 5 6'.split()
lst_2 = [int(num) for num in lst]
dct = {}
for num in lst_2:
    dct[num] = dct.get(num, 0) + 1

# Сделаем перевернутый словарь для вывода максимального значения
dct_inv = {value: key for key, value in dct.items()}
print(dct_inv[max(dct_inv.keys())])
4

{'symbol': 'number_of_occurences'}
def chars_count(str_):
  counts_dict = {}
  for char in str_:
    counts_dict[char] = counts_dict.get(char, 0) + 1
  return counts_dict

print(chars_count('abbcdead'))
{'a': 2, 'b': 2, 'c': 1, 'd': 2, 'e': 1}

str_ = 'asdfasdfasdfsdf'
{'symbol': ['symbol', 'symbol', 'symbol']}

counts_dict = {}
for char in str_:
  counts_dict.setdefault(char, []).append(char)

print(counts_dict)
{'a': ['a', 'a', 'a'], 's': ['s', 's', 's', 's'], 'd': ['d', 'd', 'd', 'd'], 'f': ['f', 'f', 'f', 'f']}

Множества


Определения

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

Некоторые свойства множеств:

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

Литералы множеств:

 a = {1, 3}
 b = {'string', 1}

Основные методы работы с множествами

Математические операции над множествами:

  • set.update(other)set |= otherset1 | set2 — объединение множеств.
  • set.intersection_update(other)set &= otherset1 & set2 — пересечение множеств.
  • set.difference_update(other, ...)set -= other set1 - set2 — разность множеств.
  • set.symmetric_difference_update(other)set ^= otherset1 ^ set2 — симметрическая разность множеств. Симметрическая разность — это множество из элементов, встречающихся в одном из множеств, но не встречающиеся в обоих.

Работа с элементами множеств:

  • set.add(elem) — добавляет элемент в множество.
  • set.remove(elem) — удаляет элемент из множества. KeyError, если такого элемента не существует.
  • set.discard(elem) — удаляет элемент, если он находится в множестве.
  • set.pop() — удаляет некоторый элемент из множества. Так как множества не упорядочены, нельзя точно сказать, какой элемент будет удален.
  • set.clear() — очистка множества.

Code Snippets


Базовые операции с множествами


Чтобы изменить содержимое ячейки, дважды нажмите на нее (или выберите «Ввод»)


# Создание множества. 
a = {1, 2, 3}
print(a, type(a))
{1, 2, 3} <class 'set'>

# Создать пустое множество как список или словарь не получится, ввиду того что
# словари используют те же скобки что и множество. 
a = {}
print(a, type(a))
{} <class 'dict'>

# Но получится через конструктор множеств.
b = set()
print(b, type(b))
set() <class 'set'>

# Множества могут содержать только хешируемые (неизменяемые) объекты.
a = {"1", 2, 3, ["t", "y", "f"], (0, 1)}
print(a, type(a))

TypeError                                 Traceback (most recent call last)
<ipython-input-8-4b0968a66307> in <module>()
      1 # Множества могут содержать только хешируемые (неизменяемые) объекты.
----> 2 a = {"1", 2, 3, ["t", "y", "f"], (0, 1)}
      3 print(a, type(a))

TypeError: unhashable type: 'list'

# И совершенно не важно как глубоко во вложенных конструкциях 
# спрячется нехешируемый объект. 
a = {"1", 2, 3, ("t", ["y"], "f"), (0, 1)}
print(a)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-806a1704b896> in <module>()
      1 # И совершенно не важно как глубоко во вложенных конструкциях
      2 # спрячется нехешируемый объект.
----> 3 a = {"1", 2, 3, ("t", ["y"], "f"), (0, 1)}
      4 print(a)

TypeError: unhashable type: 'list'

# Попытка создать множество с повторяющимися элементами.
a = {1, 2, 2, 3}
print(a)
# В множестве останется только одна копия этих элементов.
{1, 2, 3}

# Создание множества из списка.
a = set([1, 2, 2, 3])
print(a)
{1, 2, 3}

# Итерирование по множеству.
for element in {1, (2, 3), 2}:
  print(element)

# Как можно заметить - порядок не сохраняется. 
# Множество - неупорядоченная коллекция.
1
2
(2, 3)

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


# Сложение с множествами не работает. 
a = {1, 2, 3}
b = {3, 4, 5}
a = a+b
print(a)

TypeError                                 Traceback (most recent call last)
<ipython-input-17-f5ae6f0d46ad> in <module>()
      2 a = {1, 2, 3}
      3 b = {3, 4, 5}
----> 4 a = a+b
      5 print(a)

TypeError: unsupported operand type(s) for +: 'set' and 'set'

# А объединение - пожалуйста. 
a = {1, 2, 3}
b = {3, 4, 5}
a = a | b
print(a)
{1, 2, 3, 4, 5}

# Пересечение двух множеств.
a = {1, 2, 3}
b = {3, 4, 5}
a = a & b
print(a)
{3}

# Разность двух множеств.
a = {1, 2, 3}
b = {3, 4, 5}

print(a - b)
print(b - a)
{1, 2}
{4, 5}

# Симметрическая разность двух множеств.
a = {1, 2, 3}
b = {3, 4, 5}

print(a ^ b)
print(b ^ a)
{1, 2, 4, 5}
{1, 2, 4, 5}

# Извлечение элемента множества.
a = {1, 2, 3}
print(a.pop())
print(a)

try:
  while True:
    element = a.pop()
    function(element)
except KeyError:
  pass

for element in a:
  function(element)


lst = []
push == append
pop == pop
1
{2, 3}

Задачи для закрепления

  • Найти количество различных элементов в списке.
  • Даны два списка чисел. Посчитайте, сколько различных чисел входит хотя бы в один из этих списков.
  • Даны два списка чисел. Посчитайте, сколько чисел входит в первый список, и не входит во второй.

Генераторы списков, словарей, множеств


Основные определения


Генератор списков

Общий синтаксис:

[operation for element1 in iterable1 for element2 in iterable2... if conditions]

Генератор списков — это синтаксическая конструкция, которая к каждому элементу итерируемого объекта, удовлетворяющего условиям conditions, применяет операцию operation и возвращает список состоящий из результатов применения.

Таким образом, генератор списков — простая и удобная конструкция для создания списков, имеющая человекочитаемый синтаксис.


Генератор словарей

Все аналогично со списками. Общий синтаксис:

{create_key:create_value for element1 in iterable1 for element2 in iterable2... if conditions}

Генератор словарей — это синтаксическая конструкция, которая к каждому элементу итерируемого объекта, удовлетворяющего условиям conditions, применяет операцию create_key для создания ключа, операцию create_value для создания значения, и возвращает словарь состоящий из результатов применения.


Генератор множеств

Все аналогично со списками. Общий синтаксис:

{operation for element1 in iterable1 for element2 in iterable2... if conditions}

Генератор множеств — это синтаксическая конструкция, которая к каждому элементу итерируемого объекта, удовлетворяющего условиям conditions, применяет операцию operation и возвращает множество состоящее из результатов применения.


Общие замечания к генераторам:

  • Генераторов кортежей не существует, есть выражения-генераторы, но это совершенно другая сущность, о которой позже.
  • Как видно по общему синтаксису, в генераторе может участвовать несколько итерируемых объектов (iterable1iterable2, … ). В таком случае операции будут применены к каждому возможному набору из element1element2 и т.д.
  • В выполняемой инструкции может быть только одно выражение. Использовать ; для записи нескольких инструкций недопустимо.
  • В операции может быть вызвана сторонняя фукнция, если необходимы сложные преобразования данных.
  • Генераторы могут быть достаточно громоздкими. Наша задача — в случае, если они становятся чересчур сложными для понимания, возможно, стоит переписать это под обычный цикл for.

Code Snippets


Генераторы списков


a = 'A b c'
lst = []
for char in a:
  lst.append(char.lower())

print(lst)
['a', ' ', 'b', ' ', 'c']

# Создание списка, состоящего из квадратов чисел от 0 до 4. 
lst = [el**2 for el in range(5)]
print(lst)
[0, 1, 4, 9, 16]

# Работа с двумя диапазонами. Обратите внимания в каком порядке выбираются пары.
lst1 = [[a, b] for a in range(2) for b in range(2)]
print(lst1)
# Очевидно, что сначала фиксируется элемент из первого итерируемого объекта,
# и начинается проход по второму. Аналогично будет работать если итерируемых 
# объектов будет больше. 
[[0, 0], [0, 1], [1, 0], [1, 1]]

# Работа генератора списка с условием. В результат попадут только результаты
# работы с элементами, удовлетворяющими условиями.
lst1 = [element for element in 'qWeRtY' if 'a' <= element <= 'z']
print(lst1) 

Генераторы словарей


# Базовый синтаксис генератора словарей. 
dct = {str(element): element+1 for element in range(3)}
print(dct, type(dct))
{'0': 1, '1': 2, '2': 3} <class 'dict'>

# Самое важное - чтобы ключи оставались неизменяемыми объектами, иначе буде
# ошибка.
dct = {(element, element+2): element+1 for element in range(3)}
print(dct)

--------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-4a6221bdda86> in <module>()
      1 # Самое важное - чтобы ключи оставались неизменяемыми объектами, иначе буде
      2 # ошибка.
----> 3 dct = {[element, element+2]: element+1 for element in range(3)}
      4 print(dct)

<ipython-input-13-4a6221bdda86> in <dictcomp>(.0)
      1 # Самое важное - чтобы ключи оставались неизменяемыми объектами, иначе буде
      2 # ошибка.
----> 3 dct = {[element, element+2]: element+1 for element in range(3)}
      4 print(dct)

TypeError: unhashable type: 'list'

Генераторы множеств


# Создание множества генератором множеств. Главное помнить, что элементы
# множества должны быть хешируемыми. 
a = {i ** 2 for i in range(10)}
print(a, type(a))
{0, 1, 64, 4, 36, 9, 16, 49, 81, 25} <class 'set'>

# Пример именования переменной, если сама по себе она не нужна.
# В данном случае нам достаточно написать фразу необходимое количество раз. 
# Сам по себе элемент range - нам в этом выражении не нужен.
{print('smth') for _ in range(2) if "condition"}

Задачи для закрепления

  • Поменяйте неотрицательным числам в списке знак, используя один из генераторов.
  • Извлеките из списка числа, не кратные 15, используя один из генераторов.
  • Напишите генератор словарей, который для введенного числа n ставит в соответствие его текстовой записи квадрат этого числа.

[word.strip() for word in sentence.split()] 

Генераторы


Теоретическая часть

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

В целом, генераторы это итераторы, которые не хранят всю коллекцию, которую необходимо обойти, целиком. Генераторы ленивы по своей природе. Такой подход имеет как свои плюсы так и минусы.

ГенераторИтератор
Потребляет мало памятиПотребляет много памяти
Быстрый стартПеред стартом работы требуется много времени
Требует время для генерации элемента(+ переключение контекста на сам генератор)Очередной элемент получается быстро
Возможно прекратить генерацию после определенного элементаВся коллекция будет сгенерирована
Можно пройти один разМожно пройти один раз

Выражение-генератор

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

Общий синтаксис:

(operation for element1 in iterable1 for element2 in iterable2... if conditions)

Функция-генератор

Чтобы создать генератор, необходимо определить фнкцию, но вместо return указать yieldуield приостанавливает функцию и сохраняет локальное состояние, чтобы работу функции можно было возобновить с места, где она была остановлена. Функция, определенная таким образом, после вызова вернет генератор.


Code Snippets


# Создание функции генератора.
def my_generator():
    for i in range(3):
      yield i

gen = my_generator()  # получаем генератор из нашей функции
print(type(gen))

for i in gen:
  print(i)
<class 'generator'>
0
1
2

# Убеждаемся что наш генератор - итератор.
def my_generator():
    for i in range(3):
      yield i

gen = my_generator()
next(gen)
# У нас тут все атрибуты итератора: __iter__ и __next__.
print(*dir(gen), sep='\n')
__class__
__del__
__delattr__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init__
__init_subclass__
__iter__
__le__
__lt__
__name__
__ne__
__new__
__next__
__qualname__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
close
gi_code
gi_frame
gi_running
gi_yieldfrom
send
throw

# Простейшее выражение-генератор.
gen = (_ for _ in range(3))
print(type(gen))
for el in gen:
  print(el)
<class 'generator'>
0
1
2

# И в таком случае получаем тот же самый генератор, который является итератором.
print (*dir((a for a in range(2))), sep='\n')
__class__
__del__
__delattr__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init__
__init_subclass__
__iter__
__le__
__lt__
__name__
__ne__
__new__
__next__
__qualname__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
close
gi_code
gi_frame
gi_running
gi_yieldfrom
send
throw

# Проверим использование памяти выражением-генератором и генератором списков.

import sys

def my_generator():

    for i in range(1000):

      yield i

gen = my_generator()

lst_ = [_ for _ in range(1000)]

print(«Generator size:», sys.getsizeof(gen))

print(«List size:», sys.getsizeof(lst_))

Generator size: 112
List size: 9016

Задача для закрепления материала

Создайте генератор, который возвращает следующее число Фибоначчи.


Подпишитесь на рассылку

Если это было вам полезно — вы можете сказать нам спасибо!