1 of 57

Модель данных Python

2 of 57

Преподаватели

2

Ковалев Святослав, СберКорус

Tg: @Svkov42

Сабалевский Сергей, СберКорус

Tg: @sabal202

3 of 57

Организационные моменты

Как получить зачет?

Сдавать домашки. Нужно сделать ⅔ всех заданий, чтобы получить зачет.

3

4 of 57

Организационные моменты

Как устроены домашки?

Каждая домашка состоит из 3 заданий - Easy, Medium и Hard:

  • Easy - задание на 10-20 минут
  • Medium - основное задание, час-полтора, не учитывая дебаггинга
  • Hard - задача со звездочкой. Для самых заинтересованных.

Для получения зачета необходимо сдать все Easy и Medium задачи.

Hard задачей можно закрыть любую другую Easy или Medium задачу из другой темы

Сколько будет домашек - пока неизвестно

4

5 of 57

Организационные моменты

Что нужно знать, чтобы освоить курс?

  • Умение программировать на Питоне (базовое ООП, коллекции, работа с файлами)
  • Опыт работы с веб-фреймворками будет плюсом (Flask/FastApi/Django)
  • Базовое знание алгоритмов и структур данных
  • Базовые знания по визуализации данных на питоне (matplotlib/plotly)

5

6 of 57

Для кого этот курс будет максимально полезен

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

6

7 of 57

Структура курса

  1. Модель данных Python
  2. Виртуальные среды и скрипты
  3. Функциональное программирование и функции
  4. Углубленное ООП
  5. Асинхронное программирование
  6. Метапрограммирование

7

8 of 57

Зачем это знать?

  • Написание библиотек
  • Оптимизация под высокую нагрузку
  • Отладка сложных случаев
  • Глубокое знание инструмента = профессионализм
  • Спрашивают на собесах
  • Это интересно

8

9 of 57

Ссылка на чат

В чате будут:

  • Ссылка на презентации
  • Ответы на вопросы
  • Административные вопросы

9

10 of 57

О презентациях

На некоторых слайдах есть кнопочка, для интересующихся, с дополнительной информацией по теме на слайде

10

Подробнее

11 of 57

Где используют Python

  • Работа с xml/html файлами
  • GUI (графический интерфейс)
  • Создание веб-сценариев
  • Работа с изображениями, аудио и видео файлами
  • Робототехника
  • Программирование математических и научных вычислений
  • Искусственный интеллект
  • Анализ данных
  • Разработка игр
  • Веб скрапинг
  • Чат-боты

11

12 of 57

Кто использует Python

12

13 of 57

Реализации Python

13

14 of 57

Python 2 и Python 3

  • Unicode vs. ASCII
  • Итераторы
  • И много другого

14

15 of 57

Краткое описание

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

Основные архитектурные черты Python:

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

15

16 of 57

Достоинства

Недостатки

  • Гибкость
  • Интерактивность
  • Простота
  • Динамическая типизация
  • Скоростные ограничения
  • Проблемы с многопоточностью
  • Проблемы с системой типов

16

17 of 57

Python - интерпретируемый язык

17

18 of 57

Интроспекция

  • Можно “на лету” узнавать тип переменной
  • Можно посмотреть структуру программы из самой программы
  • Можно исполнить код внутри программы
  • Доступ к состоянию всей рабочей области

18

19 of 57

Что такое тип данных?

  • python имеет динамическую типизацию;
  • проверить тип можно функцией type(x);
  • привести один тип к другому можно с помощью специальных функций.

19

20 of 57

“Переменная” в Python

Тип переменной определяется её текущим значением во время присваивания.

Типы тоже являются объектами, тип которых - просто "тип".

20

21 of 57

Типы и утиная типизация

x = 1 # создается объект типа int со значением 1.

# объекту присваивается id. Переменная x ссылается на этот объект

# если объект по данному id изменит значение, то значение изменит и x, который ссылается на значение

print(x, type(x)) # 1 <class 'int'>

x = x * 1.5

