Модель данных Python
Преподаватели
2
Ковалев Святослав, СберКорус
Tg: @Svkov42
Сабалевский Сергей, СберКорус
Tg: @sabal202
Организационные моменты
Как получить зачет?
Сдавать домашки. Нужно сделать ⅔ всех заданий, чтобы получить зачет.
3
Организационные моменты
Как устроены домашки?
Каждая домашка состоит из 3 заданий - Easy, Medium и Hard:
Для получения зачета необходимо сдать все Easy и Medium задачи.
Hard задачей можно закрыть любую другую Easy или Medium задачу из другой темы
Сколько будет домашек - пока неизвестно
4
Организационные моменты
Что нужно знать, чтобы освоить курс?
5
Для кого этот курс будет максимально полезен
6
Структура курса
7
Зачем это знать?
8
Ссылка на чат
В чате будут:
9
О презентациях
На некоторых слайдах есть кнопочка, для интересующихся, с дополнительной информацией по теме на слайде
10
Подробнее
Где используют Python
11
Кто использует Python
12
Реализации Python
13
Python 2 и Python 3
14
Краткое описание
Высокоуровневый язык программирования, который поддерживает несколько парадигм программирования.
Основные архитектурные черты Python:
15
Достоинства
Недостатки
16
Python - интерпретируемый язык
17
Интроспекция
18
Что такое тип данных?
19
“Переменная” в Python
Тип переменной определяется её текущим значением во время присваивания.
Типы тоже являются объектами, тип которых - просто "тип".
20
Типы и утиная типизация
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
Присваивание
Если оператор присваивания используется для одиночных объектов, то его общая форма имеет вид:
label = object
где:
22
Равенство и идентичность
Для проверки двух объектов в Python на “одинаковость” есть 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
Целочисленные литералы
# Системы счисления
# 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
Вещественные - <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
Про точность
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
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
Пустой тип - <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
Еще про 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
Почему нет 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
Про циклы
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
Ветка else
while CONDITION:
BODY
else: # если вышли по условию (а не по break)
FINISH
for VAR in DATASET:
BODY
else: # если исчерпали данные
FINISH
33
Как работает интерпретатор Питона
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’)
]
Как работает интерпретатор Питона
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=[])
Более красивое AST
36
Как работает интерпретатор Питона
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
Все - объект
В интерпретаторе существует структура PyObject, от которой наследуются почти все, что есть в Питоне
Тип объекта - тоже объект, наследуется от PyObject и называется PyTypeObject, в нем содержится информация о функциях __init__, __str__, __hash__ и т.д. Некоторые из функций обязательные, некоторые нет
Например, тип int наследуется от PyTypeObject, а bool наследуется от int
38
Как реализованы списки
Списки - динамические массивы.
Размер растет как: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, …
Наследуется от PyObject
39
Понаблюдаем за списком
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
__hash__
Можем самостоятельно определить как будет хэшироваться наш объект, если определим функцию __hash__, но есть один нюанс - мы не можем возвращать -1
Для целых чисел хэш числа равен самому числу, но есть нюанс:
42
hash(-1) == hash(-2) # True
Вопрос
Какая алгоритмическая сложность у хэш-таблицы (словаря в питоне) для операций:
43
Как реализованы словари
44
Не изменяйте словарь во время итерации
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
С помощью словарей можно заменить 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]()
Immutable > Mutable
47
Вес списка и кортежа
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
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
Как питон работает с памятью
51
Подсчет ссылок
Каждый объект питона ведет количество ссылок, которые ведут на него
Как только количество ссылок начинает равняться нулю, объект удаляется в обход garbage collector.
52
import sys
a = 'hello'
sys.getrefcount(a)
# 2
Подсчет ссылок
Что выведет код?
53
import sys
a = 1
sys.getrefcount(a)
Сборщик мусора (GC)
Ссылки могут быть циклическими, тогда подсчет ссылок их не удалит. Для того чтобы убирать такие структуры, нужен сборщик мусора.
Он работает в предположении о том, что чем дольше объект живет, тем меньше вероятность, что мы захотим его удалить, поэтому существуют 3 поколения
Каждое следующее поколение чистится реже предыдущего
54
Домашнее задание
Easy
Написать функцию для поиска первых n чисел Фибоначчи
Medium
Построить AST полученной выше функции. Для визуализации можно использовать networkx. Для корректного отображения кода (имена переменных и операторы) на графе можно использовать метод ast.unparse (Python >= 3.9) или библиотеку astunparse
Hard
Сделать отображение графа красивым. Помечать разными цветами константы, вызов функций, операторы. Подписи вершин не должны вылазить за границы. Граф должен быть похож на дерево.
55
Как сдавать домашку
56
Полезные ссылки
https://www.youtube.com/watch?v=PxIqLgjtQ5Y
https://habr.com/ru/company/otus/blog/443312/
https://vc.ru/dev/240902-vozvrashchaem-pamyat-operacionnoy-sisteme-s-pomoshchyu-python
https://github.com/python/cpython
https://instagram-engineering.com/dismissing-python-garbage-collection-at-instagram-4dca40b29172
57