print(x, type(x)) # 1.5 <class 'float'>

x = str(x)

print(x, type(x)) # 1.5 <class 'str'>

print(type(type(x))) # <class 'type'>

21

22 of 57

Присваивание

Если оператор присваивания используется для одиночных объектов, то его общая форма имеет вид:

label = object

где:

  • object– значение, присваиваемое имени label, которое может быть выражением или одиночным значением
  • label – имя, этикетка (переменная), которое будет ссылаться на object

22

23 of 57

Равенство и идентичность

Для проверки двух объектов в Python на “одинаковость” есть 2 метода:

  1. Проверка на идентичность is
  2. И стандартные !=, ==

i, j = 100, 100 # маленькие числа (от -5 до 255)

print('ints:', i == j, i is j, id(i), id(j)) # и равны, и идентичны

# >>> ints: True True 4531811472 4531811472

x, y = 10**100, 10**100 # большие числа

print('big ints:', x == y, x is y, id(x), id(y)) # равны, но не идентичны

# big ints: True False 4576818352 4576818496

23

24 of 57

Типы делятся на изменяемые и неизменяемые

  • Immutable
    • str
    • int, float, complex
    • bool
    • tuple
  • Mutable
    • list
    • dict
    • set

24

25 of 57

Целочисленные литералы

# Системы счисления

# 2-, 8-, 10- и 16-ричные литералы

print(0b11001100, 0o314, 204, 0xCC) # 204 204 204 204

# строковые представления

print(bin(204), oct(204), str(204), hex(204))# 0b11001100 0o314 204 0xcc

25

26 of 57

Вещественные - <class 'float'>

64-разрядные вещественные числа "двойной точности" (они же - double в языках семейства Си)

# фиксированная точка (внутреннее представление всё равно плавающее)

print(1., .1, 1.1) # 1.0 0.1 1.1

# экспоненциальная запись

print(.1e1, 1e-1, 1.1e0) # 1.0 0.1 1.1

# числа можно возводить в дробную степень

print(5.+.3, 5.-.3, 5.*.3, 5.**.3) # 5.3 4.7 1.5 1.6206565966927624

# деление без остатка, деление нацело и остаток от деления!

print(5./.3, 5.//.3, 5.%.3) # 16.666666666666668 16.0 0.20000000000000018

# как получить целую и дробную части числа?

print(123.45//1, 123.45%1) # 123.0 0.45000000000000284

26

27 of 57

Про точность

import sys

print(sys.float_info)

print(sys.float_info.max_10_exp)

print(sys.float_info.epsilon)

sys.float_info(

max=1.7976931348623157e+308,

max_10_exp=308,

min=2.2250738585072014e-308,

min_10_exp=-307,

epsilon=2.220446049250313e-16

)

27

28 of 57

or и not являются ленивыми операторами

Операции and, or, not - ленивые.

a = 6

b = 9

print('max:', a if a > b else b) # Тернарный оператор

x = 0

print(x or 'default') # default

print(x and 'finally') # 0

print('one' if x == 1 else 'two' if x == 2 else 'otherwise') # otherwise

28

29 of 57

Пустой тип - <class 'NoneType'>

Тип с названием NoneType и единственным значением None. Предназначен для указания отсутствия значений. Это аналог сишного типа void, но более полезный.

print(None) # None

print(type(None)) # <class 'NoneType'>

x = None

y = 123

print(x == None, x is None, x is not None) # True True False

print(y == None, y is None, y is not None) # False False True

29

30 of 57

Еще про bool

print('bool from pos int is ', bool(5)) # True

print('bool from zero is ', bool(0)) # False

print('bool from empty str is ', bool('')) # False

print('bool from empty list is ', bool([])) # False

print(True is 1) # False

print(True == 1) # True

print(True + False) # 1

print(True / False) # ZeroDivisionError: division by zero

30

31 of 57

Почему нет switch-case

x = 2*2-1 # запомним результат вычислений в переменную

# и далее - лесенка сравнений

if x==1:

print('one')

elif x==2:

print('two')

elif x==3:

print('three')

else:

print('unexpected')

Еще один вариант - использование словаря

31

32 of 57

Про циклы

for i in range(5): # в диапазоне [0,5)

print(i)

for c in 'abcde': #проходим по итерируемому объекту

print(c)

# Если нужен цикл, в котором есть индексы, - используется коллекция enumerate

for i, c in enumerate('abcde'):

print(i, c)

32

33 of 57

Ветка else

while CONDITION:

BODY

else: # если вышли по условию (а не по break)

FINISH

for VAR in DATASET:

BODY

else: # если исчерпали данные

FINISH

33

34 of 57

Как работает интерпретатор Питона

  1. Код переводится в лексемы

34

from tokenize import tokenize

from io import BytesIO

from token import tok_name

code_string = 'print(222*555)'

tokens = tokenize(BytesIO(code_string.encode(

'utf-8')).readline) # tokenize the string

pprint([( token.string, tok_name[token.type])

for token in tokens])

[

('utf-8', 'ENCODING’),

('print', 'NAME’),

('(', 'OP’),

('222', 'NUMBER’),

('*', 'OP’),

('555', 'NUMBER’),

(')', 'OP’),

('', 'NEWLINE’),

('', 'ENDMARKER’)

]

35 of 57

Как работает интерпретатор Питона

2. Строится AST

35

import ast

ast_object = ast.parse("def to_power(a,b):

return a ** b;")

print(ast.dump(ast_object))

Module(body=[FunctionDef(name='to_power', args=arguments(posonlyargs=[], args=[arg(arg='a'), arg(arg=’b’)], kwonlyargs=[], kw_defaults=[], defaults=[]), body=[Return(value=BinOp(left=Name(id='a', ctx=Load()), op=Pow(), right=Name(id='b', ctx=Load())))], decorator_list=[])], type_ignores=[])

36 of 57

Более красивое AST

36

37 of 57

Как работает интерпретатор Питона

3. AST переводится в байткод

37

import dis

dis.dis('print(123)')

1 0 LOAD_NAME 0 (print)

2 LOAD_CONST 0 (123)

4 CALL_FUNCTION 1

6 RETURN_VALUE

38 of 57

Все - объект

В интерпретаторе существует структура PyObject, от которой наследуются почти все, что есть в Питоне

Тип объекта - тоже объект, наследуется от PyObject и называется PyTypeObject, в нем содержится информация о функциях __init__, __str__, __hash__ и т.д. Некоторые из функций обязательные, некоторые нет

Например, тип int наследуется от PyTypeObject, а bool наследуется от int

38

39 of 57

Как реализованы списки

Списки - динамические массивы.

Размер растет как: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, …

Наследуется от PyObject

39

40 of 57

Понаблюдаем за списком

40

import sys

a = []

sys.getsizeof(a) # 56

a.append(1) # [1]

sys.getsizeof(a) # 88

a.append(1) # [1, 1]

sys.getsizeof(a) # 88

a.append(1) # [1, 1, 1]

sys.getsizeof(a) # 88

a.append(1) # [1, 1, 1, 1]

sys.getsizeof(a) # 88

a.append(1) # [1, 1, 1, 1, 1]

sys.getsizeof(a) # 120

41 of 57

Хэш-функции

41

42 of 57

__hash__

Можем самостоятельно определить как будет хэшироваться наш объект, если определим функцию __hash__, но есть один нюанс - мы не можем возвращать -1

Для целых чисел хэш числа равен самому числу, но есть нюанс:

42

hash(-1) == hash(-2) # True

43 of 57

Вопрос

Какая алгоритмическая сложность у хэш-таблицы (словаря в питоне) для операций:

  • Вставки
  • Чтения
  • Удаления

43

44 of 57

Как реализованы словари

  1. Считаем хэш от ключа
  2. Хэш - индекс в словаре
  3. Если произошла коллизия, то записываем в следующий свободный слот
  4. Если свободных слотов будет мало, то сложность всех операций будет ближе к линейной, поэтому не забываем аллоцировать побольше свободных мест

44

45 of 57

Не изменяйте словарь во время итерации

45

a = {'a':1, 'b':2}

try:

for i in a:

a['c'] = 3

except RuntimeError as e:

print(f'ERROR: {e}’)

ERROR: dictionary changed size during iteration

46 of 57

С помощью словарей можно заменить if-else

46

if code == 1:

# code_1_handler()

elif code == 2:

# code_2_handle()

# ...

handlers = {

1: code_1_handler,

2: code_2_handler,

# ...

}

handlers[code]()

47 of 57

Immutable > Mutable

  • Неизменяемые типы проще читать (понятно, что находится внутри)
  • Неизменяемые типы как правило хэшируемые (а значит можно использовать как ключ словаря), причем хэш не изменяется
  • Tuple работает быстрее List’а и занимает меньше памяти
  • По иммутабельным типам можно настроить кэширование

47

48 of 57

Вес списка и кортежа

48

import sys

print(sys.getsizeof([1])) # 64

print(sys.getsizeof((1,))) # 48

a = list(range(100))

print(sys.getsizeof(a)) # 856

b = tuple(range(100))

print(sys.getsizeof(b)) # 840

49 of 57

Копирование

49

import copy

a = [1]

# Копируем объект

b = copy.copy(a)

# или

b = copy.deepcopy(a)

# или (для списков)

b = a[:]

b.append(2)

print(a)

# [1]

a = [1]

b = a

b.append(2)

print(a)

# [1, 2]

50 of 57

Как питон работает с памятью

  • Память выделяется блоками по 256 Кб и называется Ареной
  • Арены делятся на пулы по 4кб
  • Пулы делятся на блоки, внутри одного пула блоки одного размера

50

51 of 57

Как питон работает с памятью

  • Арена хранит количество свободных пулов
  • Если все пулы свободны, то арена освобождается и отдается ОС (происходит крайне редко)
  • Из-за того, что памяти всегда выделено больше, чем нужно, мы почти не тратим время на выделение памяти

51

52 of 57

Подсчет ссылок

Каждый объект питона ведет количество ссылок, которые ведут на него

Как только количество ссылок начинает равняться нулю, объект удаляется в обход garbage collector.

52

import sys

a = 'hello'

sys.getrefcount(a)

# 2

53 of 57

Подсчет ссылок

Что выведет код?

53

import sys

a = 1

sys.getrefcount(a)

54 of 57

Сборщик мусора (GC)

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

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

Каждое следующее поколение чистится реже предыдущего

54

55 of 57

Домашнее задание

Easy

Написать функцию для поиска первых n чисел Фибоначчи

Medium

Построить AST полученной выше функции. Для визуализации можно использовать networkx. Для корректного отображения кода (имена переменных и операторы) на графе можно использовать метод ast.unparse (Python >= 3.9) или библиотеку astunparse

Hard

Сделать отображение графа красивым. Помечать разными цветами константы, вызов функций, операторы. Подписи вершин не должны вылазить за границы. Граф должен быть похож на дерево.

55

56 of 57

Как сдавать домашку

  • Делаете репозиторий на гитхабе
  • В отдельную папку (hw_1, например) выкладываете код с домашкой
  • Артефакты, полученные после выполнения программы (картинки/текст/etc), необходимые для проверки, выкладываете в папку artifacts (hw_1/artifacts). ДЗ без артефактов не принимается
  • В корне проекта обязательно сделать requirements.txt с указанием используемых библиотек и их версий
  • При проверке код будет запускаться только если возникнут сомнения, что артефакты были сгенерированы этим кодом
  • Код должен быть читаемым. Если код нечитаемый, то пиши комментарии перепиши
  • Ссылку на репозиторий необходимо скинуть на почту python.amcp@yandex.ru
  • Дедлайн - через 2 недели (среда, 2 февраля, 23:59)

56

57 of 57

Полезные ссылки

57