diff --git a/.flake8 b/.flake8
deleted file mode 100644
index d8b3bc46..00000000
--- a/.flake8
+++ /dev/null
@@ -1,9 +0,0 @@
-[flake8]
-extend-ignore = E722
-exclude =
- .git,
- __pycache__,
- venv,
- lessons
-max-complexity = 10
-max-line-length = 100
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7c0fb594..7f69be5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,24 @@
# VS code Folders
*.vscode/
+# Configs data
+configs/sem.json
+
# Python Folders
*__pycache__/
*.pytest_cache
venv
+.venv
# Solution data
*solved.ipynb
*solved.py
-solved
+*solved
# Other files
*.docx
*.doc
+*.puml
+
+# Hidden tests
+*tests/
diff --git a/README.md b/README.md
index c01a894c..41483ab7 100644
--- a/README.md
+++ b/README.md
@@ -1,35 +1,57 @@
# Программирование на языке Python
-В этом репозитории вы найдете материалы лекций, семинаров и домашние задания по курсу "Программирование на языке Python", который читается в МФТИ на факультете аэромеханики и летательной техники (он же ФАЛТ, он же ИАЛТ, он же ПИШ РПИ).
+В этом репозитории хранятся материалы лекций, семинаров и домашние задания по курсу "Программирование на языке Python". Курс читается в МФТИ на факультете аэромеханики и летательной техники (ФАЛТ), ныне Физтех-школа авиационных и цифровых технологий (ПИШ ФАЛТ).
+
+## Перед началом работы
+Прежде, чем переходить к материалам курса, внимательно ознакомьтесь с требованиями к версии Python и мануалом по установке интерпретатора. Найти мануал можно [тут](./docs/guide.pdf).
## Осенний семестр
Эта часть курса посвящена знакомству с языком Python, его основам и базовым концепциям.
**Содержание:**
-
-- [Вводная лекция](./lessons/lesson1/);
-- [Структура языка и Базовые концепции](./lessons/lesson2/);
-- [Типы данных: числа, кортежи, списки](./lessons/lesson3/);
-- [Типы данных: строки](./lessons/lesson4/);
-- [Типы данных: словари и множества](./lessons/lesson5/);
-- [Исключения](./lessons/lesson6/);
-- [Функции](./lessons/lesson7/);
-- [Декораторы](./lessons/lesson8/);
-- [Функции: избранные темы](./lessons/lesson9/);
-- [ООП: Класс и Экземпляры](./lessons/lesson10/);
-- [ООП: Базовые концепции](./lessons/lesson11/);
-
-**Домашние работы:**
-- [Домашняя работа №1: Метод Наименьших Квадратов для линейных функций](./homeworks/hw1/);
+- [Введение в Python](./lessons/sem01/lesson01/);
+- [Анатомия Python-программы](./lessons/sem01/lesson02/);
+- [Числовые типы данных](./lessons/sem01/lesson03/);
+- [Последовательности: списки и кортежи](./lessons/sem01/lesson04/);
+- [Последовательности: строки](./lessons/sem01/lesson05/);
+- [Словари и множества](./lessons/sem01/lesson06/);
+- [Пользовательские функции](./lessons/sem01/lesson07/);
+- [Замыкания и Декораторы](./lessons/sem01/lesson08/);
+- [Исключения](./lessons/sem01/lesson09/);
+- [Пользовательские классы](./lessons/sem01/lesson10/);
+- [ООП в Python](./lessons/sem01/lesson11/);
+- [Протоколы в Python](./lessons/sem01/lesson12/);
+- [Модули](./lessons/sem01/lesson13/);
+
+**Домашние задания**:
+- [Домашнее задание 1](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/tree/main/homeworks/hw1);
## Весенний семестр
+Эта часть курса посвящена научным вычислениям и работе с данными на языке Python.
+
+**Содержание**:
+- [Менеджер пакетов и виртуальное окружение](./lessons/sem02/lesson01);
+- [Введение в NumPy](./lessons/sem02/lesson02/);
+- [Операции над массивами NumPy](./lessons/sem02/lesson03/);
+
+## Практические задания:
+
+Все практические задания хранятся в [отдельном репозитории](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks).
+
+## Архивные материалы
+
+- [Материалы 2023-2024 учебного года](https://github.com/EvgrafovMichail/python_mipt_dafe/tree/2023-2024-archive);
+- [Материалы 2024-2025 учебного года](https://github.com/EvgrafovMichail/python_mipt_dafe/tree/2024-2025-archive);
## Источники
Данный курс во многом основан на материале следующих источников:
- `Python in a Nutshell`. Third Edition. Alex Martelli, Anna Ravenscroft, Steve Holden. O’Reilly. 2017;
-- `Fluent Python`. Luciano Ramalho. O’Reilly. 2016;
+- `Fluent Python`. Luciano Ramalho. O’Reilly. 2016;
+- `Python Data Science Handbook`. Jake VanderPlas. O’Reilly. 2020;
- [Real Python](https://realpython.com/);
+- [Официальная документация](https://docs.python.org/3.11/);
+- [Официальная документация NumPy](https://numpy.org/);
diff --git a/create_lesson.py b/create_lesson.py
deleted file mode 100644
index 3abf2a6b..00000000
--- a/create_lesson.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import logging
-import sys
-import os
-
-
-logging.basicConfig(
- level=logging.INFO, format='[%(levelname)s]: %(message)s;'
-)
-
-
-def create_lesson_folders(lesson_id: int) -> None:
- logging.info(f'trying to create template for lesson {lesson_id}')
-
- path_to_lesson = os.path.join('lessons', f'lesson{lesson_id}')
-
- if os.path.exists(path_to_lesson):
- raise RuntimeError(f'{path_to_lesson} is already exist;')
-
- groups = ['312', '313', '314']
-
- for group in groups:
- path_to_sem = os.path.join(
- path_to_lesson, f'sem{lesson_id}_{group}'
- )
-
- os.makedirs(path_to_sem)
- logging.info(f'successfully created {path_to_sem}')
-
- logging.info(
- f'lesson {lesson_id} template was succesfully created'
- )
-
-
-if __name__ == '__main__':
- if len(sys.argv) != 2:
- raise RuntimeError(
- f'invalid arguments amount: {len(sys.argv) - 1}; '
- 'one argument was expected;'
- )
-
- lesson_id = int(sys.argv[1])
- create_lesson_folders(lesson_id)
diff --git a/homeworks/hw1/lsm_project/event_logger/__init__.py b/homeworks/.gitkeep
similarity index 100%
rename from homeworks/hw1/lsm_project/event_logger/__init__.py
rename to homeworks/.gitkeep
diff --git a/homeworks/hw1/description.md b/homeworks/hw1/description.md
deleted file mode 100644
index 970b08dd..00000000
--- a/homeworks/hw1/description.md
+++ /dev/null
@@ -1,149 +0,0 @@
-# Домашнее Задание 1: Метод Наименьших Квадратов для линейных функций
-
-## Предварительные шаги
-
-Прежде чем начать выполнение задания, вам необходимо настроить среду:
-
-- перейдите в директорию, содержащую данные о вашем репозитории;
-
-- создайте виртуальное окружение (virtual environment); этот шаг необходим, чтобы установить сторонние библиотеки, при этом не оказывая эффекта на глобальное виртуальное окружение; чтобы создать виртуальное окружение выполните следующую команду в командной строке:
- ```console
- python -m venv venv
- ```
-
- после выполнения этой команды в текущей директории должна появится папка `venv`, в которой содержится вся информация о виртуальном окружении;
-
-- теперь вам необходимо активировать виртуальное окружение, чтобы все манипуляции с пакетным менеджером осуществлялись в рамках изолированной среды; для активации необходимо запустить специальный скрипт, хранящийся в папке `venv`; запуск скрипта выглядит следующим образом:
-
- *Windows*:
- ```console
- .\venv\Scripts\activate
- ```
-
- *Unix-подобные ОС*:
- ```
- source venv/bin/activate
- ```
-- после активации виртуального окружения установим необходимые зависимости; список зависимостей хранится в файле `homeworks/hw1/requirements.txt`; для установки выполним следующую команду:
-
- ```console
- pip install -r homeworks/hw1/requirements.txt
- ```
-
-## Описание задания
-
-### Теоретическая справка
-
-Метод наименьших квадратов (МНК) - математический метод оценки некоторой зависимости путем минимизации функционала качества: суммы квадратов отклонений теоретических значений от экспериментальных данных. Оценка параметров линейной зависимости является простейшей задачей на применение МНК.
-
-Формальная постановка задачи описывается следующим образом:
-
-Пусть у нас имеется некоторая независимая величина $x$, принимающая значения из множества $X \subset \mathbb{R}$, и величина $y$, принимающая значения из множества $Y \subset \mathbb{R}$. Мы точно не знаем, как величина $y$ зависит от величины $x$, но можем предполжить, что между ними существует линейная связь: $f: x \rightarrow y$, $f(x) = ax + b$. В общем случае, эта связь неизвеста, но имеется некоторая выборка - счетное множество пар вида: $\{(x_i, y_i)\}, i = \overline{1, n}, x_i \in X, y_i \in Y$ - экспериментально полученные наблюдения. Тогда для каждого наблюдения можно определить ошибку регрессии $\varepsilon_i = y_i - f(x_i) = y_i - a \cdot x_i - b$. Наша задача - подобрать оптимальные коэффициенты $a$ и $b$, которые минимизируют сумму квадратов ошибок $\varepsilon_i$.
-
-Т.е. восстановление линейной зависимости с помощью МНК - это решение следующей оптимизационной задачи:
-
-$$\sum\limits_{i=1}^{n} {(y_i - a \cdot x_i - b)^2} \rightarrow min_{a, b}$$
-
-Данная задача имеет аналитическое решение, которое вы без труда сможете вывести самостоятельно, освоив программу Математического Анализа за второй семестр (для получения решения необходимы понятия функции многих переменных и частной производной). Само решение выглядит следующим образом:
-
-$$a = \dfrac{ \langle xy \rangle - \langle x \rangle \langle y \rangle}{ \langle {x}^2 \rangle - {\langle x \rangle }^2}$$
-
-$$b = \langle y \rangle - a \langle x \rangle$$
-
-В данных уравнения символ $\langle\cdots\rangle$ обозначает среднее значение по выборке, т.е. $\langle x \rangle = \dfrac{1}{n}\sum\limits_{i=1}^{n}{x_i}$.
-
-Помимо оценок коэффициентов линейной зависимости также вычисляют значения оценок их дисперсий, используемые для определения среднеквадратичных отклонений. Оценки дисперсий вычисляются по следующим формулам:
-
-$$\sigma_y^2 = \dfrac{1}{n - 2} \sum\limits_{i=1}^{n}{(y_i - ax_i - b)^2}$$
-
-$$\sigma_a^2 = \dfrac{\sigma_y^2}{n(\langle {x}^2 \rangle - {\langle x \rangle}^2)}$$
-
-$$\sigma_b^2 = \dfrac{\sigma_y^2 \langle {x}^2 \rangle}{n(\langle {x}^2 \rangle - {\langle x \rangle}^2)}$$
-
-
-$\sigma_y^2$ - оценка дисперсии зависимой величины (истинная дисперсия шума нам никогда неизвестна); в знаменателе расположено выражение $n-2$, необходимое для выполнения свойства несмещённости этой оценки (о несмещённости оценки и других её свойствах вы узнаете в курсе Математической Статистики в пятом семестре). Из этого следует, что $n \geq 3$, т.е. для вычисления оценки требуется как минимум 3 эксперименальные точки.
-
-### Техническое задание
-
-Ваша задача - реализация метода наименьших квадратов используя **только встроенные** средства языка Python (сторонними библиотеками, типа NumPy, пользоваться запрещено).
-
-Что вам нужно сделать:
-- Реализовать функцию `get_lsm_description()`; Функция принимает на вход три аргумента: **abscissa** - список чисел с плавающей точкой - измерения независимой величины, **ordinates** - список чисел с плавающей точкой - измерения зависимой величины, **mismatch_strategy** - значение из перечисления `MismatchStrategies` - стратегия по обработке несовпадения длин входных списков; Функция позволяет получить описание МНК и возвращает структуру типа `LSMDescription`, содержащую 4 поля: коэффициенты наклона **incline** и смещения **shift**, среднеквадратичные отклонения **$\sigma_a$**, **$\sigma_b$**, помимо этого, функция должна обладать следующей логикой:
- - Перед началом вычислений необходимо произвести валидацию переданных списков: проверить, что переданные значения - списки; в случае, если это возможно, попытаться привести полученные значения к типу list, если это невозможно - возбудить исключение `TypeError`; проверить, что все элементы списка - действительные числа (вам понадобится абстрактный класс `Real`), если это не так, возбудить исключение типа `ValueError`; проверить, что длина списка больше 2, если это не так - возбудить исключение типа `ValueError`;
- - Если длины списков не совпадают необходимо обработать несоответствие, пользуясь одной из стратегий обработки, задаваемой аргументом **mismatch_strategy**; Если mismatch_strategy == MismatchStrategies.FALL, необходимо возбудить исключение типа `RuntimeError`; Если mismatch_strategy == MismatchStrategies.CUT, необходимо привести списки к единой длине, равной минимальной из двух длин переданных аргументов; Если значение mismatch_strategy отлично от перечисленных - необходимо возбудить исключение типа `ValueError`;
- - Будет плюсом, если по ходу выполнения функции вы будете логировать определенные этапы выполнения с помощью объекта `EventLogger`;
-
-- Реализовать функцию `get_lsm_lines()`; Функция принимает на вход три аргумента: **abscissa** - список чисел с плавающей точкой - измерения независимой величины, **ordinates** - список чисел с плавающей точкой - измерения зависимой величины, **lsm_description** - необязательный аргумент, None или объект типа `LSMDescription`; Функция позволяет получить значение типа `LSMLines` - специальная структура для описания значений функций, полученных в результате использования МНК, в экспериментальных точках, обладает пятью полями: **abscissa** - список чисел с плавающей точкой - измерения независимой величины, **ordinates** - список чисел с плавающей точкой - измерения зависимой величины, **line_predicted** - список чисел с плавающей точкой - значения функции $f(x) = ax + b$ на точках из массива **abscissa**, **line_above** - список чисел с плавающей точкой - значения функции $f(x) = (a + \sigma_a)x + b + \sigma_b$ на точках из массива **abscissa**, **line_under** - список чисел с плавающей точкой - значения функции $f(x) = (a - \sigma_a)x + b - \sigma_b$ на точках из массива **abscissa**; Функция должна обладать следующей логикой:
- - Перед началом вычисления значений функций, необходимо проверить значение аргумента **lsm_description**; Если его значение - None, необходимо вычислить описание МНК - значение типа `LSMDescription`; Если тип значения **lsm_description** отличен от `LSMDescription` необходимо вызвать исключение типа `TypeError`; Иначе можно переходить к вычислениям;
- - Будет плюсом, если по ходу выполнения функции вы будете логировать определенные этапы выполнения с помощью объекта `EventLogger`;
-
-- Реализовать функцию `get_report()`, с помощью которой можно получить строку - отчет о расчитанном МНК; Функция обладает двумя параметрами: обязательный параметр **lsm_description** - объект типа `LSMDescription`, с которым вы уже знакомы, и **path** - необязательный параметр, строка - путь к файлу, в который можно сохранить отчет; Если параметр path заполнен, происходит сохранение вашего отчета в этот файл (вам будет полезна функция `open()`, возможно, будет полезна функция `os.path.exists()`); Функция возвращает строку - отчет в следующем формате:
- ```console
- ========================================LSM computing result========================================
-
- [INFO]: incline: 5.068;
- [INFO]: shift: 0.730;
- [INFO]: incline error: 0.035;
- [INFO]: shift error: 0.203;
-
- ====================================================================================================
- ```
-
-Помимо этого в проекте есть функции с префиксом _, это служебные функции, которые могут оказаться полезными для поддержания понятной структуры вашей работы.
-
-### Структура проекта
-
-*Структура проекта:*
-```console
-homeworks/hw1/
-├───lsm_project
-│ ├───event_logger
-│ │ ├───__init__.py
-│ │ └───event_logger.py
-│ ├───lsm
-│ │ ├───__init__.py
-│ │ ├───enumerations.py
-│ │ ├───functions.py
-│ │ └───models.py
-│ └───visualization.py
-├───main.py
-├───tests
-│ └...
-├───description.md
-├───measurments.json
-└───requirements.txt
-```
-
-В папке `lsm_project/` лежит специальный класс для логирования `EventLogger`, изменять его не надо. Папка `lsm/` содержит полезные перечисления в модуле `enumerations` и структуры данных в модуле `models`. Вас интересует файл `functions`, в котором лежат заготовки функций, необходимых для реализации МНК.
-
-Для самопроверки вы можете, перейдя в директорию hw1/, запустить скрипт main.py для проверки работоспособности вашего кода. В файле visualize.py лежат функции для визуализации полученной зависимости. В результате выполнения main.py в директории hw1/ должен появится png-файл следующего содержания:
-
-
-
-## Оценивание решения
-
-Первичная оценка:
-
-- Оформление: задание считается оформленным правильно, если утилита `flake8` не выдает никаких ошибок; Вы можете самостоятельно проверить правильность оформления, установив `flake8` командой:
- ```console
- pip install flake8
- ```
-
- после чего запустив его, командой:
- ```console
- flake8 path/to/hw1
- ```
-
- Каждая ошибка, обнаруженная линтером, будет наказываться снижением итоговой оценки за оформление на 0.5 балла; Максимальный балл - 10;
-
-- Корректность: задание считается правильно выполненным, если полученное решение проходит все юнит-тесты; Вы можете самостоятельно проверить корректность решения с помощью утилиты `pytest`:
-
- ```console
- pytest path/to/hw1_folder
- ```
-
- Всего вам предложено реализовать 3 функций. Функция считается правильной, если ни один тест из набора тестов для данной функции не заканчивается со статусом, отличным от `PASSED`. В этом случае, вы получаете 1 балл, иначе - 0. Итоговая оценка за правильность: $grade = 10 \times \frac{1}{3}\sum\limits_{i = 1}^{3}{grade_i}$;
-
-- Первичная оценка вычислятеся по формула: $grade_{init} = 0.3 \times grade_{beauty} + 0.7 \times grade_{correct}$;
-
-Чтобы получить итоговую оценку, вам необходимо защитить результат первичного оценивания во время устной сдачи семинаристу. Помимо этого семинаристом будет оцениваться структура вашего проекта. Семирист может понизить или аннулировать первичную оценку, если посчитает, что решение неэффективно или списано.
diff --git a/homeworks/hw1/images/lsm.png b/homeworks/hw1/images/lsm.png
deleted file mode 100644
index 05f8ea48..00000000
Binary files a/homeworks/hw1/images/lsm.png and /dev/null differ
diff --git a/homeworks/hw1/lsm_project/event_logger/event_logger.py b/homeworks/hw1/lsm_project/event_logger/event_logger.py
deleted file mode 100644
index 814dd4a3..00000000
--- a/homeworks/hw1/lsm_project/event_logger/event_logger.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""
-В этом модуле хранится специальны класс для логирования
-"""
-
-
-import logging
-
-from enum import Enum
-
-
-# уровни логирования
-class Levels(Enum):
- debug = logging.DEBUG
- info = logging.INFO
- warning = logging.WARNING
- error = logging.ERROR
-
-
-class EventLogger:
- _logger: logging.Logger
-
- def __init__(self, level: Levels = Levels.debug) -> None:
- if not isinstance(level, Levels):
- raise ValueError(
- f'unexpected level type: {type(level).__name__}'
- )
-
- self._logger = logging.getLogger('event_logger')
-
- formatter = logging.Formatter(
- '[%(levelname)s]: || %(asctime)s || %(message)s;'
- )
-
- handler = logging.StreamHandler()
- handler.setLevel(level.value)
- handler.setFormatter(formatter)
-
- self._logger.setLevel(level.value)
- self._logger.addHandler(handler)
-
- def debug(self, message: str) -> None:
- """
- Функция для вывода отладочной информации
- """
- self._logger.debug(message)
-
- def info(self, message: str) -> None:
- """
- Функция для вывода информации по ходу выполнения
- """
- self._logger.info(message)
-
- def warning(self, message: str) -> None:
- """
- Функция для вывода информации о предупреждениях
- """
- self._logger.warning(message)
-
- def error(self, message: str) -> None:
- """
- Функция для вывода информации об ошибках
- """
- self._logger.error(message)
diff --git a/homeworks/hw1/lsm_project/lsm/enumerations.py b/homeworks/hw1/lsm_project/lsm/enumerations.py
deleted file mode 100644
index b8c52fd7..00000000
--- a/homeworks/hw1/lsm_project/lsm/enumerations.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from enum import Enum
-
-
-# перечисление с возможными стратегиями обработки
-# несовпадения размеров списков
-class MismatchStrategies(Enum):
- CUT = 'cut' # для обрезки длинного списка
- FALL = 'fall' # для возбуждения исключения
diff --git a/homeworks/hw1/lsm_project/lsm/functions.py b/homeworks/hw1/lsm_project/lsm/functions.py
deleted file mode 100644
index 7c200095..00000000
--- a/homeworks/hw1/lsm_project/lsm/functions.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""
-В этом модуле хранятся функции для применения МНК
-"""
-
-
-from typing import Optional
-# from numbers import Real # раскомментируйте при необходимости
-
-from lsm_project.event_logger.event_logger import EventLogger
-
-from lsm_project.lsm.enumerations import MismatchStrategies
-from lsm_project.lsm.models import (
- LSMDescription,
- LSMStatistics,
- LSMLines,
-)
-
-
-PRECISION = 3 # константа для точности вывода
-event_logger = EventLogger() # для логирования
-
-
-def get_lsm_description(
- abscissa: list[float], ordinates: list[float],
- mismatch_strategy: MismatchStrategies = MismatchStrategies.FALL
-) -> LSMDescription:
- """
- Функции для получения описания рассчитаной зависимости
-
- :param: abscissa - значения абсцисс
- :param: ordinates - значение ординат
- :param: mismatch_strategy - стратегия обработки несовпадения
-
- :return: структура типа LSMDescription
- """
-
- global event_logger
-
- # ваш код
- # эту строчку можно менять
- return LSMDescription(
- incline=0,
- shift=0,
- incline_error=0,
- shift_error=0
- )
-
-
-def get_lsm_lines(
- abscissa: list[float], ordinates: list[float],
- lsm_description: Optional[LSMDescription] = None
-) -> LSMLines:
- """
- Функция для расчета значений функций с помощью результатов МНК
-
- :param: abscissa - значения абсцисс
- :param: ordinates - значение ординат
- :param: lsm_description - описание МНК
-
- :return: структура типа LSMLines
- """
-
- # ваш код
- # эту строчку можно менять
- return LSMLines(
- abscissa=abscissa,
- ordinates=ordinates,
- line_predicted=ordinates,
- line_above=ordinates,
- line_under=ordinates
- )
-
-
-def get_report(
- lsm_description: LSMDescription, path_to_save: str = ''
-) -> str:
- """
- Функция для формирования отчета о результатах МНК
-
- :param: lsm_description - описание МНК
- :param: path_to_save - путь к файлу для сохранения отчета
-
- :return: строка - отчет определенного формата
- """
- global PRECISION
-
- # ваш код
- # эту строчку можно менять
- return 'report'
-
-
-# служебная функция для валидации
-def _is_valid_measurments(measurments: list[float]) -> bool:
- # ваш код
- # эту строчку можно менять
- return False
-
-
-# служебная функция для обработки несоответствия размеров
-def _process_mismatch(
- abscissa: list[float], ordinates: list[float],
- mismatch_strategy: MismatchStrategies = MismatchStrategies.FALL
-) -> tuple[list[float], list[float]]:
- global event_logger
-
- # ваш код
- # эту строчку можно менять
- return [], []
-
-
-# служебная функция для получения статистик
-def _get_lsm_statistics(
- abscissa: list[float], ordinates: list[float]
-) -> LSMStatistics:
- global event_logger, PRECISION
-
- # ваш код
- # эту строчку можно менять
- return LSMStatistics(
- abscissa_mean=0,
- ordinate_mean=0,
- product_mean=0,
- abs_squared_mean=0
- )
-
-
-# служебная функция для получения описания МНК
-def _get_lsm_description(
- abscissa: list[float], ordinates: list[float]
-) -> LSMDescription:
- global event_logger, PRECISION
-
- # ваш код
- # эту строчку можно менять
- return LSMDescription(
- incline=0,
- shift=0,
- incline_error=0,
- shift_error=0
- )
diff --git a/homeworks/hw1/lsm_project/lsm/models.py b/homeworks/hw1/lsm_project/lsm/models.py
deleted file mode 100644
index b66244b1..00000000
--- a/homeworks/hw1/lsm_project/lsm/models.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from dataclasses import dataclass
-
-
-# структура данных для описания результатов МНК
-@dataclass
-class LSMDescription:
- incline: float # коэффициент наклона
- shift: float # смещение
- incline_error: float # СКО коэффициента наклона
- shift_error: float # СКО смещения
-
-
-# структура данных для описания статистик МНК
-@dataclass
-class LSMStatistics:
- abscissa_mean: float # среднее значение абсциссы
- ordinate_mean: float # среднее значение ординаты
- product_mean: float # среднее значение произведения
- abs_squared_mean: float # среднее значения квадрата абсциссы
-
-
-# структура данных для описания расчитанных линий
-@dataclass
-class LSMLines:
- abscissa: list[float] # абсциссы
- ordinates: list[float] # ординаты
- line_predicted: list[float] # ординаты полученного решения
- line_above: list[float] # ординаты верхней границы коридора ошибок
- line_under: list[float] # ординаты нижней границы коридора ошибок
diff --git a/homeworks/hw1/lsm_project/visualization.py b/homeworks/hw1/lsm_project/visualization.py
deleted file mode 100644
index a4ca19be..00000000
--- a/homeworks/hw1/lsm_project/visualization.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""
-В этом модуле хранятся утилиты для визуализации
-"""
-
-
-import contextlib
-
-import matplotlib.pyplot as plt
-
-from lsm_project.lsm.models import LSMLines
-
-
-@contextlib.contextmanager
-def switch_to_ggplot() -> None:
- """
- Контекстный менеджер для смены стиля на ggplot
- """
-
- plt.style.use('ggplot')
-
- try:
- yield
-
- finally:
- plt.style.use('default')
-
-
-def visualize_lines(lines: LSMLines, path_to_save='lsm.png') -> None:
- """
- Функция для визуализации МНК и сохранения полученной картинки
-
- :param: lines - объект с описанием линий
- :param: path_to_save - путь для сохранения изображения
- """
-
- _, ax = plt.subplots(figsize=(16, 9))
-
- color_main, color_support = 'orangered', 'lightsalmon'
-
- ax.scatter(
- lines.abscissa, lines.ordinates, c=color_support,
- s=20, label='experiment data'
- )
-
- ax.plot(
- lines.abscissa, lines.line_predicted, c=color_main,
- label='predicted line'
- )
- ax.plot(
- lines.abscissa, lines.line_above, c=color_main,
- linestyle='--', label='error'
- )
- ax.plot(
- lines.abscissa, lines.line_under, c=color_main,
- linestyle='--'
- )
-
- ax.set_xlim(min(lines.abscissa), max(lines.abscissa))
-
- ax.legend()
- ax.grid(True)
-
- plt.savefig(path_to_save)
diff --git a/homeworks/hw1/main.py b/homeworks/hw1/main.py
deleted file mode 100644
index 1c146042..00000000
--- a/homeworks/hw1/main.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import json
-import os
-
-from lsm_project.lsm.functions import get_lsm_lines, get_lsm_description, get_report
-from lsm_project.visualization import switch_to_ggplot, visualize_lines
-
-
-if __name__ == '__main__':
- path_to_data = os.path.join('.', 'measurments.json')
-
- # загружаем данные эксперимента из json-файла
- with open(path_to_data, 'r') as file:
- measurments: dict = json.load(file)
- abscissa = measurments.get('abscissa', [])
- ordinates = measurments.get('ordinates', [])
-
- # получаем зависимость с помощью МНК
- lsm_description = get_lsm_description(abscissa, ordinates)
- lines = get_lsm_lines(abscissa, ordinates, )
-
- # визуализируем результаты
- with switch_to_ggplot():
- visualize_lines(lines)
-
- # сохраняем отчет в текстовый файл
- get_report(lsm_description, 'report.txt')
diff --git a/homeworks/hw1/measurments.json b/homeworks/hw1/measurments.json
deleted file mode 100644
index edd4c404..00000000
--- a/homeworks/hw1/measurments.json
+++ /dev/null
@@ -1,206 +0,0 @@
-{
- "abscissa": [
- 0.0,
- 0.10101010101010101,
- 0.20202020202020202,
- 0.30303030303030304,
- 0.40404040404040403,
- 0.5050505050505051,
- 0.6060606060606061,
- 0.7070707070707071,
- 0.8080808080808081,
- 0.9090909090909091,
- 1.0101010101010102,
- 1.1111111111111112,
- 1.2121212121212122,
- 1.3131313131313131,
- 1.4141414141414141,
- 1.5151515151515151,
- 1.6161616161616161,
- 1.7171717171717171,
- 1.8181818181818181,
- 1.9191919191919191,
- 2.0202020202020203,
- 2.121212121212121,
- 2.2222222222222223,
- 2.323232323232323,
- 2.4242424242424243,
- 2.525252525252525,
- 2.6262626262626263,
- 2.727272727272727,
- 2.8282828282828283,
- 2.929292929292929,
- 3.0303030303030303,
- 3.131313131313131,
- 3.2323232323232323,
- 3.3333333333333335,
- 3.4343434343434343,
- 3.5353535353535355,
- 3.6363636363636362,
- 3.7373737373737375,
- 3.8383838383838382,
- 3.9393939393939394,
- 4.040404040404041,
- 4.141414141414141,
- 4.242424242424242,
- 4.343434343434343,
- 4.444444444444445,
- 4.545454545454545,
- 4.646464646464646,
- 4.747474747474747,
- 4.848484848484849,
- 4.94949494949495,
- 5.05050505050505,
- 5.151515151515151,
- 5.252525252525253,
- 5.353535353535354,
- 5.454545454545454,
- 5.555555555555555,
- 5.656565656565657,
- 5.757575757575758,
- 5.858585858585858,
- 5.959595959595959,
- 6.0606060606060606,
- 6.161616161616162,
- 6.262626262626262,
- 6.363636363636363,
- 6.4646464646464645,
- 6.565656565656566,
- 6.666666666666667,
- 6.767676767676767,
- 6.8686868686868685,
- 6.96969696969697,
- 7.070707070707071,
- 7.171717171717171,
- 7.2727272727272725,
- 7.373737373737374,
- 7.474747474747475,
- 7.575757575757575,
- 7.6767676767676765,
- 7.777777777777778,
- 7.878787878787879,
- 7.979797979797979,
- 8.080808080808081,
- 8.181818181818182,
- 8.282828282828282,
- 8.383838383838384,
- 8.484848484848484,
- 8.585858585858587,
- 8.686868686868687,
- 8.787878787878787,
- 8.88888888888889,
- 8.98989898989899,
- 9.09090909090909,
- 9.191919191919192,
- 9.292929292929292,
- 9.393939393939394,
- 9.494949494949495,
- 9.595959595959595,
- 9.696969696969697,
- 9.797979797979798,
- 9.8989898989899,
- 10.0
- ],
- "ordinates": [
- 1.2395177428269204,
- 0.7512944201114629,
- 1.55010701480048,
- 1.74585732572812,
- 2.8552246592576065,
- 4.112119877300948,
- 3.1506229204049028,
- 3.7856891183761916,
- 4.563597076455873,
- 5.767042810050336,
- 6.080766543505426,
- 6.351171365232665,
- 6.202769182666027,
- 7.122791125075468,
- 7.064149627095833,
- 9.728043427283268,
- 7.113144573052695,
- 10.909926888231663,
- 10.39178300229877,
- 10.069832705319644,
- 11.624319041656205,
- 10.485852278469377,
- 11.870935021302033,
- 13.382004975015835,
- 14.773318196664919,
- 16.541944469102727,
- 15.872309341169217,
- 13.624570191098115,
- 13.280089434886689,
- 16.286246911130583,
- 15.348169824526751,
- 16.759932711642538,
- 14.341978203950692,
- 18.302072013209447,
- 18.46878671693729,
- 17.52195112588257,
- 18.16113442285202,
- 18.91483022838616,
- 20.391259199976297,
- 20.895596910504604,
- 23.03103743046168,
- 22.17258753612072,
- 22.961694744841882,
- 23.4637629241346,
- 24.52723931846411,
- 22.35172431440725,
- 25.73672657597094,
- 24.066989369196325,
- 24.81620912076405,
- 27.026250705040553,
- 26.294193601461856,
- 26.983037375422526,
- 27.024861313374178,
- 28.15607062287881,
- 28.57059783498205,
- 28.233640360210675,
- 27.952125822270943,
- 29.632458186693697,
- 31.655083820374422,
- 31.301148644898557,
- 29.233893976737725,
- 31.46834922742015,
- 31.928113575402563,
- 32.04260201435895,
- 35.11888122515677,
- 33.50176202054842,
- 33.68695423576179,
- 34.76337177436582,
- 35.438513554382496,
- 36.12270028892861,
- 36.07203725526961,
- 37.274812010250415,
- 36.16172121853,
- 38.58167268884561,
- 38.6576914774856,
- 36.0303237989354,
- 40.36033543462993,
- 38.554480436419084,
- 41.45088264173333,
- 41.55444655703793,
- 42.08355308632591,
- 43.30934432840653,
- 42.18556786990254,
- 42.77753914977672,
- 43.34349843891769,
- 44.511135631720045,
- 45.793493068946844,
- 44.86750223307564,
- 45.654932492392824,
- 47.63976435070214,
- 47.46730951926552,
- 48.487184497495825,
- 47.6680666594154,
- 49.801818506947996,
- 48.93833138531037,
- 50.87379504976726,
- 49.908450382939364,
- 50.43525309434807,
- 49.37246795597148,
- 50.53937221822418
- ]
-}
\ No newline at end of file
diff --git a/homeworks/hw1/requirements.txt b/homeworks/hw1/requirements.txt
deleted file mode 100644
index dd1c9f0b..00000000
Binary files a/homeworks/hw1/requirements.txt and /dev/null differ
diff --git a/homeworks/hw1/tests/__init__.py b/homeworks/hw1/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/homeworks/hw1/tests/conftest.py b/homeworks/hw1/tests/conftest.py
deleted file mode 100644
index 43815499..00000000
--- a/homeworks/hw1/tests/conftest.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from unittest.mock import mock_open
-
-import pytest
-
-
-@pytest.fixture(scope='function')
-def mock_builtin_open(mocker):
- open_mock = mock_open()
-
- mocker.patch(
- 'builtins.open', open_mock
- )
-
- return open_mock
diff --git a/homeworks/hw1/tests/data.py b/homeworks/hw1/tests/data.py
deleted file mode 100644
index 6ffd0aaf..00000000
--- a/homeworks/hw1/tests/data.py
+++ /dev/null
@@ -1,377 +0,0 @@
-from lsm_project.lsm.enumerations import MismatchStrategies
-from lsm_project.lsm.models import (
- LSMDescription,
- LSMLines,
-)
-
-
-# данные для теста test_get_lsm_description_validation
-wrong_abscissa = [
- 1, [1, 2], [1, 2, 3, 'string']
-]
-wrong_ordinates = [
- None, (1, 2), [1, 2, 3, 4]
-]
-wrong_args_exceptions = [
- TypeError, ValueError, ValueError
-]
-description_wrong_ids = [
- 'invalid-type', 'invalid-len', 'invalid-elems'
-]
-
-
-# данные для теста test_get_lsm_description_missmatch
-strategies = [
- MismatchStrategies.FALL, 'wrong-strategy'
-]
-strategies_exception = [RuntimeError, ValueError]
-strategies_ids = ['fall', 'invalid']
-
-
-# данные для теста test_get_lsm_description_computitions
-abscissa = [
- -2.0,
- -1.7894736842105263,
- -1.5789473684210527,
- -1.368421052631579,
- -1.1578947368421053,
- -0.9473684210526316,
- -0.736842105263158,
- -0.5263157894736843,
- -0.3157894736842106,
- -0.10526315789473695,
- 0.10526315789473673,
- 0.3157894736842106,
- 0.5263157894736841,
- 0.7368421052631575,
- 0.9473684210526314,
- 1.1578947368421053,
- 1.3684210526315788,
- 1.5789473684210522,
- 1.789473684210526,
- 2.0
-]
-ordinates = [
- [
- -10.618265866667329,
- -9.075769196850382,
- -7.01987401354302,
- -6.875034650835992,
- -5.644335147495369,
- -4.673081021673832,
- -3.807861769466778,
- -3.2068019149772353,
- -2.3312794065871962,
- -1.0047547728270385,
- 0.9428698814607848,
- 1.5251309194711669,
- 3.0005239648942044,
- 3.787898267369031,
- 4.798481859816331,
- 6.316882876034116,
- 7.307887346978733,
- 8.375562660548871,
- 9.036896498773647,
- 9.476627357649695
- ],
- [
- 1.3817341333326714,
- 1.6610729084127764,
- 2.4538101969832966,
- 1.3354916649534823,
- 1.3030332735572632,
- 1.0111295046419575,
- 0.6131908621121701,
- -0.04890717813512968,
- -0.4365425644819323,
- -0.3731758254586167,
- 0.3112909340923644,
- -0.3696059226340968,
- -0.15737077194790022,
- -0.6331543642099141,
- -0.8857286664994583,
- -0.630485545018516,
- -0.9026389688107399,
- -1.0981215499774413,
- -1.6999456064895095,
- -2.5233726423503056
- ],
- [
- 1.3817341333326714,
- 1.8715992242022501,
- 2.874862828562244,
- 1.9670706123219033,
- 2.145138536715158,
- 2.0637610835893256,
- 1.876348756849012,
- 1.424777032391186,
- 1.247667961833857,
- 1.5215610166466464,
- 2.416554091987101,
- 1.9461835510501138,
- 2.368945017525784,
- 2.1036877410532435,
- 2.0616397545531733,
- 2.5274091918235895,
- 2.465782083820839,
- 2.480825818443611,
- 2.0895280777210163,
- 1.4766273576496944
- ],
- [
- -2.6182658666673286,
- -2.12840077579775,
- -1.125137171437756,
- -2.0329293876780965,
- -1.8548614632848421,
- -1.9362389164106741,
- -2.123651243150988,
- -2.575222967608814,
- -2.752332038166143,
- -2.4784389833533536,
- -1.5834459080128989,
- -2.053816448949886,
- -1.6310549824742162,
- -1.8963122589467567,
- -1.938360245446827,
- -1.4725908081764105,
- -1.5342179161791611,
- -1.519174181556389,
- -1.9104719222789834,
- -2.5233726423503056
- ],
- [
- -8.618265866667329,
- -7.0757691968503815,
- -5.01987401354302,
- -4.875034650835992,
- -3.6443351474953687,
- -2.6730810216738328,
- -1.8078617694667778,
- -1.2068019149772353,
- -0.331279406587196,
- 0.9952452271729616,
- 2.9428698814607848,
- 3.525130919471167,
- 5.000523964894204,
- 5.787898267369031,
- 6.798481859816331,
- 8.316882876034116,
- 9.307887346978733,
- 10.375562660548871,
- 11.036896498773647,
- 11.476627357649695
- ]
-]
-descriptions = [
- LSMDescription(
- incline=5.070,
- shift=0.016,
- incline_error=0.081,
- shift_error=0.099
- ),
- LSMDescription(
- incline=-0.930,
- shift=0.016,
- incline_error=0.081,
- shift_error=0.099
- ),
- LSMDescription(
- incline=0.070,
- shift=2.016,
- incline_error=0.081,
- shift_error=0.099
- ),
- LSMDescription(
- incline=0.070,
- shift=-1.984,
- incline_error=0.081,
- shift_error=0.099
- ),
- LSMDescription(
- incline=5.070,
- shift=2.016,
- incline_error=0.081,
- shift_error=0.099
- )
-]
-computition_ids = [
- 'incline-pos',
- 'incline-neg',
- 'shift-pos',
- 'shift-neg',
- 'incline-shift'
-]
-
-
-# данные для теста test_get_lsm_lines_description_incorrect
-descriptions_incorrect = [
- [1, 2, 3, 4], {'incline': 1, 'shift': 2}
-]
-descriptions_incorrect_ids = ['list', 'dict']
-
-
-# данные для теста test_get_lsm_lines_computition
-descriptions_correct = [
- None,
- LSMDescription(
- incline=5.070,
- shift=2.016,
- incline_error=0.081,
- shift_error=0.099
- )
-]
-lines_expected = [
- LSMLines(
- abscissa=abscissa,
- ordinates=ordinates[-1],
- line_predicted=[
- -8.124194046771436,
- -7.056848863574061,
- -5.9895036803766875,
- -4.922158497179312,
- -3.8548133139819387,
- -2.7874681307845646,
- -1.72012294758719,
- -0.6527777643898158,
- 0.41456741880755854,
- 1.481912602004933,
- 2.549257785202307,
- 3.6166029683996825,
- 4.683948151597056,
- 5.751293334794429,
- 6.818638517991804,
- 7.885983701189179,
- 8.953328884386552,
- 10.020674067583926,
- 11.0880192507813,
- 12.155364433978676
- ],
- line_above=[
- -8.188202520847563,
- -7.1037139802702,
- -6.019225439692837,
- -4.934736899115474,
- -3.85024835853811,
- -2.765759817960747,
- -1.6812712773833836,
- -0.5967827368060208,
- 0.48770580377134265,
- 1.5721943443487056,
- 2.656682884926069,
- 3.741171425503433,
- 4.825659966080796,
- 5.910148506658157,
- 6.994637047235521,
- 8.079125587812886,
- 9.163614128390247,
- 10.24810266896761,
- 11.332591209544974,
- 12.417079750122339
- ],
- line_under=[
- -8.060185572695307,
- -7.009983746877922,
- -5.959781921060538,
- -4.909580095243152,
- -3.8593782694257666,
- -2.809176443608381,
- -1.7589746177909957,
- -0.7087727919736106,
- 0.34142903384377465,
- 1.39163085966116,
- 2.4418326854785453,
- 3.4920345112959317,
- 4.542236337113316,
- 5.5924381629307,
- 6.642639988748086,
- 7.6928418145654724,
- 8.743043640382858,
- 9.793245466200242,
- 10.843447292017629,
- 11.893649117835015
- ]
- ),
- LSMLines(
- abscissa=abscissa,
- ordinates=ordinates[-1],
- line_predicted=[
- -8.124,
- -7.0566315789473695,
- -5.989263157894737,
- -4.9218947368421055,
- -3.8545263157894745,
- -2.7871578947368425,
- -1.719789473684211,
- -0.6524210526315795,
- 0.41494736842105207,
- 1.4823157894736836,
- 2.549684210526315,
- 3.617052631578948,
- 4.684421052631579,
- 5.751789473684209,
- 6.819157894736842,
- 7.8865263157894745,
- 8.953894736842106,
- 10.021263157894735,
- 11.088631578947368,
- 12.156
- ],
- line_above=[
- -8.187000000000001,
- -7.102578947368421,
- -6.018157894736843,
- -4.933736842105264,
- -3.8493157894736854,
- -2.7648947368421064,
- -1.680473684210527,
- -0.5960526315789481,
- 0.4883684210526311,
- 1.57278947368421,
- 2.6572105263157892,
- 3.741631578947369,
- 4.826052631578947,
- 5.910473684210525,
- 6.994894736842105,
- 8.079315789473686,
- 9.163736842105262,
- 10.248157894736842,
- 11.332578947368422,
- 12.417000000000002
- ],
- line_under=[
- -8.061,
- -7.010684210526316,
- -5.960368421052632,
- -4.910052631578948,
- -3.8597368421052636,
- -2.8094210526315795,
- -1.7591052631578952,
- -0.7087894736842106,
- 0.34152631578947323,
- 1.3918421052631573,
- 2.4421578947368414,
- 3.492473684210527,
- 4.54278947368421,
- 5.593105263157893,
- 6.643421052631578,
- 7.693736842105263,
- 8.744052631578947,
- 9.79436842105263,
- 10.844684210526314,
- 11.895
- ]
- )
-]
-descriptions_correct_ids = ['no-description', 'description']
-
-
-# данные для теста test_get_report
-report_expected = '\n'.join([
- 'LSM computing result'.center(100, "="), '',
- f'[INFO]: incline: {5.070:.{3}f};',
- f'[INFO]: shift: {2.016:.{3}f};',
- f'[INFO]: incline error: {0.081:.{3}f};',
- f'[INFO]: shift error: {0.099:.{3}f};',
- '', ''.center(100, "=")
-])
diff --git a/homeworks/hw1/tests/test_lsm_functions.py b/homeworks/hw1/tests/test_lsm_functions.py
deleted file mode 100644
index 1d99306d..00000000
--- a/homeworks/hw1/tests/test_lsm_functions.py
+++ /dev/null
@@ -1,125 +0,0 @@
-import pytest
-
-from pytest import approx
-
-from lsm_project.lsm.functions import (
- get_lsm_description,
- get_lsm_lines,
- get_report,
-)
-from lsm_project.lsm.models import LSMDescription, LSMLines
-
-from tests.data import (
- wrong_abscissa, wrong_ordinates,
- wrong_args_exceptions, description_wrong_ids,
- strategies, strategies_exception, strategies_ids,
- abscissa, ordinates, descriptions, computition_ids,
- descriptions_incorrect, descriptions_incorrect_ids,
- descriptions_correct, lines_expected, descriptions_correct_ids,
- report_expected
-)
-
-
-@pytest.mark.parametrize(
- 'abscissa,ordinates,exception',
- zip(
- wrong_abscissa, wrong_ordinates, wrong_args_exceptions
- ),
- ids=description_wrong_ids
-)
-def test_get_lsm_description_validation(abscissa, ordinates, exception):
- with pytest.raises(exception):
- get_lsm_description(abscissa, ordinates)
-
-
-@pytest.mark.parametrize(
- 'strategy,exception',
- zip(strategies, strategies_exception),
- ids=strategies_ids
-)
-def test_get_lsm_description_missmatch(strategy, exception):
- abscissa, ordinates = list(range(3)), list(range(4))
-
- with pytest.raises(exception):
- get_lsm_description(abscissa, ordinates, strategy)
-
-
-@pytest.mark.parametrize(
- 'ordinates,description_expected',
- zip(ordinates, descriptions),
- ids=computition_ids
-)
-def test_get_lsm_description_computitions(ordinates, description_expected):
- description = get_lsm_description(abscissa, ordinates)
- precision = 3
-
- assert isinstance(description, LSMDescription)
-
- incline = round(description.incline, precision)
- shift = round(description.shift, precision)
- incline_error = round(description.incline_error, precision)
- shift_error = round(description.shift_error, precision)
-
- assert incline == approx(description_expected.incline)
- assert shift == approx(description_expected.shift)
- assert incline_error == approx(description_expected.incline_error)
- assert shift_error == approx(description_expected.shift_error)
-
-
-@pytest.mark.parametrize(
- 'description', descriptions_incorrect,
- ids=descriptions_incorrect_ids
-)
-def test_get_lsm_lines_description_incorrect(description):
- with pytest.raises(TypeError):
- get_lsm_lines([], [], description)
-
-
-@pytest.mark.parametrize(
- 'description,lines_expected',
- zip(descriptions_correct, lines_expected),
- ids=descriptions_correct_ids
-)
-def test_get_lsm_lines_computition(description, lines_expected):
- lines = get_lsm_lines(abscissa, ordinates[-1], description)
-
- assert isinstance(lines, LSMLines)
-
- line_predicted = lines.line_predicted
- line_above = lines.line_above
- line_under = lines.line_under
-
- assert line_predicted == approx(lines_expected.line_predicted)
- assert line_above == approx(lines_expected.line_above)
- assert line_under == approx(lines_expected.line_under)
-
-
-def test_get_report():
- lsm_description = LSMDescription(
- incline=5.070,
- shift=2.016,
- incline_error=0.081,
- shift_error=0.099
- )
-
- report = get_report(lsm_description)
-
- assert report == report_expected
-
-
-def test_get_report_save_report(mock_builtin_open):
- open_mock = mock_builtin_open
-
- lsm_description = LSMDescription(
- incline=5.070,
- shift=2.016,
- incline_error=0.081,
- shift_error=0.099
- )
-
- report = get_report(lsm_description, 'report.txt')
- assert report == report_expected
-
- open_mock.assert_called_once()
- handle = open_mock()
- handle.write.assert_called_once()
diff --git a/lessons/lesson1/sem1_312/hw.py b/lessons/lesson1/sem1_312/hw.py
deleted file mode 100644
index e5112471..00000000
--- a/lessons/lesson1/sem1_312/hw.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Hello, World")
diff --git a/lessons/lesson1/sem1_313/hw.py b/lessons/lesson1/sem1_313/hw.py
deleted file mode 100644
index ec44f030..00000000
--- a/lessons/lesson1/sem1_313/hw.py
+++ /dev/null
@@ -1 +0,0 @@
-print(f"{'Hello, world!':-^80}")
\ No newline at end of file
diff --git a/lessons/lesson10/interactive_conspect.ipynb b/lessons/lesson10/interactive_conspect.ipynb
deleted file mode 100644
index c34c4ca9..00000000
--- a/lessons/lesson10/interactive_conspect.ipynb
+++ /dev/null
@@ -1,828 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# ООП: Классы и Экземпляры\n",
- "\n",
- "## Введение\n",
- "\n",
- "Прежде чем переходить к основной части данной лекции, нам необходимо поговорить об объектно-ориентированной парадигме программирования или просто об ООП. Объектно-ориентированная парадигма программирования - это подход к разработке, основанный на представлении программы в виде совокупности взаимодействующих между собой объектов. Каждый взаимодействующий объект является экземпляром некоторого класса - комбинации определенных данных и функциолнала для работы с этими данными. Классы образуют некоторую иерархию наследования. \n",
- "\n",
- "Парадигма объектно-ориентированного программирование зиждиться на 4 основных концепциях:\n",
- "- Абстракция - выделение в моделируемом предмете важного для решения конкретной задачи; в конечном счёте — контекстное понимание предмета формализуется в виде класса; в процессе описания абстракций разработчики отвечают на вопрос: какие части моделируемой сущности важны, а какие - нет?\n",
- "- Инкапсуляция - концепция для быстрой и безопасной организации иерархической управляемости объектом; инкапсулируя ту или иную информацию разработчик обычно отвечает на вопрос: какая информация является ключевой, а какая - подробностями?\n",
- "- Наследование - быстрая и безопасная реализация родственных понятий; наследование позволяет сосредоточиться на добавляемых изменениях и при этом избежать дублирования кода; разрабатывая иерархию классов разработчик отвечает на вопросы: в каких отношениях состоят эти классы: какой класс родительский, а какой - дочерний?\n",
- "- Полиморфизм - определение точки, в которой единое управление лучше распараллелить или наоборот — собрать воедино; отвечает на вопрос: что должно быть единым, а что множественным? \n",
- "\n",
- "По мере возможностей, мы будем стараться рассмотреть все эти концепции в языке программирования Python. Ведь формально, Python является объектно-ориентированным языком программирования. Однако, в отличие от прочих объектно-ориентированных языков программирования, Python не навязывает вам одну конкретную парадигму. Вы вполне можете заниматься, например, научными вычислениями без использования классов и принципов ООП, и чувствовать себя вполне комфортно. Однако знание о данном инструменте и понимание плюсов от его использования в конкретной ситуации может оказаться очень полезным."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Классы\n",
- "\n",
- "Рассматривая замыкания в одной из предыдущих лекций, мы освоили один из возможный вариантов объединения состояния с функциональностью. Однако данный подход не являться настолько удобным, гибким и ортодоксальным, как классы. Классы - это очень удобный способ хранения данных и ограничения круга возможных операций, применимых к этим данным, поэтому они могут оказаться весьма полезными при решении ряда задача. \n",
- "\n",
- "Обычно в теории ООП классом называют определенный пользователем тип данных, поддерживающий создание экземпляров по ходу выполнения программы. Существенно осознавать разницу между классом и экземпляром и не отождествлять их. О классе можно думать, как о некотором предикате - описании объектов, которые могут считаться экземплярами данного класса, описание допустимых данных, для хранения, а также допустимых операций. Экземпляр же - это конкретная реализация данного класса, который хранит конкретные данные, удовлетворяющие описанным в классе условиям, и поддерживающий только те операции, которые были описаны классом. От одного класса может быть создано большое количество экземпляров. \n",
- "\n",
- "Понимание различий между классами и экземплярами существенно важно не только в теоретическом плане, но и в практическом, поскольку экземпляры и объекты в Python - это полноправные объекты. Давайте начнем наше знакомство с этими объектами с классов. \n",
- "\n",
- "### Ключевое слово class\n",
- "\n",
- "В Python существует встроенный механизм создания новых пользовательских типов данных - классов. В общем виде он может быть описан следующим образом: \n",
- "\n",
- "```python\n",
- "class Classname(base_classes):\n",
- " statements\n",
- "```\n",
- "\n",
- "Давайте разберем эту конструкцию, представляющую собой составное утверждение, состоящее из одного положения, подробнее.\n",
- "\n",
- "- `class` - ключевое слово, которое сигнализирует интерпретатору о желании пользователя создать новый класс (более точным переводом аналог англ. *class object* было бы класс-объект, но для простоты повествования, поставим знак равенство между классом и классом-объектом); \n",
- "- `Classname` - идентефикатор нашего класса; в момент выполнения class-statement интерпретатор создает специальный объект, описывающий наш класс, с которым будет связан указанный идентефикатор; для дальнейшего использования данного класса пользователю необходимо будет обращаться к классу именно по этому идентефикатору; отдельное внимание обращаю на тот момент, что в отличие от идентификаторов всех, виданных нами до данного момента, сущностей, идентефикаторы классов записываются не в snake_case, а в PascalCase; это не какие-то особенности синтаксиса, а соглашение в сообществе python-разработчиков для упрощения чтения кода и понимания, является ли данный объект классом или нет; формально нарушение данного соглашения не является ошибкой с позиции интерпретатора; \n",
- "- `base_classes` - разделенные запятыми выражения, результатами выполнения которых являются классы, которые в данном котексте называются базовыми классами или родительскими классами - классы, от которых будет происходить наследование; базовые классы указываются в скобках; при нежелании наследования от какого либо класса, base-classes может не заполняться, в таком случае определение класса будет выглядить следующим образом:\n",
- " ```python\n",
- " class Classname:\n",
- " statements\n",
- " ```\n",
- " вопросы наследования будут обсуждаться в одной из следующих лекций;\n",
- "\n",
- "- `statements` - непустая последовательность независимых утвержедний, называемая также телом класса; тело класса выполняется сразу при определении, как часть выполнения class-statement; до тех пор, пока тело класса не выполнено, определяемый класс не является созданным, а идентефикатор класса не связан ни с каким объектом; \n",
- "\n",
- "Важно отметить, что после определение класса экземпляра класса не создается. Создается именно класс, который описывает пользовательский тип данных. \n",
- "\n",
- "### Атрибуты класса\n",
- "\n",
- "Как говорилось выше, тело класса - это непустая последовательность независимых утвержедений. Формально, в тело класса можно поместить любое корректное с точки зрения синтаксиса утверждение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0\n",
- "1\n",
- "2\n",
- "3\n",
- "4\n"
- ]
- }
- ],
- "source": [
- "class AbsurdClass:\n",
- " for i in range(5):\n",
- " print(i)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако чаще всего на практике в теле класса описываются атрибуты класса, т.е. поля, ответственные за хранение некоторых данных. В роли атрибута может выступать любой объект, однако, все атрибуты класса условно можно разделить на две категории:\n",
- "\n",
- "- дескрипторы - объекты, которые определяют специальный метод `__get__`; если дескриптор также определяет специальный метод `__set__`, то такой дескриптор называется переопределяющим дескриптором; \n",
- "- все остальные объекты, которые не определяют метод `__get__`; \n",
- "\n",
- "Давайте проиллюстрируем процесс создания класса, создав простой класс точки двумерной плоскости, которая будет обладать двумя атрибутами - числами с плавающей точкой соответствующей координатам данной точки:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " abscissa: float = 0\n",
- " ordinate: float = 0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Создание атрибутов напоминает процесс привязки переменных, за тем лишь исключением, что на этот раз эта привязка осуществляется в теле класса. Помимо явной привязки к константам вы можете свободно использовать атрибуты в теле класса так, как будто это обычные переменные:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " attr1: int = 1\n",
- " attr2: int = 2\n",
- " attr3: float = attr1 / attr2 "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "По аналогии с функциями, все имена, определенные в теле класса образуют пространство имен, которое существует на протяжении всей жизни класса, т.е. на протяжении времени выполнения программы, в которой данный класс используется. Пространство имен класса сохраняется в специальном атрибуте `__dict__`, который по своей структуре похож на словарь. Ключами данного словаря выступают имена атрибутов класса, а значениями - значения, связанные с данными именами. Проиллюстрируем сказанное:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "mappingproxy({'__module__': '__main__',\n",
- " '__annotations__': {'attr1': int, 'attr2': int, 'attr3': float},\n",
- " 'attr1': 1,\n",
- " 'attr2': 2,\n",
- " 'attr3': 0.5,\n",
- " '__dict__': ,\n",
- " '__weakref__': ,\n",
- " '__doc__': None})"
- ]
- },
- "execution_count": 32,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "MyClass.__dict__"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как мы видим, помимо явно определнных нами атрибутов в этом словаре оказались и другие атрибуты, которые были определены интерпретатором неявно в момент создания класса. С большей частью из них мы знакомы на примрах с функциями. Оставшиеся атрибуты хранят служебную информацию, используемую интерпретатором, или метаинформацию, используемую средами разработки."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Методы класса\n",
- "\n",
- "Подавляющее большинство классов включают в свое тело определении функций. Функции являются атрибутами класса. Более того, объекты-функции определяют функции `__get__`, в чем можно убедиться вызвав:\n",
- "```python\n",
- "dir(func)\n",
- "```\n",
- "где func - любая функция. Т.е. функции являются дескрипторами. Атрибуты класса, которые являются функциями также называются методами. Определение функции внутри тела класса почти ничем не отличается от определения обычной функции в вашей программе. Здесь действуют все те же правила. Единственная вещь, которую не стоит забывать - это наличие обязательного первого аргумента. Все функции, определяемые в теле класса должны обязательно иметь первый обязательный аргумент, который будет передан в функцию неявно при ее вызове. В качестве этого аргумента в функцию будет передан экземпляр класса, с которым связана данная функция. Конвенционально данный аргумент называется `self`. Интерпретатору все равно на именование данного аргумента, и вы можете называть его как хотите, однако подобный стиль крайне нерекомендован.\n",
- "\n",
- "Проиллюстрируем все описанное выше простым примером:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " def hello_world(self) -> None:\n",
- " print('hello world!')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В теле методов имена атрибутов класса не могут быть использованы напрямую. Для обращения к атрибутам класса в теле метода класса необходимо указывать имя класса и через точку конкретезировать имя атрибута, к которому необходимо обратиться:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " answer: int = 42\n",
- "\n",
- " def get_answer_to_the_main_question(self) -> int:\n",
- " return MyClass.answer"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также в теле метода, вы можете обращаться к атрибутам экземпляра класса, используя похожий синтаксис, но заменив идентефикатор класса на ссылка на экземпляр этого класса:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " answer: int = 42\n",
- "\n",
- " def get_answer_to_the_main_question(self) -> int:\n",
- " return self.answer"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Обращения к атрибутам класса"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Для обращения к атрибутам класса вне данного класса необходимо указать имя класса и через точку указать имя атрибута, к которому вы хотите обратиться. \n",
- "\n",
- "Возвращаясь к примеру выше, обращение к атрибуту класса будет выглядеть следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "MyClass.answer = 42\n"
- ]
- }
- ],
- "source": [
- "print(f\"{MyClass.answer = }\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "А вот вызов методов класса без создания экземпляра класса невозможен."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [
- {
- "ename": "TypeError",
- "evalue": "MyClass.get_answer_to_the_main_question() missing 1 required positional argument: 'self'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson10\\interactive_conspect.ipynb Cell 21\u001b[0m line \u001b[0;36m1\n\u001b[1;32m----> 1\u001b[0m MyClass\u001b[39m.\u001b[39;49mget_answer_to_the_main_question()\n",
- "\u001b[1;31mTypeError\u001b[0m: MyClass.get_answer_to_the_main_question() missing 1 required positional argument: 'self'"
- ]
- }
- ],
- "source": [
- "MyClass.get_answer_to_the_main_question()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Ошибка, полученная в следствии попытки этого вызова красноречиво сообщает нам о том, что мы не передали в функцию обязательный аргумент self, который, как говорилось ранее, соответствует экземпляру функции. Для того, чтобы обойти это поведение, мы можем сделать метод статическим с помощью специального декоратора:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " answer: int = 42\n",
- "\n",
- " @staticmethod\n",
- " def get_answer_to_the_main_question() -> int:\n",
- " return MyClass.answer"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "42"
- ]
- },
- "execution_count": 39,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "MyClass.get_answer_to_the_main_question()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В этом случае все работает, как ожидалось, а сигнатура метода класса может принимать любой вид и обойтись без использования злосчастного self. Использование статических методов редко оправдано и в большенстве случаев может быть с легостью заменено на отдельную функцию вне класса. Статические методы полезны в том случае, когда функция тесно связана с логикой и бизнес-доменом класса, и немыслима без его существования, даже если технически возможна ее независимая реализация.\n",
- "\n",
- "Атрибуты класса могут быть изменены извне:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "MyClass.answer = 43\n"
- ]
- }
- ],
- "source": [
- "MyClass.answer = 43\n",
- "\n",
- "print(f\"{MyClass.answer = }\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Более того, мы можем определять атрибуты класса вне класса:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "MyClass.answer_old = 42\n"
- ]
- }
- ],
- "source": [
- "MyClass.answer_old = 42\n",
- "\n",
- "print(f\"{MyClass.answer_old = }\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако подобный стиль крайне нежелателен, поскольку обычно другие разработчики ожидают видеть определение атрибутов класса только внутри тела класса, и подобный стиль может стать значительными препятствием для понимания вашего кода. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Порядок поиска атрибутов в классе\n",
- "\n",
- "Давайте поговорим, как интерпретатор определяет значение атрибута в классе. Итак предположим, что у нас есть некий класс C и мы совершаем обращение C.name:\n",
- "\n",
- "- первое, что сделает интерпретатор - заглянет в C.\\_\\_dict\\_\\_; если имя name находится в этом словаре, а значение элемента с этим ключом - это дескриптор, то результатом выполнения C.name станет значение:\n",
- " ```python\n",
- " C.__dict__[\"name\"].__get__(...)\n",
- " ```\n",
- "- если имя name было найдено в словаре, но значение элемента с этим ключом не является дескриптором, Python вернет следующее значение:\n",
- " ```python\n",
- " C.__dict__[\"name\"]\n",
- " ```\n",
- "\n",
- "- если имя name не было найдено в словаре, интерпретор пойдет искать это имя по описанной схеме в родительских классах; если имя не будет найдено ни в одном родительском классе, то в обычно случае будет возбуждено исключение AtributeError"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Экземпляры класса\n",
- "\n",
- "### Введение\n",
- "\n",
- "Как было сказано выше, экземпляр класса - это конкретная реализация класса, которая хранит конкретные данные. Экземпляр класса - это объект, тип данных которого равен данному классу. Создание экземпляров класса напоминает вызов функции и выглядит следуюшим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " abscissa: float = 0\n",
- " ordinate: float = 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "type\n",
- "Point2D\n",
- "True\n"
- ]
- }
- ],
- "source": [
- "point = Point2D()\n",
- "\n",
- "print(type(Point2D).__name__)\n",
- "print(type(point).__name__)\n",
- "print(isinstance(point, Point2D))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В примере выше был создан экземпляр класса Point2D. Как мы видим, сам класс и экземпляр класса - это два разных объекта, которые имеют разные типы данных. Но с помощью функции `isinstance` мы можем удостовериться, что наш экземпляр класса - это объект, чей тип соответствует исходному классу."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Конструктор\n",
- "\n",
- "В примере выше по умолчанию все экзмепляры нашего класса будут меть одинаковое значение атрибутов, что убивает идею их создания. Зачем нам большое количество одинаковых объектов, если все они будут одинаковыми? Для гибкой настройки экземпляра класса в момент создания в Python существует специальный метод \\_\\_init\\_\\_, о котором можно думать, как о конструктуре:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " abscissa: float = 0\n",
- " ordinate: float = 0\n",
- "\n",
- " def __init__(\n",
- " self,\n",
- " abscissa: float = 0,\n",
- " ordinate: float = 0,\n",
- " ) -> None:\n",
- " self.abscissa = abscissa\n",
- " self.ordinate = ordinate\n",
- "\n",
- " def print_point(self) -> None:\n",
- " print(\n",
- " f\"abscissa: {self.abscissa}; \",\n",
- " f\"ordinate: {self.ordinate}\"\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "abscissa: 1; ordinate: 1\n",
- "abscissa: -1; ordinate: -1\n"
- ]
- }
- ],
- "source": [
- "point1 = Point2D(1, 1)\n",
- "point2 = Point2D(-1, -1)\n",
- "\n",
- "point1.print_point()\n",
- "point2.print_point()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Типичные конструкторы осуществляют операции привязки атрибутов экземпляра к переданным значением. Важно отметить, что конструктор связывает с переданными значениями именно атрибуты экземпляра, поскольку каждый экземпляр создает свое простраство имен, в котором и хранятся значения атрибутов данного конкретного экземпляра.\n",
- "\n",
- "Нередка ситуация, когда в конструкторе происходит что-то более сложное простой привязки атрибутов. Но если конструктор полностью состоит из данной привязки, вы можете с лекостью сократить количество однотипного кода, используя декоратор из библиотеки `dataclasses`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "metadata": {},
- "outputs": [],
- "source": [
- "from dataclasses import dataclass\n",
- "\n",
- "\n",
- "@dataclass\n",
- "class Point2D:\n",
- " abscissa: float = 0\n",
- " ordinate: float = 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Point2D(abscissa=1, ordinate=1)\n",
- "Point2D(abscissa=-1, ordinate=-1)\n"
- ]
- }
- ],
- "source": [
- "point1 = Point2D(1, 1)\n",
- "point2 = Point2D(-1, -1)\n",
- "\n",
- "print(point1)\n",
- "print(point2)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В это случае, помимо простого конструктора, за нас также реализуют функционал для строкового представления объекта. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Создание экземпляров: метод \\_\\_new\\_\\_\n",
- "\n",
- "Каждый класс имеет специальный метод **\\_\\_new\\_\\_**, который определяется неявно, или при необходимости может быть реализован в классе явным образом. Когда вы осуществляете вызов класса с целью создания нового экземпляра, первым вызывается метод \\_\\_new\\_\\_. Данный метод и создает новый объект - экземпляр нашего класса, и возвращает его как результат выполнения. В качестве аргумента этот метод принимает сам класс и данные для инициализации экземпляра. Если в классе определен метод \\_\\_init\\_\\_, то он будет вызван с переданным экземпляром класса и переданными данными инициализации после вызова функции \\_\\_new\\_\\_. Таким образом код:\n",
- "```python\n",
- "point = Point2D(1, 1)\n",
- "```\n",
- "\n",
- "Может быть представлен более подробно в виде:\n",
- "```python\n",
- "point = Point2D.__new__(Point2D, 1, 1)\n",
- "\n",
- "if isinstance(point, Point2D):\n",
- " type(point).__init__(point, 1, 1)\n",
- "```"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Атрибуты экземпляров класса"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "После того, как вы создали экземпляр класса, вы можете обращаться к его атрибутам таким же образом, как это происходило с атрибутами классов, только на этот раз имя класса заменятеся на идетефикатор экземпляра:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "point.abscissa = 3\n"
- ]
- }
- ],
- "source": [
- "point = Point2D(3, -8)\n",
- "\n",
- "print(f\"{point.abscissa = }\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также вы можете вызывать методы классов у экземпляров, причем, в данном случае могут быть вызваны как статичные методы классов, так и обычные методы:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 52,
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyClass:\n",
- " def print_hello(self) -> None:\n",
- " print(\"Hello!\")\n",
- "\n",
- " @staticmethod\n",
- " def print_main_answer() -> None:\n",
- " print(\"Main answer is 42!\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Hello!\n",
- "Main answer is 42!\n"
- ]
- }
- ],
- "source": [
- "my_class = MyClass()\n",
- "\n",
- "my_class.print_hello()\n",
- "my_class.print_main_answer()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также, как и с классами, вы можете добавлять атрибуты экземпляра динамически:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "42\n"
- ]
- },
- {
- "ename": "AttributeError",
- "evalue": "type object 'MyClass' has no attribute 'answer'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson10\\interactive_conspect.ipynb Cell 50\u001b[0m line \u001b[0;36m4\n\u001b[0;32m 1\u001b[0m my_class\u001b[39m.\u001b[39manswer \u001b[39m=\u001b[39m \u001b[39m42\u001b[39m\n\u001b[0;32m 3\u001b[0m \u001b[39mprint\u001b[39m(my_class\u001b[39m.\u001b[39manswer)\n\u001b[1;32m----> 4\u001b[0m \u001b[39mprint\u001b[39m(MyClass\u001b[39m.\u001b[39;49manswer)\n",
- "\u001b[1;31mAttributeError\u001b[0m: type object 'MyClass' has no attribute 'answer'"
- ]
- }
- ],
- "source": [
- "my_class.answer = 42\n",
- "\n",
- "print(my_class.answer)\n",
- "print(MyClass.answer)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "42\n",
- "42\n"
- ]
- }
- ],
- "source": [
- "my_class = MyClass()\n",
- "MyClass.answer = 42\n",
- "\n",
- "print(my_class.answer)\n",
- "print(MyClass.answer)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Пример ниже демонстрирует интересный факт: динамическое добавление атрибута в экземпляр класса не оказывает влияние на искходный класс, но динамическое добавление атрибута в исходный класс каким-то образом отражается на экземпляре, хотя экземпляр был создан до добавления этого атрибута. Давайте разебремся почему. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Порядок поиска атрибутов в экземпляре класса\n",
- "\n",
- "Приведенные выше пример демонстрирует следующие два факта: каждый экземпляр класса обладает собственным пространством имен, которое не имеет никакого влияние на исходный класс, в каждом экземпляре класса храниться информация о том классе, экземпляром которого он является. \n",
- "\n",
- "Продемонстрируем эти принципы:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "{}\n",
- "{'__module__': '__main__', 'print_hello': , 'print_main_answer': )>, '__dict__': , '__weakref__': , '__doc__': None, 'answer': 42}\n"
- ]
- }
- ],
- "source": [
- "print(my_class.__class__)\n",
- "print(my_class.__dict__)\n",
- "print(MyClass.__dict__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Для того, чтобы понять, почему модификация исходного класса влияет на состояние экземпляра необходимо рассмотреть порядок поиска атрибутов в экземпляре. Для этого предположим, что у нас есть класс C, есть его экземпляр c_instance и есть выражение c_instance.name. Тогда справедлив следующий порядок поиска имени name:\n",
- "\n",
- "- проверяется, есть ли перезаписываемый дескриптор с именем name в классе C; если он есть, тогда результатом выполнения будет:\n",
- " ```python\n",
- " c_instance.__class__.__dict__[\"name\"].__get__(...)\n",
- " ```\n",
- "- иначе осуществляется поиск имени name в словаре класса; если оно там присутствует, возвращается значение:\n",
- " ```python\n",
- " c_instance.__dict__[\"name\"]\n",
- " ```\n",
- "\n",
- "- если его там не найдено, то поиск нужного имени делигируется классу C, и осуществляется он по правилам, описанным в разделе выше;"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Таким образом, возвращаясь к примеру выше, с нахождением экземпляром добавленного в класс атрибута, реализовался 3 случай поиска, когда мы делигировали поиск нашему классу, а класс нашел нужно имя в своем пространстве имен. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson10/sem10_312/vector3d.py b/lessons/lesson10/sem10_312/vector3d.py
deleted file mode 100644
index 44eb9f8d..00000000
--- a/lessons/lesson10/sem10_312/vector3d.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-- __init__() - инициализация экземпляра класса. Конструирует новый объект типа Vector3D
- из трех чисел с плавающей точкой(float). По умолчанию конструирует нулевой вектор.
- Если пользователь попытается инициализировать объект нечисловыми типами,
- необходимо бросить исключение;
-- __repr__() - возвращает текстовую строку: `'Vector3D(x, y, z)'`, где x, y, z - значения компонент;
-- __abs__() - возвращает длину вектора;
-- __bool__() - возвращает True, если вектор ненулевой, иначе - False;
-- __eq__(other) - сравнивает два вектора, возвращает True, если векторы равны покомпонентно, иначе False;
-- __neg__() - возвращает новый объект типа Vector3D, компоненты которого равны компонентам данного вектора,
- домноженным на минус единицу;
-- __add__(other) - складывает два вектора, возвращает новый объект типа Vector3D - сумму;
-- __sub__(other) - вычитает вектор other из данного вектора, возвращает новый объект типа Vector3D - разность;
-- __mul__(scalar) - умножение вектора на скаляр слева, возвращает новый объект типа Vector3D - произведение;
-- __rmul__(scalar) - умножение вектора на скаляр справа, возвращает новый объект типа Vector3D - произведение;
-- __truediv__(scalar) - деление вектора на скаляр, возвращает новый объект типа Vector3D - частное;
-- dot(other) - возвращает результат скалярного произведения;
-- cross(other) - возвращает векторное произведение между векторами;
-
-"""
-from typing import Generator, Any
-
-
-class Vector3D:
- _x: float
- _y: float
- _z: float
-
- def __init__(
- self,
- x: float = 0,
- y: float = 0,
- z: float =0
- ) -> None:
- pass
-
- def __iter__(self) -> Generator[float, None, None]:
- pass
-
- def __repr__(self) -> str:
- pass
-
- def __abs__(self) -> float:
- pass
-
- def __bool__(self) -> bool:
- pass
-
- def __eq__(self, other: Any) -> bool:
- pass
-
- def __neg__(self):
- pass
-
- def __add__(self, other):
- pass
-
- def __sub__(self, other):
- pass
-
- def __mul__(self, scalar: float):
- pass
-
- def __rmul__(self, scalar: float):
- pass
-
- def __truediv__(self, scalar):
- pass
-
- def dot(self, other) -> float:
- pass
-
- def cross(self, other):
- pass
-
- @property
- def x(self) -> float:
- pass
-
- @property
- def y(self) -> float:
- pass
-
- @property
- def z(self) -> float:
- pass
\ No newline at end of file
diff --git a/lessons/lesson10/sem10_313/vector3d.py b/lessons/lesson10/sem10_313/vector3d.py
deleted file mode 100644
index 44eb9f8d..00000000
--- a/lessons/lesson10/sem10_313/vector3d.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-- __init__() - инициализация экземпляра класса. Конструирует новый объект типа Vector3D
- из трех чисел с плавающей точкой(float). По умолчанию конструирует нулевой вектор.
- Если пользователь попытается инициализировать объект нечисловыми типами,
- необходимо бросить исключение;
-- __repr__() - возвращает текстовую строку: `'Vector3D(x, y, z)'`, где x, y, z - значения компонент;
-- __abs__() - возвращает длину вектора;
-- __bool__() - возвращает True, если вектор ненулевой, иначе - False;
-- __eq__(other) - сравнивает два вектора, возвращает True, если векторы равны покомпонентно, иначе False;
-- __neg__() - возвращает новый объект типа Vector3D, компоненты которого равны компонентам данного вектора,
- домноженным на минус единицу;
-- __add__(other) - складывает два вектора, возвращает новый объект типа Vector3D - сумму;
-- __sub__(other) - вычитает вектор other из данного вектора, возвращает новый объект типа Vector3D - разность;
-- __mul__(scalar) - умножение вектора на скаляр слева, возвращает новый объект типа Vector3D - произведение;
-- __rmul__(scalar) - умножение вектора на скаляр справа, возвращает новый объект типа Vector3D - произведение;
-- __truediv__(scalar) - деление вектора на скаляр, возвращает новый объект типа Vector3D - частное;
-- dot(other) - возвращает результат скалярного произведения;
-- cross(other) - возвращает векторное произведение между векторами;
-
-"""
-from typing import Generator, Any
-
-
-class Vector3D:
- _x: float
- _y: float
- _z: float
-
- def __init__(
- self,
- x: float = 0,
- y: float = 0,
- z: float =0
- ) -> None:
- pass
-
- def __iter__(self) -> Generator[float, None, None]:
- pass
-
- def __repr__(self) -> str:
- pass
-
- def __abs__(self) -> float:
- pass
-
- def __bool__(self) -> bool:
- pass
-
- def __eq__(self, other: Any) -> bool:
- pass
-
- def __neg__(self):
- pass
-
- def __add__(self, other):
- pass
-
- def __sub__(self, other):
- pass
-
- def __mul__(self, scalar: float):
- pass
-
- def __rmul__(self, scalar: float):
- pass
-
- def __truediv__(self, scalar):
- pass
-
- def dot(self, other) -> float:
- pass
-
- def cross(self, other):
- pass
-
- @property
- def x(self) -> float:
- pass
-
- @property
- def y(self) -> float:
- pass
-
- @property
- def z(self) -> float:
- pass
\ No newline at end of file
diff --git a/lessons/lesson10/sem10_314/vector3d.py b/lessons/lesson10/sem10_314/vector3d.py
deleted file mode 100644
index 44eb9f8d..00000000
--- a/lessons/lesson10/sem10_314/vector3d.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-- __init__() - инициализация экземпляра класса. Конструирует новый объект типа Vector3D
- из трех чисел с плавающей точкой(float). По умолчанию конструирует нулевой вектор.
- Если пользователь попытается инициализировать объект нечисловыми типами,
- необходимо бросить исключение;
-- __repr__() - возвращает текстовую строку: `'Vector3D(x, y, z)'`, где x, y, z - значения компонент;
-- __abs__() - возвращает длину вектора;
-- __bool__() - возвращает True, если вектор ненулевой, иначе - False;
-- __eq__(other) - сравнивает два вектора, возвращает True, если векторы равны покомпонентно, иначе False;
-- __neg__() - возвращает новый объект типа Vector3D, компоненты которого равны компонентам данного вектора,
- домноженным на минус единицу;
-- __add__(other) - складывает два вектора, возвращает новый объект типа Vector3D - сумму;
-- __sub__(other) - вычитает вектор other из данного вектора, возвращает новый объект типа Vector3D - разность;
-- __mul__(scalar) - умножение вектора на скаляр слева, возвращает новый объект типа Vector3D - произведение;
-- __rmul__(scalar) - умножение вектора на скаляр справа, возвращает новый объект типа Vector3D - произведение;
-- __truediv__(scalar) - деление вектора на скаляр, возвращает новый объект типа Vector3D - частное;
-- dot(other) - возвращает результат скалярного произведения;
-- cross(other) - возвращает векторное произведение между векторами;
-
-"""
-from typing import Generator, Any
-
-
-class Vector3D:
- _x: float
- _y: float
- _z: float
-
- def __init__(
- self,
- x: float = 0,
- y: float = 0,
- z: float =0
- ) -> None:
- pass
-
- def __iter__(self) -> Generator[float, None, None]:
- pass
-
- def __repr__(self) -> str:
- pass
-
- def __abs__(self) -> float:
- pass
-
- def __bool__(self) -> bool:
- pass
-
- def __eq__(self, other: Any) -> bool:
- pass
-
- def __neg__(self):
- pass
-
- def __add__(self, other):
- pass
-
- def __sub__(self, other):
- pass
-
- def __mul__(self, scalar: float):
- pass
-
- def __rmul__(self, scalar: float):
- pass
-
- def __truediv__(self, scalar):
- pass
-
- def dot(self, other) -> float:
- pass
-
- def cross(self, other):
- pass
-
- @property
- def x(self) -> float:
- pass
-
- @property
- def y(self) -> float:
- pass
-
- @property
- def z(self) -> float:
- pass
\ No newline at end of file
diff --git a/lessons/lesson11/interactive_conspect.ipynb b/lessons/lesson11/interactive_conspect.ipynb
deleted file mode 100644
index ac592e48..00000000
--- a/lessons/lesson11/interactive_conspect.ipynb
+++ /dev/null
@@ -1,965 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# ООП: Базовые концепции\n",
- "\n",
- "На [прошлой лекции](../lesson10/interactive_conspect.ipynb) мы начали изучать объектно ориентированный стиль программирования и его проявление в Python. В самом начале занятия мы затронули 4 основные концепции ООП: инкапсуляция, наследование, полиморфизм и абстракция. В сегодняшнем занятии мы постараемся рассмотреть реализацию этих концепций в Python.\n",
- "\n",
- "## Инкапсуляция\n",
- "\n",
- "Согласно концепции инкапсуляции все данные и методы объекта деляться на два основных класса: интерфейс и реализацию. Интерфейс описывает совокупность данных и операций, доступных пользователю для манипуляции над объектом. По факту в сознании пользователя объект определенного типа данных полностью описывается и определяется его интерфейсом. Реализация же состоит из служебной информации и включает в себя данные и методы обеспечивающие надежное функционирование объекта, но скрытые от пользователя. В идеале пользователь не должен иметь доступ к реализации, а все его взаимодействие с объектом должно выстраиваться через использование методов и данных, предоставляемых интерфейсом. \n",
- "\n",
- "В таких объектно-ориентированных языках программирования, как С++ инкапсуляция реализуется с помощтю спецификаторов доступа, типа private, shared или public. В Python подобных механизмов нет. По умолчанию, вы можете получить доступ абсолютно к любому полю класса. Однако, в Python существуют специальные соглашения об именовании атрибутов, чтобы разработчики могли понимать, какая часть функционала вашего объекта задумывалась как интерфейс, а какая - как реализация. Согласно данному соглашению, атрибуты класса поддерживают три типа именования.\n",
- "\n",
- "**Первый тип** \n",
- "\n",
- "Имя атрибута не содержит ведущих нижних подчеркиваний. В данном случае атрибут является публичным и входит в состав интерфейса. Вы можете смело обращаться к нему и, если класс не определяет иного, перезаписывать этот атрибут в своих приложениях. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " abscissa: float = 0\n",
- " ordinate: float = 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'__module__': '__main__', '__annotations__': {'abscissa': , 'ordinate': }, 'abscissa': 0, 'ordinate': 0, '__dict__': , '__weakref__': , '__doc__': None}\n",
- "point.abscissa = 0; point.ordinate = 0;\n",
- "point.abscissa = 6; point.ordinate = 0;\n"
- ]
- }
- ],
- "source": [
- "point = Point2D()\n",
- "print(Point2D.__dict__)\n",
- "\n",
- "print(f'{point.abscissa = }; {point.ordinate = };')\n",
- "point.abscissa = 6\n",
- "print(f'{point.abscissa = }; {point.ordinate = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Второй тип** \n",
- " \n",
- "Имя атрибута содержит одно нижнее подчеркивание в качестве первого символа. В этом случае вы можете думать об этом атрибуте, как о shared-атрибуте. Т.е. атрибут является деталью реализации. Предполагается, что такой атрибут может быть использован при написании любых методов данного класса. Также вы можете использовать его в дочерних классах для реализации определнного функционала. Но как пользователь обращаться напрямую к этому методу вы не должны. Однако за прямое обращение вы не получите никаких санкций со стороны интерпретатора. Максимум - предупреждение в достатоно интеллектуальных IDE. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " _abscissa: float = 0\n",
- " _ordinate: float = 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'__module__': '__main__', '__annotations__': {'_abscissa': , '_ordinate': }, '_abscissa': 0, '_ordinate': 0, '__dict__': , '__weakref__': , '__doc__': None}\n",
- "point._abscissa = 0; point._ordinate = 0;\n",
- "point._abscissa = 6; point._ordinate = 0;\n"
- ]
- }
- ],
- "source": [
- "point = Point2D()\n",
- "print(Point2D.__dict__)\n",
- "\n",
- "print(f'{point._abscissa = }; {point._ordinate = };')\n",
- "point._abscissa = 6\n",
- "print(f'{point._abscissa = }; {point._ordinate = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Третий тип**\n",
- "\n",
- "Имя атрибута содержит два ведущих нижних подчеркивания. В этом случае такой атрибут считается полностью приватным и используется только в реализации данного класса. Вы не сможете обратиться к нему напрямую вне тела данного класса. Обращение к приватному атрибуту по его имени в пользовательском коде или в теле дочернего класса приведет к ошибке. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Point2D:\n",
- " __abscissa: float = 0\n",
- " __ordinate: float = 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'__module__': '__main__', '__annotations__': {'_Point2D__abscissa': , '_Point2D__ordinate': }, '_Point2D__abscissa': 0, '_Point2D__ordinate': 0, '__dict__': , '__weakref__': , '__doc__': None}\n"
- ]
- },
- {
- "ename": "AttributeError",
- "evalue": "'Point2D' object has no attribute '__abscissa'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson11\\interactive_conspect.ipynb Cell 9\u001b[0m line \u001b[0;36m4\n\u001b[0;32m 1\u001b[0m point \u001b[39m=\u001b[39m Point2D()\n\u001b[0;32m 2\u001b[0m \u001b[39mprint\u001b[39m(Point2D\u001b[39m.\u001b[39m\u001b[39m__dict__\u001b[39m)\n\u001b[1;32m----> 4\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00mpoint\u001b[39m.\u001b[39;49m__abscissa\u001b[39m \u001b[39m\u001b[39m= }\u001b[39;00m\u001b[39m; \u001b[39m\u001b[39m{\u001b[39;00mpoint\u001b[39m.\u001b[39m__ordinate\u001b[39m \u001b[39m\u001b[39m= }\u001b[39;00m\u001b[39m;\u001b[39m\u001b[39m'\u001b[39m)\n",
- "\u001b[1;31mAttributeError\u001b[0m: 'Point2D' object has no attribute '__abscissa'"
- ]
- }
- ],
- "source": [
- "point = Point2D()\n",
- "print(Point2D.__dict__)\n",
- "\n",
- "print(f'{point.__abscissa = }; {point.__ordinate = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Такие атрибуты называются чисто служебными. Детальное изучения атрибута \\_\\_dict\\_\\_ класса Point2D, код которого приведен в примере выше, показывает, что в момент определения класса, интерпретатор Python неявным образом подменяет имя чисто служебных атрибутов и сохраняет их в пространство имен класса под именем \\_Classname\\_\\_atributename. Т.е., зная эту деталь, мы все же можем получить доступ к данным атрибутам, минуя препятствия, созданные интерпретатором: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'__module__': '__main__', '__annotations__': {'_Point2D__abscissa': , '_Point2D__ordinate': }, '_Point2D__abscissa': 0, '_Point2D__ordinate': 0, '__dict__': , '__weakref__': , '__doc__': None}\n",
- "point._Point2D__abscissa = 0; point._Point2D__ordinate = 0;\n",
- "point._Point2D__abscissa = 6; point._Point2D__ordinate = 0;\n"
- ]
- }
- ],
- "source": [
- "point = Point2D()\n",
- "print(Point2D.__dict__)\n",
- "\n",
- "print(f'{point._Point2D__abscissa = }; {point._Point2D__ordinate = };')\n",
- "point._Point2D__abscissa = 6\n",
- "print(f'{point._Point2D__abscissa = }; {point._Point2D__ordinate = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### property\n",
- "\n",
- "Встроенный декоратор **property** позволяет добавить в ваш класс дескриптор, ограничивающий доступ к тому или иному атрибуту через интерфейс. Стоит отметить, что property не решает проблему доступа полностью, но лишь позволяет описать интерфейс взаимодействия с тем или иным атрибутом. Так, например, с помощью property мы можем добавить в интерфейс нашего класса поля, доступные исключительно для чтения:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "from dataclasses import dataclass\n",
- "from typing import Any, Generator\n",
- "\n",
- "\n",
- "@dataclass\n",
- "class Vector2D:\n",
- " _x: float = 0\n",
- " _y: float = 0\n",
- "\n",
- " def __iter__(self) -> Generator:\n",
- " yield from (self._x, self._y)\n",
- "\n",
- " def __abs__(self) -> float:\n",
- " return sum(x_i ** 2 for x_i in self) ** 0.5\n",
- "\n",
- " @property\n",
- " def x(self) -> float:\n",
- " return self._x\n",
- "\n",
- " @property\n",
- " def y(self) -> float:\n",
- " return self._y"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "vector.x = 2; vector.y = 7\n"
- ]
- },
- {
- "ename": "AttributeError",
- "evalue": "property 'x' of 'Vector2D' object has no setter",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson11\\interactive_conspect.ipynb Cell 15\u001b[0m line \u001b[0;36m4\n\u001b[0;32m 1\u001b[0m vector \u001b[39m=\u001b[39m Vector2D(\u001b[39m2\u001b[39m, \u001b[39m7\u001b[39m)\n\u001b[0;32m 3\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00mvector\u001b[39m.\u001b[39mx\u001b[39m \u001b[39m\u001b[39m= }\u001b[39;00m\u001b[39m; \u001b[39m\u001b[39m{\u001b[39;00mvector\u001b[39m.\u001b[39my\u001b[39m \u001b[39m\u001b[39m= }\u001b[39;00m\u001b[39m'\u001b[39m)\n\u001b[1;32m----> 4\u001b[0m vector\u001b[39m.\u001b[39;49mx \u001b[39m=\u001b[39m \u001b[39m-\u001b[39m\u001b[39m2\u001b[39m\n",
- "\u001b[1;31mAttributeError\u001b[0m: property 'x' of 'Vector2D' object has no setter"
- ]
- }
- ],
- "source": [
- "vector = Vector2D(2, 7)\n",
- "\n",
- "print(f'{vector.x = }; {vector.y = }')\n",
- "vector.x = -2"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "При необходимости, с помощью propery мы можем включить перезапись значения того или иного атрибута в интерфейс:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "from dataclasses import dataclass\n",
- "from typing import Generator\n",
- "\n",
- "\n",
- "@dataclass\n",
- "class Vector2D:\n",
- " _x: float = 0\n",
- " _y: float = 0\n",
- "\n",
- " def __iter__(self) -> Generator:\n",
- " yield from (self._x, self._y)\n",
- "\n",
- " def __abs__(self) -> float:\n",
- " return sum(x_i ** 2 for x_i in self) ** 0.5\n",
- "\n",
- " @property\n",
- " def x(self) -> float:\n",
- " return self._x\n",
- " \n",
- " @x.setter\n",
- " def x(self, x_new: float) -> None:\n",
- " self._x = float(x_new)\n",
- "\n",
- " @property\n",
- " def y(self) -> float:\n",
- " return self._y"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "vector.x = 2; vector.y = 7\n",
- "vector.x = -2.0; vector.y = 7\n"
- ]
- }
- ],
- "source": [
- "vector = Vector2D(2, 7)\n",
- "\n",
- "print(f'{vector.x = }; {vector.y = }')\n",
- "vector.x = -2\n",
- "print(f'{vector.x = }; {vector.y = }')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако, как было сказано ранее, property не решает проблему полной инкапсуляции. Несмотря на возможность ограничения манипуляции с атрибутами через интерфейс, мы по-прежнему имеем доступ к реализации:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "vector._x = -2.0\n",
- "vector._x = 2\n"
- ]
- }
- ],
- "source": [
- "print(f'{vector._x = }')\n",
- "vector._x = 2\n",
- "print(f'{vector._x = }')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Наследование\n",
- "\n",
- "Концепция наследования позволяет сформулировать четкую иерархуию классов в вашем коде. Помимо этого, вы можете думать о наследовании, как о расширении функционала существующего класса и о средстве, позволяющим избежать дублирования кода. Классы, от которых происходит наследование, обычно называются родительскими классами, или суперклассами. В Python принят второй вариант наименования. Классы-наследники называются дочерними классами или сабклассами, в Python принят второй вариант именования.\n",
- "\n",
- "Как мы помним из предыдущей лекции, суперклассы указываются в момент создания нового класса. Синтаксически наследование выглядит следующим образом:\n",
- "\n",
- "```python\n",
- "class Derived(base-classes):\n",
- " ...\n",
- "```\n",
- "\n",
- "В данном псевдокоде, `base-classes` - это последовательность выражений, разделенных запятыми. Результаты вычисления этих выражений - классы."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### MRO\n",
- "\n",
- "Из факта того, что `base-classes` - это последовательность выражений, можно сделать вывод о допустимости множественного наследования в Python, т.е. наш класс может расширять одновременно несколько классов и приходятся сабклассом одноврменное для нескольких суперклассов. Однако возможность множественного наследования ведет к большому количеству проблем. Самая популярная проблема - это, так называемая проблема ромба (англ. *diamond problem*).\n",
- "\n",
- "Проблема ромба заключается в следующем: предположим у нас есть некоторый класс, который обладает некоторым методом. Для конкретики представим, что имеется класс `Parallelogram`, у которого есть метод `area`. У данного супер класса есть два сабкласса `Rectangle` и `Rhombus`, которые переопределяют метод area исходного класса. Также есть класс `Square`, который является сабклассом и для класса `Rectangle`, и для класса `Rhombus`. Мы создаем экземпляр класса Square и вызываем у него метод area, метод какого класса будет вызван? "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Parallelogram:\n",
- " def area(self) -> None:\n",
- " print('parallelogram area')\n",
- "\n",
- "\n",
- "class Rectangle(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rectangle area')\n",
- "\n",
- "\n",
- "class Rhombus(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rhombus area')\n",
- "\n",
- "\n",
- "class Square(Rectangle, Rhombus):\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "rectangle area\n"
- ]
- }
- ],
- "source": [
- "square = Square()\n",
- "square.area()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Данный пример иллюстрирует две вещи: первая вещь - возможность переопределять атрибуты суперкласса в сабклассе, об этой возможности мы поговорим чуть позже; вторая вещь - это стратегия для решения проблемы ромба в Python. Как мы видим, в качестве метода area наш класс Square использует метод класса Rectangle. Но почему?\n",
- "\n",
- "В момент определения класса Python сохраняет весь граф наследования в специальный атрибут, который называется \\_\\_mro\\_\\_. Это сокращение от английского method resolution order, т.е. порядок разрешения методов. \\_\\_mro\\_\\_ - это специальный атрибут класса, в котором хранится порядок обхода графа наследования для поиска того или иного атрибута. Сам алгоритм поиска мы обсуждали на предыдущей лекции. Давайте посмотрим на порядок обхода классов для поиска аттрибута и освежим в памяти этот самый алгоритм поиска. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "(, , , , )\n"
- ]
- }
- ],
- "source": [
- "print(Square.__mro__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Итак, при попытке вызвать метод area у экземпляра класса Square, интерпретатор пытается найти переопределяющий дескрипток в классе Square. Поскольку класс Square не содержит в себе подобного дескриптора, интерпретатор пытается найти имя area в атрибуте \\_\\_dict\\_\\_ экземпляра класса Square. Но данный атрибут пуст. Тогда интерпретатор пытается найти указанное имя в атрибуте \\_\\_dict\\_\\_ самого класса Square, но и этот атрибут не содержит нужного имени. Тогда интерпретатор начинает поиск в следующем элементе кортежа \\_\\_mro\\_\\_, т.е. в классе Rectangle. Атрибут \\_\\_dict\\_\\_ данного класса содержит искомое имя, интерпретатор использует найденное в Rectangle значение для имени area в качестве результата поиска, а сам поиск на этом завершается.\n",
- "\n",
- "Но почему классы в атрибуте \\_\\_mro\\_\\_ расположены именно в такой последовательности? И что в этом кортеже делает класс `object`? Ответ на первый вопрос достаточно сложный, однако, если упростить алгоритм обхода графа наследования, то он будет представлять из себя следующую конструкцию. Чем ближе суперклассы к сабклассу в графе наследования, тем левее они будут находится в кортеже \\_\\_mro\\_\\_, более того, при множественном наследовании, классы, указанные левее, будут располагаться левее и в \\_\\_mro\\_\\_. Собственно, это явно и илюстрирует полученный кортеж. Классы, от которых мы наследовались напрямую, расположены ближе к левой границе массива. При этом, порядок их расположения соответствует порядку из следования в области наследования при определении нашего класса Square.\n",
- "\n",
- "Ответ на второй вопрос состоит в следующем: даже при отсутствии наследования, все объекты Python неявно наследуются от встроенного типа данных object, который описывает объекты Python."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### super()\n",
- "\n",
- "Предположим, что в предыдущем примере мы бы хотели переопределить функцию area в классе Square. Однако наша переопределенная функция не должна перекрывать функцию суперкласса, но лишь дополнять ее поведение. Как нам быть в этом случае? Вообще, ничего не мешает нам вызвать нужную функцию, явно указав базовый класс, чей метод мы хотели бы модифицировать. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Parallelogram:\n",
- " def area(self) -> None:\n",
- " print('parallelogram area')\n",
- "\n",
- "\n",
- "class Rectangle(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rectangle area')\n",
- "\n",
- "\n",
- "class Rhombus(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rhombus area')\n",
- "\n",
- "\n",
- "class Square(Rectangle, Rhombus):\n",
- " def area(self) -> None:\n",
- " Rhombus.area(self)\n",
- " print('square area')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "rhombus area\n",
- "square area\n"
- ]
- }
- ],
- "source": [
- "square = Square()\n",
- "square.area()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако, в этом случае нам приходится держать в голове порядок обхода графа наследования, чтобы точно знать, у какого именно класса нам необходимо вызвать нужный метод. Чтобы избежать этой головной боли мы можем использовать встроенный объект `super()`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Parallelogram:\n",
- " def area(self) -> None:\n",
- " print('parallelogram area')\n",
- "\n",
- "\n",
- "class Rectangle(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rectangle area')\n",
- "\n",
- "\n",
- "class Rhombus(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rhombus area')\n",
- "\n",
- "\n",
- "class Square(Rectangle, Rhombus):\n",
- " def area(self) -> None:\n",
- " super().area()\n",
- " print('square area')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "rectangle area\n",
- "square area\n"
- ]
- }
- ],
- "source": [
- "square = Square()\n",
- "square.area()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Во-первых, данный подход является более элегантным. Во-вторых, данный подход требует от вас меньше умственных усилий, поскольку super осуществит поиск нужного атрибута среди суперклассов графа наследования и вернет значение первого найденного. Однако, если вам необходимо осуществить поиск конкретного атрибута в конкретном суперклассе, минуя метод разрешения методов, вы всегда можете воспользоваться явным вызовом метода класса, как это было в примере выше. \n",
- "\n",
- "Вызов super необходим при инициализации экземпляра сабкласса:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Parent:\n",
- " def __init__(self) -> None:\n",
- " print('init Parent')\n",
- "\n",
- "\n",
- "class ChildWrong(Parent):\n",
- " def __init__(self) -> None:\n",
- " print('init ChildWrong')\n",
- "\n",
- "\n",
- "class ChaildNaive(Parent):\n",
- " pass\n",
- "\n",
- "\n",
- "class ChildGood(Parent):\n",
- " def __init__(self) -> None:\n",
- " super().__init__()\n",
- " print('init ChildGood')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "init ChildWrong\n",
- "init Parent\n",
- "init Parent\n",
- "init ChildGood\n"
- ]
- }
- ],
- "source": [
- "child_wrong = ChildWrong()\n",
- "child_naive = ChaildNaive()\n",
- "child_good = ChildGood()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В данном примере показано два важных положения. Первое: если сабкласс не переопределяет метод init, то для инициализации его экземпляров будет использоваться соответствующий метод суперкласса. Второе: при переопределении метода init в сабклассе метод init суперкласса не вызывается неявным образом, его необходимо вызывать явно. Причем, если имеет место мультинаследование, то вызова super будет недостаточно, поскольку, как обсуждалось выше, super прервет выполнение найдя нужный атрибут в одном из суперклассов:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [],
- "source": [
- "class A:\n",
- " def __init__(self) -> None:\n",
- " print('init A')\n",
- "\n",
- "\n",
- "class B:\n",
- " def __init__(self) -> None:\n",
- " print('init B')\n",
- "\n",
- "\n",
- "class C(A, B):\n",
- " def __init__(self) -> None:\n",
- " super().__init__()\n",
- " print('init C')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "init A\n",
- "init C\n"
- ]
- }
- ],
- "source": [
- "c_instance = C()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Корректная реализация данного класса выглядила бы следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {},
- "outputs": [],
- "source": [
- "class A:\n",
- " def __init__(self) -> None:\n",
- " print('init A')\n",
- "\n",
- "\n",
- "class B:\n",
- " def __init__(self) -> None:\n",
- " print('init B')\n",
- "\n",
- "\n",
- "class C(A, B):\n",
- " def __init__(self) -> None:\n",
- " A.__init__(self)\n",
- " B.__init__(self)\n",
- " \n",
- " print('init C')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "init A\n",
- "init B\n",
- "init C\n"
- ]
- }
- ],
- "source": [
- "c_instance = C()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Полиморфизм\n",
- "\n",
- "Концепция полиморфизма заключается в унификации точки входа в определенную операцию для разных типов данных или комбинаций аргементов. Мы уже знакомы с полиморфизмом на примере вногих втроенных функций. Ярким примером может быть функция `len()`. Данная функция способна принимать на вход список, кортеж, словарь, любой объект, реализующий функцию \\_\\_len\\_\\_(). Т.е. это единая точка входа в операции определения размера для объектов различных типов данных.\n",
- "\n",
- "В ООП под полиморфизмом обычно понимают полиморфизм классов. Т.е. наличие в классах методов с одинаковыми сигнатурами, однако реализующими операции, исходя из логики самого класса. Для демонстрации полиморфизма классов рассмотрим один из примеров, приведенных выше."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Parallelogram:\n",
- " def area(self) -> None:\n",
- " print('parallelogram area')\n",
- "\n",
- "\n",
- "class Rectangle(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rectangle area')\n",
- "\n",
- "\n",
- "class Rhombus(Parallelogram):\n",
- " def area(self) -> None:\n",
- " print('rhombus area')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "parallelogram area\n",
- "rectangle area\n",
- "rhombus area\n"
- ]
- }
- ],
- "source": [
- "polygons = [Parallelogram(), Rectangle(), Rhombus()]\n",
- "\n",
- "for polygon in polygons:\n",
- " polygon.area()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как мы видим, в данном случае классы обладают полиморфной функцией area. Благодаря этому мы можем работать схожим образом с тремя разными экземплярами трех разных классов.\n",
- "\n",
- "Подобный полиморфизм классов приводит нас к понятию протоколов. Говоря о протаколе, мы подразумеваем, что объект реализует некоторый функционал. Например, мы знакомы с вами с протоколом последовательностей. Когда мы говорили, что объект является последовательность, мы подразумевали, что он ведет себя как последовательсноть, т.е. поддерживает определенный набор операций.\n",
- "\n",
- "Понятие протокола является неформальным, это значит, что обычно нам не приходится проверять соответствие объекта определенному типу данных. Вместо этого мы просто пытаемся работать с полученным объектом, как с представителем данного протокола. Если у нас получается обработать его нужным образом - хорошо, не получается - значит объект не удовлетворял требуемому протоколу. \n",
- "\n",
- "Ярким примером данного принципа может служить стремление интерпретатора обрабатывать объект, частично реализующий протокол последовательности, как последовательность:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Iterable, Union, Any\n",
- "\n",
- "\n",
- "class MySequence:\n",
- " _iterable: list\n",
- "\n",
- " def __init__(self, iterable: Iterable) -> None:\n",
- " self._iterable = list(iterable)\n",
- "\n",
- " def __getitem__(self, index: Union[int, slice]) -> Any:\n",
- " return self._iterable[index]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1\n",
- "2\n",
- "3\n",
- "4\n"
- ]
- }
- ],
- "source": [
- "my_seq = MySequence((1, 2, 3, 4))\n",
- "\n",
- "for elem in my_seq:\n",
- " print(elem)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Абстракция\n",
- "\n",
- "Последняя концепция ООП - это абстракция. Абстракция - это выделение в моделируемом предмете важного для решения конкретной задачи. Абстракции являются более формальными протоколами, посколько они строго определены. Если, как мы видели выше, для обработки объекта по протоколу последовательностей не требуется строгого соответствия протоколу последовательности, то при разработке абстракций, от объекта, наследующего некоторый абстрактный интерфейс, требуется четкое соответствие ему. \n",
- "\n",
- "Для реализации обстракций в коде используют абстрактные базовые классы. Это специальные классы, которые описывают четкий интерфес взаимодействия с объектами, но при этом не реализующие его. Создать экземпляр абстрактного класса невозможно. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [],
- "source": [
- "import abc\n",
- "\n",
- "\n",
- "class Polygon(abc.ABC):\n",
- " @abc.abstractmethod\n",
- " def area(self) -> float:\n",
- " ...\n",
- "\n",
- "\n",
- "class Square(Polygon):\n",
- " _side_len: float\n",
- "\n",
- " def __init__(self, side_len: float) -> None:\n",
- " self._side_len = float(side_len)\n",
- "\n",
- " def area(self) -> float:\n",
- " return self._side_len ** 2\n",
- " \n",
- " @property\n",
- " def side_len(self) -> float:\n",
- " return self._side_len\n",
- " \n",
- "\n",
- "class Rectangle(Polygon):\n",
- " _length: float\n",
- " _width: float\n",
- "\n",
- " def __init__(self, length: float, width: float) -> None:\n",
- " self._length = float(length)\n",
- " self._width = float(width)\n",
- "\n",
- " def area(self) -> float:\n",
- " return self._length * self._width\n",
- " \n",
- " @property\n",
- " def length(self) -> float:\n",
- " return self._length\n",
- " \n",
- " @property\n",
- " def width(self) -> float:\n",
- " return self._width"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "square: 16.0; rect: 15.0;\n"
- ]
- }
- ],
- "source": [
- "square = Square(4)\n",
- "rectangle = Rectangle(3, 5)\n",
- "\n",
- "print(f'square: {square.area()}; rect: {rectangle.area()};')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [
- {
- "ename": "TypeError",
- "evalue": "Can't instantiate abstract class Polygon with abstract method area",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson11\\interactive_conspect.ipynb Cell 51\u001b[0m line \u001b[0;36m1\n\u001b[1;32m----> 1\u001b[0m polygon \u001b[39m=\u001b[39m Polygon()\n",
- "\u001b[1;31mTypeError\u001b[0m: Can't instantiate abstract class Polygon with abstract method area"
- ]
- }
- ],
- "source": [
- "polygon = Polygon()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как вы видите, абстрактный базовый класс не подерживает создание экземпляров. Абстрактный базовый класс может включать как обычные методы, так и чисто виртуальные методы, как в данном примере. При этом, если абстрактный базовый класс включает в себя методы с реализацией, то реализация должна состоять только из вызовов методов описываемого интерфейса, без ограничения возможностей внутренний реализации объектов. Классы, наследующиеся от абстрактного базового класса обязаны реализовать в себе чисто виртуальные методы, иначе итерпретатор будет считать их виртуальными классами и не позволит создавать экземпляры. При этом, классы наследники могут дополнять интерфейс исходного базового класса."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Абстрактные базовые классы и чистые интерфейсы - это большая редкость в Python. Большая часть объектов реализуются как представители определнных протоколов. На практике, вам врят ли придется реализовывать свой абстрактный базовый класс."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson11/sem11_313/common/__init__.py b/lessons/lesson11/sem11_313/common/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/lessons/lesson11/sem11_313/common/log.py b/lessons/lesson11/sem11_313/common/log.py
deleted file mode 100644
index 555ae643..00000000
--- a/lessons/lesson11/sem11_313/common/log.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import logging
-import os
-
-from enum import Enum
-
-
-class Levels(Enum):
- debug = logging.DEBUG
- info = logging.INFO
- warning = logging.WARNING
- error = logging.ERROR
-
-
-class EventLogger:
- _logger: logging.Logger
-
- def __init__(
- self,
- name: str,
- level: Levels = Levels.debug,
- path_to_logs: str = ''
- ) -> None:
- if not isinstance(level, Levels):
- raise ValueError(
- f'unexpected level type: {type(level).__name__}'
- )
-
- self._logger = logging.getLogger(name)
-
- formatter = logging.Formatter(
- '[%(levelname)s]: %(name)s || %(asctime)s || %(message)s;'
- )
-
- handler_console = logging.StreamHandler()
- handler_console.setLevel(level.value)
- handler_console.setFormatter(formatter)
-
- self._logger.setLevel(level.value)
- self._logger.addHandler(handler_console)
-
- if path_to_logs:
- path_to_folder = os.path.split(path_to_logs)[0]
-
- if path_to_folder and not os.path.exists(path_to_folder):
- os.makedirs(path_to_folder)
-
- handler_file = logging.FileHandler(path_to_logs)
- handler_file.setLevel(level.value)
- handler_file.setFormatter(formatter)
-
- self._logger.addHandler(handler_file)
-
- def debug(self, message: str) -> None:
- self._logger.debug(message)
-
- def info(self, message: str) -> None:
- self._logger.info(message)
-
- def warning(self, message: str) -> None:
- self._logger.warning(message)
-
- def error(self, message: str) -> None:
- self._logger.error(message)
diff --git a/lessons/lesson11/sem11_313/demo.py b/lessons/lesson11/sem11_313/demo.py
deleted file mode 100644
index 4c00f21b..00000000
--- a/lessons/lesson11/sem11_313/demo.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import matplotlib.pyplot as plt
-
-from utils import main
-
-
-if __name__ == '__main__':
- with plt.style.context('ggplot'):
- main()
\ No newline at end of file
diff --git a/lessons/lesson11/sem11_313/regressors/__init__.py b/lessons/lesson11/sem11_313/regressors/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/lessons/lesson11/sem11_313/regressors/lsm_regressor.py b/lessons/lesson11/sem11_313/regressors/lsm_regressor.py
deleted file mode 100644
index c8534de6..00000000
--- a/lessons/lesson11/sem11_313/regressors/lsm_regressor.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from typing import Iterable, Union
-from numbers import Real
-
-from regressors.regressor_abc import RegressorABC
-
-
-class RegressorLSM(RegressorABC):
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- # ваш код
- pass
-
- def predict(self, abscissa: Union[Real, Iterable]) -> list:
- # ваш код
- return [abscissa] if isinstance(abscissa, Real) else abscissa
diff --git a/lessons/lesson11/sem11_313/regressors/nonparametric_regressor.py b/lessons/lesson11/sem11_313/regressors/nonparametric_regressor.py
deleted file mode 100644
index 95081e7e..00000000
--- a/lessons/lesson11/sem11_313/regressors/nonparametric_regressor.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from typing import Iterable, Union
-from numbers import Real
-
-from regressors.regressor_abc import RegressorABC
-
-
-class NonparametricRegressor(RegressorABC):
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- # ваш код
- pass
-
- def predict(self, abscissa: Union[Real, Iterable]) -> list:
- # ваш код
- return abscissa
-
diff --git a/lessons/lesson11/sem11_313/regressors/regressor_abc.py b/lessons/lesson11/sem11_313/regressors/regressor_abc.py
deleted file mode 100644
index 7ea23e3c..00000000
--- a/lessons/lesson11/sem11_313/regressors/regressor_abc.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import abc
-
-from typing import Iterable, Union
-from numbers import Real
-
-
-class RegressorABC(abc.ABC):
- @abc.abstractmethod
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- ...
-
- @abc.abstractmethod
- def predict(
- self, abscissa: Union[Real, Iterable]
- ) -> list:
- ...
diff --git a/lessons/lesson11/sem11_313/task_description.ipynb b/lessons/lesson11/sem11_313/task_description.ipynb
deleted file mode 100644
index cd1df854..00000000
--- a/lessons/lesson11/sem11_313/task_description.ipynb
+++ /dev/null
@@ -1,122 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "228e5039",
- "metadata": {},
- "source": [
- "# Практическое задание."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Постановка задач\n",
- "\n",
- "Вам дан абстрактный базовый класс, формализующий интерфейс универсального регрессора - `RegressorABC`. RegressorABC имеет два абстрактных метода: fit и predict. Метод fit используется для извлечения информации из экспериментальных данных, эта информация используется для построения регрессионных зависимостей. Метод predict используется для получения предсказанного алгоритмом значения в заданной точке или заданном наборе точек. Используя абстрактный базовый класс, вам необходимо реализовать два конкретных регрессионных алгоритма, наследуя интерфейс регрессора.\n",
- "\n",
- "**Задачи**:\n",
- "- Реализуйте алгоритм непараметрической регресси, используя интерфейс, описанный в классе RegressorABC - решаем вместе; \n",
- "- Реализуйте Метод наименьших квадратов, используя интерфейс, описанный в классе RegressorABC - самостоятельно; "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "20eafc42",
- "metadata": {},
- "source": [
- "## Теоретическая справка"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b4bf8c3d",
- "metadata": {},
- "source": [
- "### Общая постановка задачи регрессии\n",
- "\n",
- "Заданы некоторые множества: $X$ - множество объектов, и $Y$ - множество ответов; \n",
- "$y: X \\rightarrow Y$ - неизвестная зависимость; \n",
- "$X^l = \\{x_1, x_2, ..., x_l\\} \\subset X$ - обучающая выборка; \n",
- "$y_i = y(x_i), i = 1, l$ - известные ответы для обучающей выборки; \n",
- "\n",
- "В рамках данного задания $X = \\mathbb{R}$, $Y = \\mathbb{R}$; \n",
- "\n",
- "**Задача** состоит в том, чтобы найти алгоритм $a: X \\rightarrow Y$, приближающий $y$ на всём множестве $X$, используя обучающую выборку $(X^l, y^l)$; \n",
- "**Замечание**: $y$ - истинная зависимость, $a$ - аппроксимация.\n",
- "\n",
- "\n",
- "### Непараметрическая Регрессия\n",
- "\n",
- "В рамках непараметрической регрессии мы пытаемся приблизить значении $y(x)$ некоторой константой $\\alpha$ в некоторой окрестности точки $x$. Задача сводится к нахождению данной константы для каждого $x \\in X$.\n",
- "\n",
- "Для того, чтобы отыскать оптимальное значения данной константы, решим следующую оптимизационную задачу:\n",
- "\n",
- "$Q(\\alpha, X^l) = \\sum_{i = 1}^{l}{w_i(x)(\\alpha - y_i)^2} \\rightarrow min_{\\alpha \\in \\mathbb{R}},$ где $w_i(x) = K(\\frac{\\rho(x, x_i)}{h})$ - вес i-ого квадратичного отклонения, $K(\\frac{\\rho(x, x_i)}{h})$ - ядро, невозрастающая, ограниченная, гладкая функция, $h$ - ширина окна сглаживания. Фактически мы минимизируем взвешанную сумму квадратов отклонений известных ответов от подобранной константы по $\\alpha$.\n",
- "\n",
- "$$\n",
- "\\frac{d}{d\\alpha}Q(\\alpha, X^l) = \\sum_{i = 1}^{l}{w_i(x)(2\\alpha - 2y_i)} = 0; \n",
- "\\\\ \\sum_{i = 1}^{l}{\\alpha w_i(x)} = \\sum_{i = 1}^{l}{y_i w_i(x)} \\rightarrow \\alpha = \\frac{\\sum_{i = 1}^{l}{y_i w_i(x)}}{\\sum_{i = 1}^{l}{w_i(x)}};\n",
- "\\\\ a(x, X^l) = \\frac{\\sum_{i = 1}^{l}{y_i w_i(x)}}{\\sum_{i = 1}^{l}{w_i(x)}} = \\frac{\\sum_{i = 1}^{l}{y_i K(\\frac{\\rho(x, x_i)}{h})}}{\\sum_{i = 1}^{l}{K(\\frac{\\rho(x, x_i)}{h})}};\n",
- "$$ \n",
- "\n",
- "Таким образом мы получили аппроксимацию $a(x, X^l) = \\frac{\\sum_{i = 1}^{l}{y_i K(\\frac{\\rho(x, x_i)}{h})}}{\\sum_{i = 1}^{l}{K(\\frac{\\rho(x, x_i)}{h})}}$, которая позволяет вычислять значение функции в данной точке по имеющимся данным.\n",
- "\n",
- "В качестве ядра будем использовать так называемое [ядро Епанечникова](https://ru.wikipedia.org/wiki/%D0%AF%D0%B4%D1%80%D0%BE_(%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B0)):\n",
- "$$\n",
- "\\begin{equation*}\n",
- "K(x) = \n",
- " \\begin{cases}\n",
- " \\frac{3}{4}(1 - x^2), |x| \\le 1\\\\\n",
- " 0, |x| > 1\n",
- " \\end{cases}\n",
- "\\end{equation*}\n",
- "$$\n",
- "\n",
- "Ещё одним тонким моментом, связанным с ядром, является ширина окна $h$.Ширина окна - некоторое число, которое можно подобрать руками, или реализовать адаптивный расчет. Т.к. адаптивный расчет является более надёжным методом, не требующим долгой настройки, мы пойдём именно по этому пути. \n",
- "\n",
- "Для адаптивного расчета ширины окна необходимо вычислить расстояния от элемента $x$, для которого мы хотим осуществить предсказание, до всех элементов $x_i \\in X^l$ обучающей выборки. После чего необходимо упорядочить их по возрастанию расстояния до $x$. Из полученного вариационного ряда выберем k-ый элемент $x_k$. Расстояние $\\rho(x, x_k)$ и будет нашей шириной окна, адаптивно вычисляемой для каждого элемента $x \\in X$. Может показаться, что мы несильно упростили задачу, ведь теперь вместо выбора $h$ мы будем выбирать $k$. Но, благодаря подобным действиям мы получим лучшие результаты, ведь при данном подходе ширина окна будет своя для кажого $x$. "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "abca6c26",
- "metadata": {},
- "source": [
- "___"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "55af3f6d",
- "metadata": {},
- "source": [
- "## Дополнительные материалы.\n",
- "\n",
- "1. [Машинное обучение. Метрические методы. К.В. Воронцов, Школа анализа данных, Яндекс.](https://www.youtube.com/watch?v=mCJcRDIY4TI&list=PLJOzdkh8T5krxc4HsHbB8g8f0hu7973fK&index=3&t=2079s)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.12"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson11/sem11_313/utils.py b/lessons/lesson11/sem11_313/utils.py
deleted file mode 100644
index 855ecacc..00000000
--- a/lessons/lesson11/sem11_313/utils.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from typing import Callable, Union
-
-import matplotlib.pyplot as plt
-import numpy as np
-
-
-from regressors.nonparametric_regressor import NonparametricRegressor
-from regressors.lsm_regressor import RegressorLSM
-
-from common.log import EventLogger
-
-
-K_NEIGHBOURS = 100
-POINTS_AMOUNT = 1000
-BOUNDS = (-10, 10)
-FIGSIZE = (16, 8)
-
-
-def visualize_results(
- axis: plt.Axes,
- abscissa: list,
- ordinates: list,
- predictions: list,
-) -> None:
- axis.scatter(abscissa, ordinates, label='source', c='royalblue', s=1)
- axis.plot(abscissa, predictions, label='prediction', c='steelblue')
-
- axis.set_xlim(min(abscissa), max(abscissa))
- axis.legend()
-
-
-def get_demonstration(
- function: Callable[[np.ndarray], np.ndarray],
- regressors: list[Union[RegressorLSM, NonparametricRegressor]],
-) -> None:
- event_logger = EventLogger(function.__name__)
- event_logger.info('obtain linear function data')
-
- abscissa = np.linspace(*BOUNDS, POINTS_AMOUNT)
- ordinates = function(abscissa).tolist()
- abscissa = abscissa.tolist()
-
- event_logger.info('start fitting regressors')
-
- for regressor in regressors:
- event_logger.info(f'fit {type(regressor).__name__}')
- regressor.fit(abscissa, ordinates)
-
- event_logger.info('prepare figure')
- _, axes = plt.subplots(1, 2, figsize=(16, 8))
-
- event_logger.info('start getting predictions')
-
- for ax, regressor in zip(axes, regressors):
- event_logger.info(f'get prediction for {type(regressor).__name__}')
- predictions = regressor.predict(abscissa)
-
- ax.set_title(type(regressor).__name__, fontweight='bold')
- visualize_results(ax, abscissa, ordinates, predictions)
-
- plt.show()
-
-
-def main() -> None:
- functions = [linear, linear_modulated]
- regressors = [RegressorLSM(), NonparametricRegressor(K_NEIGHBOURS)]
-
- for function in functions:
- get_demonstration(function, regressors)
-
-
-def linear(abscissa: np.ndarray) -> np.ndarray:
- function_values = 5 * abscissa + 1
- noise = np.random.normal(size=abscissa.size)
-
- return function_values + noise
-
-
-def linear_modulated(abscissa: np.ndarray) -> np.ndarray:
- function_values = np.sin(abscissa) * abscissa
- noise = np.random.normal(size=abscissa.size)
-
- return function_values + noise
diff --git a/lessons/lesson11/sem11_314/common/__init__.py b/lessons/lesson11/sem11_314/common/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/lessons/lesson11/sem11_314/common/log.py b/lessons/lesson11/sem11_314/common/log.py
deleted file mode 100644
index 555ae643..00000000
--- a/lessons/lesson11/sem11_314/common/log.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import logging
-import os
-
-from enum import Enum
-
-
-class Levels(Enum):
- debug = logging.DEBUG
- info = logging.INFO
- warning = logging.WARNING
- error = logging.ERROR
-
-
-class EventLogger:
- _logger: logging.Logger
-
- def __init__(
- self,
- name: str,
- level: Levels = Levels.debug,
- path_to_logs: str = ''
- ) -> None:
- if not isinstance(level, Levels):
- raise ValueError(
- f'unexpected level type: {type(level).__name__}'
- )
-
- self._logger = logging.getLogger(name)
-
- formatter = logging.Formatter(
- '[%(levelname)s]: %(name)s || %(asctime)s || %(message)s;'
- )
-
- handler_console = logging.StreamHandler()
- handler_console.setLevel(level.value)
- handler_console.setFormatter(formatter)
-
- self._logger.setLevel(level.value)
- self._logger.addHandler(handler_console)
-
- if path_to_logs:
- path_to_folder = os.path.split(path_to_logs)[0]
-
- if path_to_folder and not os.path.exists(path_to_folder):
- os.makedirs(path_to_folder)
-
- handler_file = logging.FileHandler(path_to_logs)
- handler_file.setLevel(level.value)
- handler_file.setFormatter(formatter)
-
- self._logger.addHandler(handler_file)
-
- def debug(self, message: str) -> None:
- self._logger.debug(message)
-
- def info(self, message: str) -> None:
- self._logger.info(message)
-
- def warning(self, message: str) -> None:
- self._logger.warning(message)
-
- def error(self, message: str) -> None:
- self._logger.error(message)
diff --git a/lessons/lesson11/sem11_314/demo.py b/lessons/lesson11/sem11_314/demo.py
deleted file mode 100644
index 4c00f21b..00000000
--- a/lessons/lesson11/sem11_314/demo.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import matplotlib.pyplot as plt
-
-from utils import main
-
-
-if __name__ == '__main__':
- with plt.style.context('ggplot'):
- main()
\ No newline at end of file
diff --git a/lessons/lesson11/sem11_314/regressors/__init__.py b/lessons/lesson11/sem11_314/regressors/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/lessons/lesson11/sem11_314/regressors/lsm_regressor.py b/lessons/lesson11/sem11_314/regressors/lsm_regressor.py
deleted file mode 100644
index c8534de6..00000000
--- a/lessons/lesson11/sem11_314/regressors/lsm_regressor.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from typing import Iterable, Union
-from numbers import Real
-
-from regressors.regressor_abc import RegressorABC
-
-
-class RegressorLSM(RegressorABC):
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- # ваш код
- pass
-
- def predict(self, abscissa: Union[Real, Iterable]) -> list:
- # ваш код
- return [abscissa] if isinstance(abscissa, Real) else abscissa
diff --git a/lessons/lesson11/sem11_314/regressors/nonparametric_regressor.py b/lessons/lesson11/sem11_314/regressors/nonparametric_regressor.py
deleted file mode 100644
index 95081e7e..00000000
--- a/lessons/lesson11/sem11_314/regressors/nonparametric_regressor.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from typing import Iterable, Union
-from numbers import Real
-
-from regressors.regressor_abc import RegressorABC
-
-
-class NonparametricRegressor(RegressorABC):
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- # ваш код
- pass
-
- def predict(self, abscissa: Union[Real, Iterable]) -> list:
- # ваш код
- return abscissa
-
diff --git a/lessons/lesson11/sem11_314/regressors/regressor_abc.py b/lessons/lesson11/sem11_314/regressors/regressor_abc.py
deleted file mode 100644
index 7ea23e3c..00000000
--- a/lessons/lesson11/sem11_314/regressors/regressor_abc.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import abc
-
-from typing import Iterable, Union
-from numbers import Real
-
-
-class RegressorABC(abc.ABC):
- @abc.abstractmethod
- def fit(self, abscissa: Iterable, ordinates: Iterable) -> None:
- ...
-
- @abc.abstractmethod
- def predict(
- self, abscissa: Union[Real, Iterable]
- ) -> list:
- ...
diff --git a/lessons/lesson11/sem11_314/task_description.ipynb b/lessons/lesson11/sem11_314/task_description.ipynb
deleted file mode 100644
index 6e8bc084..00000000
--- a/lessons/lesson11/sem11_314/task_description.ipynb
+++ /dev/null
@@ -1,123 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "228e5039",
- "metadata": {},
- "source": [
- "# Практическое задание."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3ae03192",
- "metadata": {},
- "source": [
- "## Постановка задач\n",
- "\n",
- "Вам дан абстрактный базовый класс, формализующий интерфейс универсального регрессора - `RegressorABC`. RegressorABC имеет два абстрактных метода: fit и predict. Метод fit используется для извлечения информации из экспериментальных данных, эта информация используется для построения регрессионных зависимостей. Метод predict используется для получения предсказанного алгоритмом значения в заданной точке или заданном наборе точек. Используя абстрактный базовый класс, вам необходимо реализовать два конкретных регрессионных алгоритма, наследуя интерфейс регрессора.\n",
- "\n",
- "**Задачи**:\n",
- "- Реализуйте алгоритм непараметрической регресси, используя интерфейс, описанный в классе RegressorABC - решаем вместе; \n",
- "- Реализуйте Метод наименьших квадратов, используя интерфейс, описанный в классе RegressorABC - самостоятельно; "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "20eafc42",
- "metadata": {},
- "source": [
- "## Теоретическая справка"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b4bf8c3d",
- "metadata": {},
- "source": [
- "### Общая постановка задачи регрессии\n",
- "\n",
- "Заданы некоторые множества: $X$ - множество объектов, и $Y$ - множество ответов; \n",
- "$y: X \\rightarrow Y$ - неизвестная зависимость; \n",
- "$X^l = \\{x_1, x_2, ..., x_l\\} \\subset X$ - обучающая выборка; \n",
- "$y_i = y(x_i), i = 1, l$ - известные ответы для обучающей выборки; \n",
- "\n",
- "В рамках данного задания $X = \\mathbb{R}$, $Y = \\mathbb{R}$; \n",
- "\n",
- "**Задача** состоит в том, чтобы найти алгоритм $a: X \\rightarrow Y$, приближающий $y$ на всём множестве $X$, используя обучающую выборку $(X^l, y^l)$; \n",
- "**Замечание**: $y$ - истинная зависимость, $a$ - аппроксимация.\n",
- "\n",
- "\n",
- "### Непараметрическая Регрессия\n",
- "\n",
- "В рамках непараметрической регрессии мы пытаемся приблизить значении $y(x)$ некоторой константой $\\alpha$ в некоторой окрестности точки $x$. Задача сводится к нахождению данной константы для каждого $x \\in X$.\n",
- "\n",
- "Для того, чтобы отыскать оптимальное значения данной константы, решим следующую оптимизационную задачу:\n",
- "\n",
- "$Q(\\alpha, X^l) = \\sum_{i = 1}^{l}{w_i(x)(\\alpha - y_i)^2} \\rightarrow min_{\\alpha \\in \\mathbb{R}},$ где $w_i(x) = K(\\frac{\\rho(x, x_i)}{h})$ - вес i-ого квадратичного отклонения, $K(\\frac{\\rho(x, x_i)}{h})$ - ядро, невозрастающая, ограниченная, гладкая функция, $h$ - ширина окна сглаживания. Фактически мы минимизируем взвешанную сумму квадратов отклонений известных ответов от подобранной константы по $\\alpha$.\n",
- "\n",
- "$$\n",
- "\\frac{d}{d\\alpha}Q(\\alpha, X^l) = \\sum_{i = 1}^{l}{w_i(x)(2\\alpha - 2y_i)} = 0; \n",
- "\\\\ \\sum_{i = 1}^{l}{\\alpha w_i(x)} = \\sum_{i = 1}^{l}{y_i w_i(x)} \\rightarrow \\alpha = \\frac{\\sum_{i = 1}^{l}{y_i w_i(x)}}{\\sum_{i = 1}^{l}{w_i(x)}};\n",
- "\\\\ a(x, X^l) = \\frac{\\sum_{i = 1}^{l}{y_i w_i(x)}}{\\sum_{i = 1}^{l}{w_i(x)}} = \\frac{\\sum_{i = 1}^{l}{y_i K(\\frac{\\rho(x, x_i)}{h})}}{\\sum_{i = 1}^{l}{K(\\frac{\\rho(x, x_i)}{h})}};\n",
- "$$ \n",
- "\n",
- "Таким образом мы получили аппроксимацию $a(x, X^l) = \\frac{\\sum_{i = 1}^{l}{y_i K(\\frac{\\rho(x, x_i)}{h})}}{\\sum_{i = 1}^{l}{K(\\frac{\\rho(x, x_i)}{h})}}$, которая позволяет вычислять значение функции в данной точке по имеющимся данным.\n",
- "\n",
- "В качестве ядра будем использовать так называемое [ядро Епанечникова](https://ru.wikipedia.org/wiki/%D0%AF%D0%B4%D1%80%D0%BE_(%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B0)):\n",
- "$$\n",
- "\\begin{equation*}\n",
- "K(x) = \n",
- " \\begin{cases}\n",
- " \\frac{3}{4}(1 - x^2), |x| \\le 1\\\\\n",
- " 0, |x| > 1\n",
- " \\end{cases}\n",
- "\\end{equation*}\n",
- "$$\n",
- "\n",
- "Ещё одним тонким моментом, связанным с ядром, является ширина окна $h$.Ширина окна - некоторое число, которое можно подобрать руками, или реализовать адаптивный расчет. Т.к. адаптивный расчет является более надёжным методом, не требующим долгой настройки, мы пойдём именно по этому пути. \n",
- "\n",
- "Для адаптивного расчета ширины окна необходимо вычислить расстояния от элемента $x$, для которого мы хотим осуществить предсказание, до всех элементов $x_i \\in X^l$ обучающей выборки. После чего необходимо упорядочить их по возрастанию расстояния до $x$. Из полученного вариационного ряда выберем k-ый элемент $x_k$. Расстояние $\\rho(x, x_k)$ и будет нашей шириной окна, адаптивно вычисляемой для каждого элемента $x \\in X$. Может показаться, что мы несильно упростили задачу, ведь теперь вместо выбора $h$ мы будем выбирать $k$. Но, благодаря подобным действиям мы получим лучшие результаты, ведь при данном подходе ширина окна будет своя для кажого $x$. "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "abca6c26",
- "metadata": {},
- "source": [
- "___"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "55af3f6d",
- "metadata": {},
- "source": [
- "## Дополнительные материалы.\n",
- "\n",
- "1. [Машинное обучение. Метрические методы. К.В. Воронцов, Школа анализа данных, Яндекс.](https://www.youtube.com/watch?v=mCJcRDIY4TI&list=PLJOzdkh8T5krxc4HsHbB8g8f0hu7973fK&index=3&t=2079s)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson11/sem11_314/utils.py b/lessons/lesson11/sem11_314/utils.py
deleted file mode 100644
index 855ecacc..00000000
--- a/lessons/lesson11/sem11_314/utils.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from typing import Callable, Union
-
-import matplotlib.pyplot as plt
-import numpy as np
-
-
-from regressors.nonparametric_regressor import NonparametricRegressor
-from regressors.lsm_regressor import RegressorLSM
-
-from common.log import EventLogger
-
-
-K_NEIGHBOURS = 100
-POINTS_AMOUNT = 1000
-BOUNDS = (-10, 10)
-FIGSIZE = (16, 8)
-
-
-def visualize_results(
- axis: plt.Axes,
- abscissa: list,
- ordinates: list,
- predictions: list,
-) -> None:
- axis.scatter(abscissa, ordinates, label='source', c='royalblue', s=1)
- axis.plot(abscissa, predictions, label='prediction', c='steelblue')
-
- axis.set_xlim(min(abscissa), max(abscissa))
- axis.legend()
-
-
-def get_demonstration(
- function: Callable[[np.ndarray], np.ndarray],
- regressors: list[Union[RegressorLSM, NonparametricRegressor]],
-) -> None:
- event_logger = EventLogger(function.__name__)
- event_logger.info('obtain linear function data')
-
- abscissa = np.linspace(*BOUNDS, POINTS_AMOUNT)
- ordinates = function(abscissa).tolist()
- abscissa = abscissa.tolist()
-
- event_logger.info('start fitting regressors')
-
- for regressor in regressors:
- event_logger.info(f'fit {type(regressor).__name__}')
- regressor.fit(abscissa, ordinates)
-
- event_logger.info('prepare figure')
- _, axes = plt.subplots(1, 2, figsize=(16, 8))
-
- event_logger.info('start getting predictions')
-
- for ax, regressor in zip(axes, regressors):
- event_logger.info(f'get prediction for {type(regressor).__name__}')
- predictions = regressor.predict(abscissa)
-
- ax.set_title(type(regressor).__name__, fontweight='bold')
- visualize_results(ax, abscissa, ordinates, predictions)
-
- plt.show()
-
-
-def main() -> None:
- functions = [linear, linear_modulated]
- regressors = [RegressorLSM(), NonparametricRegressor(K_NEIGHBOURS)]
-
- for function in functions:
- get_demonstration(function, regressors)
-
-
-def linear(abscissa: np.ndarray) -> np.ndarray:
- function_values = 5 * abscissa + 1
- noise = np.random.normal(size=abscissa.size)
-
- return function_values + noise
-
-
-def linear_modulated(abscissa: np.ndarray) -> np.ndarray:
- function_values = np.sin(abscissa) * abscissa
- noise = np.random.normal(size=abscissa.size)
-
- return function_values + noise
diff --git a/lessons/lesson2/conspect.md b/lessons/lesson2/conspect.md
deleted file mode 100644
index 2b222cfd..00000000
--- a/lessons/lesson2/conspect.md
+++ /dev/null
@@ -1,238 +0,0 @@
-# Структура языка
-
-**Содержание:**
-
-- [Строки и отступы](#строки-и-отступы)
-- [Утверждения](#утверждения-statements)
-- [Токены](#токены)
- - [Идентефикаторы](#идентефикаторы)
- - [Разделители](#разделители)
- - [Литералы](#литералы)
- - [Операторы](#опреаторы)
- - [Ключевые слова](#ключевые-слова)
-- [Переменные и ссылки](#переменные-и-ссылки)
-- [Управление потоком выполнения](#управление-потоком-выполнения)
- - [Ветвление](#ветвление)
- - [Цикл while](#цикл-while)
- - [Цикл for](#цикл-for)
- - [break](#break)
- - [continue](#continue)
- - [else в циклах](#else-в-циклах)
- - [pass](#pass)
-
-## Строки и отступы
-
-Любой скрипт на Python представляет собой последовательность *логических строк*, каждая из которых состоит из одной или нескольких *физических строк*. Физическая строка может содержать комментарии. Комментарием считается часть строки, идущая после символа #. Эта часть строки игнорируется интерпретатором. Помимо комментариев интерпретатором также игнорируются строки, состоящие только из символов табуляции. Такие строки считаются *пустыми* и служат для разбиения листингов кода на логические блоки.
-
- Совет: не пренебрегайте комментариями и пустыми строками. Несмотря на их полное игнорированием интерпретатором, эти элементы языка помогут вам четче и яснее структурировать ваши программы, а также улучшать читабельность вашего кода.
-
-Для программистов, переходящих на Python с других языков, наподобие C или C++, будет необычно слышать, что конец любых утверждений и выражений в Python происходит с концом физической или логической строки, не требуя использования дополнительных разделителей(например, точки с запятой). Как и в C++ слишком длинные физические строки могут быть разбиты на несколько физических строк и объединены в одну логическую с помощью символа \\. Однако этот стиль не рекомендуется, поскольку в Python присутствует более элегантное python-way решение. Интерпретатор Python автоматически объединяет физические строки, находящиеся в круглых ((), квадратных ([) и фигурных ({) скобках в одну логическую строку. Так что, не гнушайтесь использования этого механизма для повышения качества вашего кода.
-
-```Python
-# это физическая строка
-light_velocity = 3e8
-
-# а это логическая строка
-very_long_string = 'this is very very very long string so long ' \
-'so you need to postpone its part to the next physical line'
-
-# логическая строка python-way
-another_long_string = (
- 'this is very very very long string so long '
- 'so you need to postpone its part to the next physical line'
-)
-```
-
-В отличие от C++ и многих других языков программирования, которые используют скобки или специальные ключевые слова для обозначения блока кода, Python использует отступы. Отступы - это единственный способ обозначить блок кода. Каждая логическая строка в вашей программе имеет свой уровень отступа. Блок кода - это непрерывная последовательность логических строк, имеющих одинаковый уровень отступа. Логическая строка с меньшим отступом завершает блок кода.
-
-## Утверждения (statements)
-
-Поимимо взгляда с позиции строк, вы можете смотреть на скрипт с точки зрения *простых* и *составных* утверждений.
-
-Простыми утверждениями называются утверждения, которые не содержат в себе других утверждений (да простят меня математики за подобные определения). Любое выражение, т.е. конструкция, порождающая некоторое значение, представляет собой простое выражение (утверждение). В качестве примеров простых выражений можно привести вызов функций или операцию присваивания (или, как мы узнаем далее, операцию привязки).
-
-Сложными утверждениями, по закону исключенного третьего, называются утверждения, которые содержат в себе хотя бы одно другое утверждение и осуществляют контроль их выполнения. Сложные утверждения имеют не менее одного __положения__ (clause). Положения, принадлежащие к данному сложному утверждению, имеют одинаковый отступ. Каждое положение обладает __хедером__, который обязательно начинается с ключевого слова и заканчивается двоеточием. За хедером следует набор утверждений - __тело положения__.
-
-## Токены
-
-Интерпретатор Python разбивает каждую логическую строку на последовательность языковых единиц, также известных, как __токены__. К токенам относятся: *идентефикаторы*, *разделители*, *литералы*, *операторы*, *ключевые слова*. Токены разделяются пробелами. Иногда разделение токенов пробелами необходимо, иногда - нет.
-
-### Идентефикаторы
-
-__Идентефикатор__ - это имя, которое используется для обозначения переменных, функций, классов, модулей и прочих объектов. Идентефикаторы начинаются с буквы английского алфавита или с нижнего подчеркивания. За начальным символом следует сколько угодно букв, нижних подчеркиваний и цифр. Символы пунктуации не допустимы для использовании в идентефикаторах.
-
-В Python используется следующее соглашение об идентефикаторах. Идентефикаторы переменных, функций и модулей пишутся в snake_case, идентефикаторы классов - в PascalCase.
-
-Некоторые идентефикаторы, начинающиеся с двух нижних подчеркиваний и заканчивающиеся двумя нижними подчеркиваниями, являются зарезервированными специальными именами.
-
-**Важно:** идентефикаторы чувствительны к регистру.
-
-```Python
-
-my_variable = 5 # валидное имя
-MyVariable = 6 # тоже валидное имя
-
-_var1 = 1 # и это валидное имя
-__len__ = 4 # переопределение заразарвированной переменной
- # так лучше никогда не делать
-
-2var = 2 # SyntaxError
-
-```
-
-### Разделители
-
-В Python используются следующие символы и комбинации символов в качестве разделителей в выражениях, литералах и для прочих целей в различных утверждениях:
-
-```Python
-( ) [ ] { }
-, : . ` = ; @
-+= -= *= /= // %=
-&= |= ^= >>= <<= **=
-```
-
-Также некоторые символы имеют специальные значения. К их числу относятся:
-
-```Python
-' " # \
-```
-
-### Литералы
-
-Литералы - это прямое указание ~~в программы~~ значения некоторых данных. Ниже приведены пример числовых литералов:
-
-```Python
-1 # целое число
-3.14 # число с плавающей точкой
-1.0j # комплексное число
-```
-
-
-### Опреаторы
-
-Следующие символы используются Python в качестве операторов:
-
-```Python
-+ - * / % ** // << >> &
-| ^ ~ < <= > >= != ==
-```
-
-### Ключевые слова
-
-Ключевые слова - это набор зарезервированных идентефикаторов, которые используются в Python для специальных синтаксических конструкций. Из этого следует, что вы не можете использовать ключевые слова в качестве идентефикатора дрегих объектов. Ключевые слова содержат только идентефикаторы в нижнем регистре. Некоторые ключевые слова служат для начала сложных утверждений, в то время как другие являются операторами.
-
-## Переменные и ссылки
-
-Программы, написанные на Python, получают доступ к данным с помощью ссылок. Ссылка - это некоторое имя, которое ссылается на какое-то значение (объект). Ссылка может быть представлена в виде переменной, атрибута или элемента коллекции. Ссылки в Python не имеют типа данных. Да, объект, на который ссылается ссылка имеет строго определенный тип данных (о типах данных мы поговорим в одной из следующих лекций), но сама ссылка в один момент времени может ссылаться на целое число, затем быть перепривязана к строке, ну и в конце концов может быть привязана к пользовательскому типу данных. Далее мы подробнее рассмотрим переменные.
-
-Из предыдущего абзаца видно, что переменные в Python - это ссылки на настоящие объекты в памяти. В отличие от C или C++ в Python нет объявления переменных. Переменные начинают существовать с момента выполнения привязки (определения переменной). Привязка осуществляется с использованием операции присваивания:
-
-```Python
-num1 = 5
-num2 = num1
-num1 = 6
-```
-
-После выполнения первого утверждения в памяти компьютера будет создан объек "целое число" со значением 5, ссылка на этот объект будет привязана к переменной num1. Во втором примере новый объект в памяти создан не будет, переменная num2 будет указывать на тот же объект в памяти, что и num1. В третьем утверждении num1 была перепривязана. Здесь происходит почти все то же, что происходило в первой строке листинга кода. Единственное исключение - это отвязка ссылки num1 от объекта "целое число 5" и привязка этой ссылки к объекту "целое число 6". В итоге, после выполнения этого куска кода в памяти компьютера будет два объекта типа "целое число": со значением 5, на который в программе ссылается переменная num2, и со значением 6, на который ссылается переменная num1.
-
-Для лучшего понимания того, что происходит, можно использовать встроенную функцию `id()`, которая принимает на вход объект Python и возвращает целое число - его уникальный id.
-
-*Код:*
-```Python
-num1 = 5
-num2 = num1
-
-print(id(num1)) # посмотрим, как выглядит идентификатор
-print(id(num1) == id(num2))
-
-num1 = 6
-
-print(id(num1) == id(num2))
-
-```
-*Вывод:*
-```Console
->>> True
->>> False
-```
-
-В данном примере отчетливо видно, что в момент определения переменной num2, ссылки num1 и num2 ссылаются на один и тот же объект в памяти (их id равны). После перепривязки num1, ссылки num1 и num2 начинают ссылаться на два разных объекта в памяти.
-
-Очень важно понять данную концепцию. В будущем она ни раз пригодиться нам при работе с ***изменяемыми типами данных***.
-
-Поговорим немного о присваивании, ибо с ним не все так просто. В Python присутствует два вида присваивания: простое и составное. Простое присваивание используется для привязки - в этот момент может создаваться новый объект в памяти компьютера и новая ссылка, используется для перепривязки. Пока мы ничего не знаем (или притворяемся, что не знаем) о коллекциях, рассмотрим следующие интересные случаи использования простого присваивания:
-
-```Python
-a = b = c = 0 # случай 1: каскадное присваивание
-a = 5
-
-a, b = b, a # случай 2: множественное присваивание
-```
-
-В первом случае мы привязали сразу три ссылки к объекту со значеним 0. Во втором перепривязали a к b, a b - к a. Это работает так, как мы ожидаем, потому что операция присваивания выполняется следующим образом: сначала вычисляется выражение, стоящее справа от знака =, затем осществляется превязка/перепревязка. В примере 1 мы сначала вычислили значение литерала 0 (сюрприз, сюрприз, оно оказалось равным нулю), затем привязали к объекту с этим значением ссылку с, затем вычислили значение ссылки с и привязали к ней b, и т.д. Во втором случае мы вычислили значения по ссылкам b и a и перепривязали a и b. Все просто!
-
-Помимо простого присваивания в Python есть операторы составного присваивания. Обычно они имеют следующий вид: <бинарный оператор>=. В большинстве своем легко понять, что и как делают эти операторы. Интересующая нас вещь заключается в том, что в отличие от простого присваивания, при составном присваивании никогда не создаетя новой ссылки, для осуществления составного присваивания переменная должна уже сущестовать в программе.
-
-## Управление потоком выполнения
-
-Поток выполнения программы - это порядок, в котором выполняется код. Порядок выполнения программы в Python зависит от конструкций ветвления, циклов и вызовов функции (о которых мы поговорим в одной из следующих лекци).
-
-### Ветвление
-
-Ветвление - это сложное утверждение, которое включает в себя положения if, elif и else. В самом общем виде конструкция ветвления выглядит следующим образом:
-
-```Python
-if expression1:
- do_something1()
-
-elif expression2:
- do_somethin2()
-
-elif expression3:
- do_somethong3()
-
-else:
- do_another_thing()
-```
-
-Положения elif и else являются не обязательными.
-
-Вы вольны использовать любые выражения в положениях if и elif. Использование выражений таким образом называют "__использованием выражения в булевом контексте__". В этом контексте любое значение приводится к типу bool и принимает значения True или False. Заметим существенную разницу между фразами "выражение принимает значение True" и "выражение расценивается, как True". Когда мы работаем с ветвлением нас интересует именно второй вариант.
-
-Если 'expression1' расценивается как True, то выполняется блок кода, который следует за ним. Иначе Python будет оценивать выражение в первом блоке elif, потом во втором, и так далее, пока одно из них не будет расценено, как True, или пока не дойдет до блока else/конца ветвления.
-
-### Цикл while
-
-Цикл while предназначен для выполнения некоторой последовательности действий до тех пор, пока выражение, стоящее в условии цикла, расценивается как True.
-
-```Python
-while expression:
- do_something()
-```
-
-### Цикл for
-
-Цикл for осуществляет выполнение некоторой последовательности действий, котонтролируемую итерируемым объектом (рассмотрим позже в лекциях).
-
-```Python
-for target in iterable:
- do_something()
-```
-
-target - переменная контроля цикла. На каждой итерации она перепривязывается к новому элементу итерируемого объекта iterable. Что может показаться интересным - переменная target будет доступна вне цикла for даже после его завершения и содержать в себе последний рассмотренный элемент из iterable.
-
-### break
-
-Ключевое слово break может быть использовано только в циклах для мгновенного прерывания этих самых циклов. Когда break выполняется, цикл завершается. Если имеет место вложенный цикл, содержащий break, break прерывает только вложенный цикл, в котором он встретился, не затрагивая выполнение внешнего цикла.
-
-### continue
-
-Как и break, continue может быть использовано только в циклах для пропуска части тела цикла. Обычно continue используется для уменьшения степени вложенности кода в теле цикла.
-
-### else в циклах
-
-Помимо очевидного использования else в ветвлении, else так же может быть использовано вместе с циклами. В контексте циклов слово else может сбивать с толку, поскольку по смыслу гораздо сильнее подходило бы слово then, но добавление нового ключевого слова испортило бы обратную совместимость, чего создатель языка Python Гвидо Ван Россум старательно избегает. Собственно, блок else, размещенный после цикла, будет выполнятся только в том случае, когда цикл был завершен "естественным путем": без прерывания в теле цикла с помощью инструкции break.
-
-### pass
-
-Тело циклов и ветвлений не может состоять из пустых строк, однако мы не всегда хотим что-то делать при выполнении определенного условия (???тогда можно не проверять это условие :-)... Я думал, что 'pass' - это заглушка ???). С этой целью в языке существует пустая команда pass, которая идеально подходит как раз для описанного случая.
diff --git a/lessons/lesson2/sem2_312/git_training.zip b/lessons/lesson2/sem2_312/git_training.zip
deleted file mode 100644
index 80dd34e3..00000000
Binary files a/lessons/lesson2/sem2_312/git_training.zip and /dev/null differ
diff --git a/lessons/lesson2/sem2_312/git_training_final.zip b/lessons/lesson2/sem2_312/git_training_final.zip
deleted file mode 100644
index f96cf90b..00000000
Binary files a/lessons/lesson2/sem2_312/git_training_final.zip and /dev/null differ
diff --git a/lessons/lesson2/sem2_312/try_lection_material.py b/lessons/lesson2/sem2_312/try_lection_material.py
deleted file mode 100644
index 026ac3af..00000000
--- a/lessons/lesson2/sem2_312/try_lection_material.py
+++ /dev/null
@@ -1,46 +0,0 @@
-very_long_string = 'this is very very very long string so long ' \
-'so you need to postpone its part to the next physical line'
-print(very_long_string)
-
-another_long_string = (
- 'this is very very very long string so long '
- 'so you need to postpone its part to the next physical line'
-)
-print(another_long_string)
-
-
-my_variable = 5 # валидное имя
-print(f"my_variable = {my_variable}")
-
-MyVariable = 6 # тоже валидное имя
-print(f"MyVariable = {MyVariable}")
-
-_var1 = 1 # и это валидное имя
-print(f"_var1 = {_var1}")
-
-__len__ = 4 # переопределение заразарвированной переменной
- # так лучше никогда не делать
-print(f"__len__ = {__len__}")
-
-print("####################################################")
-
-num1 = 5
-num2 = num1
-print(id(num1) == id(num2))
-
-num1 = 6
-print(id(num1) == id(num2))
-
-id1 = id(num2)
-print(f"num2 = {num2}, id = {id1}")
-
-num2 = 3
-id2 = id(num2)
-print(f"num2 = {num2}, id = {id2}")
-
-num2 = 5
-id3 = id(num2)
-print(f"num2 = {num2}, id = {id3}")
-print(id1 == id3)
-
-
diff --git a/lessons/lesson3/conspect.md b/lessons/lesson3/conspect.md
deleted file mode 100644
index 94b08988..00000000
--- a/lessons/lesson3/conspect.md
+++ /dev/null
@@ -1,460 +0,0 @@
-# Типы данных: числа, списки, кортежи
-
-**Содержание:**
-
-- [Общие сведения о типах данных](#общие-сведения-о-типах-данных)
-- [Числовые типы данных](#числовые-типы-данных)
- - [Целочисленные литералы](#целочисленные-литералы)
- - [Литералы чисел с плавающей точкой](#литералы-чисел-с-плавающей-точкой)
- - [Литералы комплексных числе](#литералы-комплексных-чисел)
- - [Логический тип данных](#логический-тип-данных)
- - [Преобразования цичловых типов](#преобразования-числовых-типов)
- - [Арифметические операции](#арифметические-операции)
- - [Логические операции](#логические-операции)
- - [Битовые операции](#битовые-операции)
-- [Последовательности и итерируемые объекты](#последовательности-и-итерируемые-объекты)
-- [Операции над последовательностями](#операции-над-последовательностями)
-- [Кортежи](#кортежи)
-- [Списки](#списки)
-
-## Общие сведения о типах данных
-
-Как нам известно из [прошлой лекции](../lesson2/conspect.md
-), в Python существуют переменные. Переменные являются ссылками на некоторые данные в памяти компьютера. Данные, на которые ссылаются переменные, называются *объектами* (objects). В отличие от переменных, которые не являются типизированными, объекты имеют строго заданный тип данных. Тип данных определяет значения, которые может принимать данный объект, набор операций, поддерживаемых данным объектом, его атрибуты, поддерживает ли объект хранение элементов, и какие ограничения накладываются на элементы, если поддерживает.
-
-Все типы данных в Python делятся на две основные группы: изменяемые (*mutable*) и неизменяемые (*immutable*). Объекты изменяемых типов данных могут менять свое значение в процессе выполнения программы. Объекты неизменяемых - не могут.
-
-Для определения типа данных объекта, с которым связана та или иная переменная, в Python существует специальная функция `type()`, принимающая на вход объект Python.
-
-*Код:*
-```Python
-num = 5
-print(type(5), type(5).__name__, sep='\n')
-```
-
-*Вывод:*
-```Console
-
-int
-```
-В Python реализованы основные типы данных: числа, стоки, кортежи, списки, множества и словари. Также при необходимости пользователь может самостоятельно определить свой тип данных, называемый *классом*. Но это тема совершенно другой лекции.
-
-## Числовые типы данных
-
-В Python реализованы следующие числовые типы данных: целые числа, числа с плавающей точкой и комплексные числа. Также для работы с числами в стандартной библиотеки Python существуют модули `decimal` и `fractions`, но они в рамках нашего курса обсуждаться не будут.
-
-Числовые типы данных являются неизменяемыми. Отсюда следует, что почти любые операции над числами приводят к созданию нового объекта числового типа данных и его аллокации в памяти компьюетра. Мы говорим "почти", так как Python заранее аллоцирует в памяти объекты типа int, чье значение лежит в диапазоне от -5 до 256. Таким образом, если результатом операции над числами будет являться целое число в указанном диапазоне, создание нового объекта происходить не будет. Все, что сделает интерпретатор - перепривяжет идентефикатор.
-
-*Пример 1*:
-```Python
-num = 1
-print(id(num))
-
-num += 5
-print(id(num))
-```
-*Результат*:
-```Console
-140706836767528
-140706836767688
-```
-
-*Пример 2*:
-
-```Python
-num1 = 5
-num2 = 10
-num3 = 5
-
-print(id(num1) == id(num2 - num3) == id(num3))
-```
-
-*Результат*:
-```Console
-True
-```
-
-*Пример 3*:
-```Python
-num1 = 500
-num2 = 1000
-num3 = 500
-
-print(
- id(num1) == id(num3),
- id(num1) == id(num2 - num3),
- id(num3) == id(num2 - num1),
- sep='\n'
-)
-```
-
-*Результат*:
-```Console
-False
-False
-False
-```
-
-### Целочисленные литералы
-
-Для начала обсудим целочисленные литералы. В Python целочисленные литералы могут иметь десятичный, двоичный, восмеричный и шестнадцатеричный вид.
-
-*Пример целочисленных литералов:*
-
-```Python
-1, 23, 4567 # десятичные литералы
-0b101, 0b11011 # двоичные литералы
-0o1, 0o6645 # восьмеричные литералы
-0x1, 0xDA5 # шестандцатеричные литералы
-```
-
-Также для читабельности числовых литералов вы можете разделять разряды с помощью нижнего подчеркивания. Эта информация в равной степени применима ко всем числовым типам данных.
-
-*Пример использования разделителей:*
-```Python
-1_000_000_000
-0xF1c5_910D_FF0A
-```
-
-В отличие от языков программирования типа C и C++ максимальное (и минимальное) значение целочисленного типа данных не имеет заранее обговоренного ограничения и упирается в количество свободной памяти компьютера. С одной стороны, это является плюсом, т.к. при работе с Python вы не будете задаваться вопросами о переполнении даже при работе с очень большими числами (см. длинная арифметика), с другой стороны, особенности хранения целых чисел, позволяющие проворачивать подобное, накладывают заметные ограничения на производительность.
-
-### Литералы чисел с плавающей точкой
-
-Литералы чисел с плавающей точкой представляют собой последовательность десятичных чисел, включающую точку, суффикс экспоненты (символ e, за которым опционально следует знак и как минимум одна десятичная цифра) или и точку, и суффикс. Первым символом числа с плавающе точкой не может быть символ экспоненты, им может быть или цифра, или точка.
-
-*Примеры литералов*:
-
-```Python
-0. 0.0 .0
-1. 1.0 1e0 1.e0
-```
-
-Числа с плавающей точкой в Python являются аналогом чисел с плавающей точкой в С: они имеют те же ограничения, ту же точность, а их значения хранятся аналогичным образом.
-
-### Литералы комплексных чисел
-
-Комплексные числа представляют собой алгебраическую сумму реального числа с плавающей точкой с мнимым числом с плавающей точкой. Доступ к мнимой и реальной части комплексного числа осуществляется через атрибуты чтения `.real` и `.imag`.
-
-*Примеры литералов:*
-
-```Python
-0j 0.j 0.0j .0j
-1j 1.j 1.0j 1e0j
-1.e0j 1.0e0j
-```
-
-### Логический тип данных
-
-Формально, логический тип является отдельным типом данных. Однако, фактически, он является подклассом типа `int`, т.е. целых чисел. Булевый тип данных имеет всего два значения: правда и ложь. Для этих значений существуют специальные константы-синглтоны `True` и `False`, соответственно, которые также имеют свои числовые эквиваленты: 1 и 0. Почти любые данные в Python могут быть представлены булевым значением: любое число, отличное от 0 в булевом контексте принимает значение True, любая непусткая коллекция также принимает значение True. 0, пустая коллекция и синглтон `None` принимают значение False.
-
-Также вы можете преобразовать данные в булево значение явным образом, используя функцию `bool()`. Однако данная практика не рекомендована, поскольку python-way - это неявное преобразование данных к логическому типу в булевом контексте.
-
-*Пример*:
-```Python
-# рекомендованный стиль
-if x:
- ...
-
-# нежелательный стиль
-if bool(x):
- ...
-```
-
-### Преобразования числовых типов
-
-В Python вы можете осуществлять арифметические и логические операции между значениями любых числовых типов данных. В случае, если операнды имеют различные числовые типы данных, Python осуществит неявное преобразование операнда с "меньшим" типом данных к "большему" типу данных. Числовые типы данных в порядке от "меньшего" типа, к "большему": целые числа, числа с плавающей точкой, комплексные числа. В случае необходимости вы также можете осуществить явное преобразование типов, используя встроенные функции `int()`, `float()` и `complex()`. Функция complex() принимает на вход два числа: действительную и мнимую часть. Также комплексные числа не могут быть преобразованы в целые числа или числа с плавающей точкой напрямую, из-за неоднозначности преобразования.
-
-Функция int() отбрасывает дробную часть, в случае ее наличия. Также, в отличие от других функций, int() может быть вызвана с двумя аргументами - строка, содержащая в себе число, и основание системы счисление - число от 2 до 36. Для систем счисления с основанием большим 10 необходимо использовать соответствующие буквы англиского алфавита для обозначения цифр.
-
-*Пример*:
-```Python
-print(int('abba'), 36)
-```
-*Вывод*:
-```
-481222
-```
-
-Помимо объектов числовых типов данных, функции int(), float() и complex() могут принимать на вход строки, содержащие допустимые числовые литералы, знак числа, а также ведущие и завершающие пробелы.
-
-### Арифметические операции
-
-В целом, числовые типы данных поддерживают классический набор арифметически операций, которые работают очевидным образом. Однако мы уделим отдельное внимание делению.
-
-В Python существует три вида операций деления: истинное деление (/), целочисленное деление (//) и модульное деление (%). Результатом истинного деления будет число типа float, даже если оба операнда были целыми числами. Однако, если вы хотите себя обезопасить и написать программу, обратно совместимую с программами, написанными на Python v2, используйте преобразование одного из операндов к типу float.
-
-*Пример*:
-```Python
-num1 = 1
-num2 = 2
-
-# чуть медленнее
-float(num1) / num2
-
-# чуть быстрее
-1.0 * num1 / num2
-```
-
-Результат целочисленного деления - целая часть от деления, модульного - остаток. Для получения и целой, и дробной части в одно действие, вы можете использовать встроенную функцию `divmod()`.
-
-Для возведения в степень используется бинарная операция ** или втроенная функция `pow()`.
-
-*Примеры возведения в степень*:
-```Python
-print(3 ** 4)
-print(pow(3, 4))
-print(pow(3, 4, 8)) # (3 ** 4) % 8
-```
-
-*Вывод*:
-```Python
-81
-81
-1
-```
-
-### Логические операции
-
-Числовые типы данных могут быть использованы с логическими операндами. Сравнение на равенство/неравенство применимо ко всем числовым типам данных. Логические операции порядка (<, <=,>, >=) могут быть использованы только с некомплексными числами, в противном случае будет вызываться исключение.
-
-Результатом выполнения логических операций является булево значение. Также логические операции могут объединяться в сложные логические выражения с помощью бинарных операторов `and` и `or`.
-Операторы and и or являются "ленивыми": второй аргумент оператора and вычисляется только в том случае, когда первый принимает значение True; второй аргумент оператора or вычисляется только если первый принимает значение False.
-
-Также сложные условия могут заменяться на последовательность логических операторов.
-
-*Пример:*
-
-```Python
-a, b, c = 1, 2, 3
-
-# сложное условие
-if a < b and b < c:
- ...
-
-# последовательность операторов
-if a < b < c: # a < b and b < c:
- ...
-
-```
-
-### Битовые операции
-
-Целые числа поддерживают битовые операции: сдвиг (<<, >>), инверсия (~), битовое и (&), битовое или (|), не или (^).
-
-## Последовательности и итерируемые объекты
-
-Одной из базовых концепций Python явялется концепция *итерируемого объекта*. Мы уже встречались с итерируемыми объектами, когда обсуждали цикл for, тогда мы ограничились тем, что итерируемый объект - это объект, чьи элементы можно перебрать в цикле. Если подойти к этому вопросу формальнее, то итерируемый объект в Python - это объект, который поддерживает вызов функции `iter()`, пораждающий итератор. Итератор же - это объект, с которым поддерживается вызов функции `next(i)`, где i - итератор. Каждый вызов функции iter(i) возвращает новое значение, хранящееся (коллекции) или вычисляемое (генераторы) в итераторе. Все итераторы делятся на ограниченные и неограниченные. Ограниченные итераторы способны вернуть конечное число элементов, после определенного числа вызовов функции next(i) ограниченные итераторы вызывают исключение `StopIteration`, сигнализирующее о завершении элементов. Неограниченные итераторы поддерживают неограниченное количество вызовов функции next(i). Для получения итератора в Python используется функция `iter()`.
-
-Итерируемые объекты - очень общая концепция, которая включает в себя большое количество различных классов объектов. К числу таких классов относятся последовательности.
-
-В Python под последовательностями подразумевается упорядоченная коллекция, индексируемая целыми числами. Последовательности реализуют специальные методы `__len__()` (определение количества элементов) и `__getitem__()` (доступ к элементу через [ ]). Последовательности являются итерируемыми объектами, но обратное неверно: не все итерируемые объекты являются последовательностями.
-
-Исходя из всего вышесказанного становится понятна логика работы цикла for:
-
-```Python
-# исходный код
-for i in iterable:
- do_something(i)
-
-# логика работы
-_iterator = iter(iterable)
-while True:
- # блок для обработки исключений
- try:
- i = next(_iterator)
- # обрабатываем только StopIteration
- except StopIteration:
- break
- do_something(i)
-```
-
-К последовательностям в Python относятся кортежи, списки и строки. В этой лекции речь пойдет о кортежах и списках.
-
-## Операции над последовательностями
-
-Прежде чем переходить к детальному рассмотрению представителей последовательностей обсудим набор операций, свойственных для всех последовательностей.
-
-### Конкатенация и повторение
-
-
-Вы можете конкатенировать последовательности одного типа, используя оператор +. Также вы можете повторять последовательность n раз, умножая последовательность на целое число справа или слева. Повторение последовательности n раз эквивалентно последовательной конкатенации n одинаковых последовательностей.
-
-### Проверка вхождения
-
-С помощью ператора `in` вы можете проверить является ли какой-либо объект Python элементом последовательности. Оператор возвращает True, если является и False иначе. Существует две формы отрацания этого оператора, описанные в примере.
-
-*Пример:*
-```Python
-array = list(range(5))
-num = 3
-
-print(num in array) # проверка вхождения
-print(not(num in array)) # отрицания
-print(num not in array) # еще одна форма отрицания
-```
-
-### Индексация
-
-Вы можете обращаться к элементам последовательности по целочисленном индексу:
-
-*Пример*:
-```Python
-sequence = [1, 2, 3, 4]
-print(sequence[2])
-```
-
-*Вывод*:
-```Console
-3
-```
-Индексация начинается с нуля и осуществляется через квадратные скобочки. Индекс может принимать значения от 1 до n, где n - длина последовательности. Также индексы могут принимать отрицательные значения от -1 до -n. Отрицательные индексы можно воспринимать как индекс n + i, где i - отрицательный индекс. Таким образом индексу -1 соответствует n-1 элемент последовательности, -2 - n-2 и т.д. Если индекс принимает значение большее n-1 или меньшее -n, вызывается исключение выххода индекса за границы массива.
-
-### Срезы
-
-Также в можете получить подпоследовательность элементов последовательности, используя *срезы*. Срезы в Python - это полноценные объекты, которые можно создать следующим образом:
-
-```Python
-slice(start, stop, step)
-```
-
-где start - индекс начала подпоследовательности, stop - индекс конца подпоследовательности, step - шаг. stop в результирующую подпоследовательность не включается. Но таким синтаксисом почти не пользуются. В контексте индексации объект среза конструируется неявно. Индексация с помощью среза выглядит так:
-
-```Python
-array = [1, 2, 3, 4, 5]
-
-print(arr[1:3])
-print(arr[:3])
-print(arr[1:])
-print(arr[::2])
-print(arr[::-1])
-print(arr[-2:-5:-1])
-```
-
-*Вывод*:
-```Console
-[2, 3]
-[1, 2, 3]
-[2, 3, 4, 5]
-[1, 3, 5]
-[5, 4, 3, 2, 1]
-[4, 3, 2]
-```
-
-## Кортежи
-
-Кортеж (tuple) - неизменяемая упорядоченная последовательность элементов. Элементы кортежа - независмые объекты различных типов. Несмотря на то, что кортеж - неизменяемый тип данных, вы можете использовать объекты изменяемых типов данных в качестве элементов кортежа и изменять их. Однако пользоваться подобной практикой крайне не рекомендуется. Старайтесь избегать использования изменяемых объектов в качестве элементов кортежа.
-
-Для объявления кортежа используйте ряд выражений, разграниченных запятыми. Вы также можете поместить выражения, разделенные запятыми в круглые скобки, но это необходимо только в том случае, когда разделение только лишь запятыми не может быть однозначно воспринято интерпретатором, или если вы хотите создать пустой кортеж. Также вы можете создать кортеж, добавив запятую после выражения.
-
-*Пример создания кортежа*:
-
-```Python
-(1, 2, 3)
-1, 2, 3
-(3.14, )
-3.14,
-()
-```
-
-Вы также можете создать кортеж с помощью втроенной функции `tuple()`. На вход требуется любой итерируемый объект. Вызов функции без аргументов приведет к созданию пустого кортежа.
-
-*Пример использования tuple*:
-
-```Python
-tuple('hello')
-tuple()
-```
-
-*Вывод*:
-```Console
-('h', 'e', 'l', 'l', 'o')
-()
-```
-
-Кортежи поддерживают все операции над последовательностями. Результат конкатенации и срезки - новый кортеж. Из неизменяемость следует невозможность перезаписи элементов кортежа.
-
-Помимо обозначенных операций, кортеж имеет два строенных метода: `index()` и `count()`. Метод индекс принимает на вход объект Python и возвращает индекс первого его вхождения вкортеж, если он есть в коллекции, иначе - ValueError. Метод count() принимает на вход объект Python и подсчитывает количество его вхождений в кортеж.
-
-*Пример*:
-```Python
-numbers = tuple(range(5)) * 2
-
-print(numbers
-)
-print(numbers.count(3))
-print(numbers.count(42))
-print(numbers.index(3))
-print(numbers.index(42))
-```
-*Вывод*:
-```Console
-(0, 1, 2, 3, 4, 0, 1, 2, 3, 4)
-2
-0
-3
-Traceback (most recent call last):
- File "", line 1, in
-ValueError: tuple.index(x): x not in tuple
-```
-
-## Списки
-
-Список - изменяемая упорядоченная последовательность элементов. Элементы списка - независимые эобъекты различных типов данных. Для определения списка испульзуется ряд выражений, разделенных запятыми и помещенных в квадратные скобки.
-
-*Примеры объявления списков*:
-
-```Python
-[42, 3.14, 'string']
-[100]
-[]
-```
-По аналогии с кортежем, список может быть создан из любого итерируемого объекта с помощью функции `list()`. Вызов функции без аргументов приведет к созданию пустого списка.
-
-*Пример использования list*:
-
-```Python
-list('hello')
-list()
-```
-
-*Вывод*:
-```Console
-['h', 'e', 'l', 'l', 'o']
-[]
-```
-
-Списки поддерживают все операции, свойственные последовательностям.
-
-### Изменения через индексацию
-
-Поскольку списки - изменяемые объекты, вы можете изменять значения их элементов, используя индексацию , удалять или добавлять новые элементы используя срезы.
-
-*Примеры изменения содержимого*:
-```Python
-nums = [1, 2, 3, 4, 5]
-nums[1] = 42 # [1, 42, 3, 4, 5]
-nums[2:4] = [5, 6, 7, 8] # [1, 42, 5, 6, 7, 8, 5]
-nums[2:2] = [3, 3, 3] # [1, 42, 3, 3, 3, 5, 6, 7, 8, 5]
-nums[2:7] = [] # [1, 42, 7, 8, 5]
-nums[:] = [1] # [1]
-
-```
-
-### Операции на месте
-
-Для списков составное присваивание типа *= и += не будут создавать новый объект, а будут модифицировать имеющийся список.
-
-### Методы
-
-Списки имеют модифицирующие и немодифицирующие методы. К немодифицирующим методам относятся метод index() и метод count(), которые работают аналогичено соответствующим методам кортежей. Ниже представлены модифицирующие методы списков, который изменяют текущий объект.
-
-|Сигнатура|Описание|
-|--|--|
-|append(x)|добавляет элемент x в конец списка|
-|extend(x)|добавляет все элементы из итерируемого объекта x в конец списка|
-|insert(i, x)|вставляет элемент x перед элементом с индексом i|
-|remove(x)|удаляет из списка первое вхождение элемента x|
-|pop(i=-1)|возвращает элемент под индексом i и удаляет его из списка; удаляет последний элемент, если значние i опущено; если список пуст или i - недействительный индекс, вызывает исключение|
-|reverse()|разворачивает список на месте|
-|sort(key=None, reverse=False)|сортирует список на месте|
-___
diff --git a/lessons/lesson3/sem3_312/.ipynb_checkpoints/tasks-checkpoint.ipynb b/lessons/lesson3/sem3_312/.ipynb_checkpoints/tasks-checkpoint.ipynb
deleted file mode 100644
index 6fa30ee1..00000000
--- a/lessons/lesson3/sem3_312/.ipynb_checkpoints/tasks-checkpoint.ipynb
+++ /dev/null
@@ -1,283 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Iterable, Any"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Разминка: методы через срезы\n",
- "\n",
- "Значительную часть модифицирующих методов списков можно реализовать через срезы. Ваша задача реализовать аналоги методов append(), extend(), insert(), reverse(), используя только срезы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def my_append(list_instance: list, x: Any) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_extend(\n",
- " list_instance: list, expansion: Iterable\n",
- ") -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_insert(list_instance: list, i: int) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_reverse(list_instance: list) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 1: Сложение\n",
- "\n",
- "На вход подаются два списка, репрезентирующие числа в десятичной системе счисления. Элементы списков - числа от 0 до 9, представляющие значения разрядов числа. Самый левый разряд - самый больший. Т.е. число 123 будет представлено списком [1, 2, 3]. Ваша задача - вычислить сумму переданных чисел и вернуть список, представляюзщий эту сумму. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "def sum_two_nums(num1: list[int], num2: list[int]) -> list[int]:\n",
- " # ВАШ КОД\n",
- " return [0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "num1 = [1, 2, 3]\n",
- "num2 = [7, 7]\n",
- "\n",
- "assert sum_two_nums(num1, num2) == [2, 0, 0]\n",
- "assert sum_two_nums(num2, num1) == [2, 0, 0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 2: Объеденяй и не властвуй\n",
- "\n",
- "На вход подан список intervals, где intervals[ i ] = [$start_i$, $stop_i$]. Объедените все пересекающиеся интервалы и верните список непересекающихся интервалов, покрывающий все интервалы из intervals."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:\n",
- " # ВАШ КОД\n",
- " return []"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "intervals = [[1,3],[2,6],[8,10],[15,18]]\n",
- "assert merge_intervals(intervals) == [[1,6],[8,10],[15,18]]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 3: Удалим дубликаты\n",
- "\n",
- "Дан список nums, отсортированный в неубывающем порядке. Ваша задача удалить дублирующиеся элементы **на месте** так, чтобы каждый уникальный элемент массива имел лишь одно вхождение. При этом относительный порядок следования элементов должен остаться без изменений."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "def remove_duplicates(nums: list[int]) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "nums = [1, 1, 2]\n",
- "\n",
- "remove_duplicates(nums)\n",
- "assert nums == [1, 2]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 4: Уникальные пути\n",
- "\n",
- "Вам дано двумерное поле размера m X n. В левом верхнем углу (grid[0][0]) расположен робот. Робот старается добраться до правого нижнего угла (grid[-1][-1]). Робот может ходить только вниз или вправо. \n",
- "\n",
- "Свободные клетки и препятствия помечены в массиве grid 0 и 1 соответственно. Пути робот из верхнего левого угла в правый нижний угол могут проходить только через свободные клетки. \n",
- "\n",
- "Ваша задача - вычислить максимальное возможное количество уникальных путей из левого верхнего угла в правый нижний угол."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "def compute_unique_pathes(grid:list[list[int]]) -> int:\n",
- " # ВАШ КОД\n",
- " answer = 0\n",
- " return answer"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "grid = [\n",
- " [0, 0, 0],\n",
- " [0, 1, 0],\n",
- " [0, 0, 0]\n",
- "]\n",
- "\n",
- "assert compute_unique_pathes(grid) == 2"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 5: Игра в прыжки\n",
- "\n",
- "Вам дан список jumps, состоящий из целых чисел, индексирующийся с нуля и имеющий длину n. Вы начинаете движение с позиции jumps[0]. Каждый элемент списка jumps[i] представляет собой длину максимального прыжка вперед с позиции i: если вы находитесь в позиции i, вы можете прыжком переместиться на любую позицию от i до i + jumps[i].\n",
- "\n",
- "Ваша задача - определить минимальное число прыжков, необходимое для достижения позиции n - 1."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "def jump(jumps: list[int]) -> int:\n",
- " # ВАШ КОД\n",
- " return 0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "jumps = [2,3,1,1,4]\n",
- "assert jump(jumps) == 2"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson3/sem3_312/list_tasks/task1.py b/lessons/lesson3/sem3_312/list_tasks/task1.py
deleted file mode 100644
index c8037114..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task1.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Копирование массива
-""" Выделите память под 2 массива.
- Заполните один из них числами (можно случайными)
-
- Затем скопируйте значения из первого массива во второй.
-
- Работает ли в этом случае оператор '='? Почему?
- А можно ли короче?
-"""
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/list_tasks/task2.py b/lessons/lesson3/sem3_312/list_tasks/task2.py
deleted file mode 100644
index bfbcf3ff..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task2.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Обращение массива в самом себе
-
-""" Первая же идея: B[k] = A[N-1-k]
- Чем хороша эта идея? Чем плоха?
- Является ли это решением?
-"""
-
-def invert_array(A: list, N: int) -> None:
- """ Переворот массива A задом наперёд
- в рамках индексов от 0 до N-1
- """
- pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/list_tasks/task3.py b/lessons/lesson3/sem3_312/list_tasks/task3.py
deleted file mode 100644
index ae97cd98..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task3.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Линейный поиск в массиве
-
-""" Реализуйте функцию, решающую эту задачу,
- и протестируйте её.
-
- Как можно дополнить документ-строку,
- чтобы она лучше описывала поведение функции?
-"""
-
-def array_search(A: list, N: int, x: int) -> int:
- """ Осуществляет поиск числа x в массиве A
- от 0 до N-1 индекса включительно.
-
- Возвращает индекс элемента x в массиве А
- Или -1, если такого нет.
- """
- pass
diff --git a/lessons/lesson3/sem3_312/list_tasks/task4.py b/lessons/lesson3/sem3_312/list_tasks/task4.py
deleted file mode 100644
index 6045d180..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task4.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import random
-
-#1: Добавление элемента в начале и в конце массива "вручную"
-""" Вам дан массив целых чисел arr, основанный на Python-списке
- (Можно генерировать этот массив случаным образом).
- Добавьте в начало и в конец массива arr число x
- без использования функций append, insert и срезов
- (по аналогии со массивами в C/C++).
-"""
-
-#1.2: реализуйте вставку элемента в массив по индексу:
-def insert(arr: list, length:int, index: int, element: int) -> list:
- """ Вставка значения element в массив arr
- длины length по индексу index.
-
- Для результирующего массива должно быть справедливо следующее:
- arr[index] == element
- len(arr) == length + 1
- """
- pass
-
diff --git a/lessons/lesson3/sem3_312/list_tasks/task5.py b/lessons/lesson3/sem3_312/list_tasks/task5.py
deleted file mode 100644
index 59ac5dc2..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task5.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Циклический сдвиг в массиве (внутри массива, т.е. без доп. массива)
-
-""" Примеры циклического сдвига:
- #1: 1234 -> 4123
- #2: 1234 -> 2341
- #3: 123456 -> 561234
-"""
-
-def cycle_shift(arr: list, N: int, step: int) -> None:
- """ Реализует циклический сдвиг массива arr длины N
- на шаг step. Т.е. если до сдвига элемент x имел
- индекс i, то после сдвига этот же элемент должен
- иметь индекс (i+step).
-
- <<этот комментарий не полный: в нём исключены
- важные аспекты, которые нужно осознать в процессе решения задачи>>
- """
- pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/list_tasks/task6.py b/lessons/lesson3/sem3_312/list_tasks/task6.py
deleted file mode 100644
index 5b5b4bdf..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task6.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Умножение списка на целое число
-
-""" Что произойдёт при умножении списка
- на целое число с помощью оператора '*'?
- Почему? А как работает оператор '+'?
-
-
- А теперь представьте, что мы хотим добавить
- ко всем числам в массиве число x. А потом
- мы ещё хотим умножить все числа в массиве на y.
- Как это сделать?
- Как это сделать короче?
-"""
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/list_tasks/task7.py b/lessons/lesson3/sem3_312/list_tasks/task7.py
deleted file mode 100644
index 1a771ed7..00000000
--- a/lessons/lesson3/sem3_312/list_tasks/task7.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Проверка упорядоченности массива
-# Решите эту задачу наиболее лаконичным образом
-# но без использования встроенных функций
-
-def is_ordered(arr: list, N: int, ascending: bool=True) -> bool:
- """ Проверка массива arr длины N на упорядоченность.
- Если ascending равен True, то производится проверка того,
- что элементы массива расположены в восходящем порядке,
- иначе (когда ascending равен False) проверяется
- убывающий порядок.
-
- Если порядок соблюдён, то возвращается True,
- а иначе - False
- """
- pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task1.py b/lessons/lesson3/sem3_312/previous_problems/task1.py
deleted file mode 100644
index 86bcdf32..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task1.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Найти максимально достижимое число
-# оригинальное условие: https://leetcode.com/problems/find-the-maximum-achievable-number/description/
-
-""" Даны 2 целых числа: num и t
- Назовём число x ДОСТИГАЕМЫМ, если оно может стать равным num
- после применения следующих операций в количестве не большем,
- чем t штук:
- - увеличить или уменьшить x на 1 и одновременно увеличить или уменьшить num на 1
-
- Вернуть максимальное достигаемое число.
-
- #1: num=4, t=1
- > 6
-
- #2: num=3, t=2
- > 7
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task2.py b/lessons/lesson3/sem3_312/previous_problems/task2.py
deleted file mode 100644
index 890e51d6..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task2.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Операция XOR на массиве
-# оригинальное условие: https://leetcode.com/problems/xor-operation-in-an-array/
-
-""" Даны 2 целых числа: n и start
- Определим массив nums так: nums[i] = start + 2 * i (индексация начинается с нуля),
- длина массива nums равна n.
-
- Вычислить битовый XOR всез элементов в массиве nums
-
- #1: n=5, start=0
- > 8
-
- #2: n=4, start=3
- > 8
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task3.py b/lessons/lesson3/sem3_312/previous_problems/task3.py
deleted file mode 100644
index 098c0097..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task3.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Сумма делящихся
-# оригинальное условие: https://leetcode.com/problems/sum-multiples/
-
-""" Дано целое положительное число n. Найти сумму всех чисел
- в диапазоне [1, n] включительно, делящихся на 3, 5 или 7
-
- #1: n=7
- > 21
-
- #2: n=10
- > 40
-
- #3: n=0
- > 30
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task4.py b/lessons/lesson3/sem3_312/previous_problems/task4.py
deleted file mode 100644
index ffb233ce..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task4.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Число после двойного переворота
-#оригинальное условие: https://leetcode.com/problems/a-number-after-a-double-reversal/
-
-""" Операция поворта числа - это перестановка цифр в обратном порядке.
- Например: 2021 -> 1202, а 12300 -> 321, т.к. передние нули не значимы.
-
- Дано целое число num. Переверните его и получите число reversed1,
- затем переверните его и получите reversed2.
- Верните True, если reversed2 равен num, а иначе верните False
-
- #1: num=526
- > True
-
- #2: num=1800
- > False
-
- #3: num=0
- > True
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task5.py b/lessons/lesson3/sem3_312/previous_problems/task5.py
deleted file mode 100644
index c6c00503..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task5.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Максимум из 6 и 9
-# оригинальное условие: https://leetcode.com/problems/maximum-69-number/
-
-""" Дано целое положительное число, состоящее только из цифр "6" и "9".
- Вычислите максимальное число, которое можно получить не более чем
- одним изменением цифры (6->9 или 9->6)
-
- #1: num=9669
- > 9969
-
- #2: num=9996
- > 9999
-
- #3: num=9999
- > 9999
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/previous_problems/task6.py b/lessons/lesson3/sem3_312/previous_problems/task6.py
deleted file mode 100644
index 708081ab..00000000
--- a/lessons/lesson3/sem3_312/previous_problems/task6.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Подъём по лестнице
-# оригинальное условие: https://leetcode.com/problems/climbing-stairs/
-
-""" Вы поднимаетесь по лестнице. Вам нужно совершить n шагов,
- чтобы добраться до вершины.
-
- Каждый раз вы можете совершать либо 1 шаг, либо 2 шага.
- Сколькими различными способами вы можете подняться наверх?
-
- #1: n=2
- > 2
-
- #2: n=3
- > 2
-"""
-
-pass
\ No newline at end of file
diff --git a/lessons/lesson3/sem3_312/tasks.ipynb b/lessons/lesson3/sem3_312/tasks.ipynb
deleted file mode 100644
index 52ad9c1e..00000000
--- a/lessons/lesson3/sem3_312/tasks.ipynb
+++ /dev/null
@@ -1,319 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Iterable, Any"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Разминка: методы через срезы\n",
- "\n",
- "Значительную часть модифицирующих методов списков можно реализовать через срезы. Ваша задача реализовать аналоги методов append(), extend(), insert(), reverse(), используя только срезы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def my_append(list_instance: list, x: Any) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_extend(\n",
- " list_instance: list, expansion: Iterable\n",
- ") -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_insert(list_instance: list, i: int) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_reverse(list_instance: list) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 1: Сложение\n",
- "\n",
- "На вход подаются два списка, репрезентирующие числа в десятичной системе счисления. Элементы списков - числа от 0 до 9, представляющие значения разрядов числа. Самый левый разряд - самый больший. Т.е. число 123 будет представлено списком [1, 2, 3]. Ваша задача - вычислить сумму переданных чисел и вернуть список, представляюзщий эту сумму. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "def sum_two_nums(num1: list[int], num2: list[int]) -> list[int]:\n",
- " # ВАШ КОД\n",
- " return [0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "num1 = [1, 2, 3]\n",
- "num2 = [7, 7]\n",
- "\n",
- "assert sum_two_nums(num1, num2) == [2, 0, 0]\n",
- "assert sum_two_nums(num2, num1) == [2, 0, 0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 2: Объеденяй и не властвуй\n",
- "\n",
- "На вход подан список intervals, где intervals[ i ] = [$start_i$, $stop_i$]. Объедените все пересекающиеся интервалы и верните список непересекающихся интервалов, покрывающий все интервалы из intervals."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:\n",
- " # ВАШ КОД\n",
- " return []"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "intervals = [[1,3],[2,6],[8,10],[15,18]]\n",
- "assert merge_intervals(intervals) == [[1,6],[8,10],[15,18]]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 3: Удалим дубликаты\n",
- "\n",
- "Дан список nums, отсортированный в неубывающем порядке. Ваша задача удалить дублирующиеся элементы **на месте** так, чтобы каждый уникальный элемент массива имел лишь одно вхождение. При этом относительный порядок следования элементов должен остаться без изменений."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "def remove_duplicates(nums: list[int]) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "nums = [1, 1, 2]\n",
- "\n",
- "remove_duplicates(nums)\n",
- "assert nums == [1, 2]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 4: Уникальные пути\n",
- "\n",
- "Вам дано двумерное поле размера m X n. В левом верхнем углу (grid[0][0]) расположен робот. Робот старается добраться до правого нижнего угла (grid[-1][-1]). Робот может ходить только вниз или вправо. \n",
- "\n",
- "Свободные клетки и препятствия помечены в массиве grid 0 и 1 соответственно. Пути робот из верхнего левого угла в правый нижний угол могут проходить только через свободные клетки. \n",
- "\n",
- "Ваша задача - вычислить максимальное возможное количество уникальных путей из левого верхнего угла в правый нижний угол."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "def compute_unique_pathes(grid:list[list[int]]) -> int:\n",
- " # ВАШ КОД\n",
- " answer = 0\n",
- " return answer"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "grid = [\n",
- " [0, 0, 0],\n",
- " [0, 1, 0],\n",
- " [0, 0, 0]\n",
- "]\n",
- "\n",
- "assert compute_unique_pathes(grid) == 2"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 5: Игра в прыжки\n",
- "\n",
- "Вам дан список jumps, состоящий из целых чисел, индексирующийся с нуля и имеющий длину n. Вы начинаете движение с позиции jumps[0]. Каждый элемент списка jumps[i] представляет собой длину максимального прыжка вперед с позиции i: если вы находитесь в позиции i, вы можете прыжком переместиться на любую позицию от i до i + jumps[i].\n",
- "\n",
- "Ваша задача - определить минимальное число прыжков, необходимое для достижения позиции n - 1."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "def jump(jumps: list[int]) -> int:\n",
- " paths = [10000] * len(jumps)\n",
- " paths[0] = 0\n",
- "\n",
- " for i in range(len(jumps) - 1):\n",
- " for j in range(jumps[i]):\n",
- " if j < len(jumps):\n",
- " paths[i + j + 1] = min(paths[i + j + 1], paths[i] + 1)\n",
- " return paths[-1]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "ename": "IndexError",
- "evalue": "list index out of range",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_7998/3492420268.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mjumps\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;31m#3,1,1,4]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# assert jump(jumps) == 2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mjump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjumps\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m/tmp/ipykernel_7998/3729407881.py\u001b[0m in \u001b[0;36mjump\u001b[0;34m(jumps)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjumps\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mj\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjumps\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mpaths\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mj\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpaths\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mj\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpaths\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mpaths\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;31mIndexError\u001b[0m: list index out of range"
- ]
- }
- ],
- "source": [
- "jumps = [2,1]#3,1,1,4]\n",
- "# assert jump(jumps) == 2\n",
- "jump(jumps)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson3/sem3_313/tasks.ipynb b/lessons/lesson3/sem3_313/tasks.ipynb
deleted file mode 100644
index e769d783..00000000
--- a/lessons/lesson3/sem3_313/tasks.ipynb
+++ /dev/null
@@ -1,271 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Iterable, Any"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Разминка: методы через срезы\n",
- "\n",
- "Значительную часть модифицирующих методов списков можно реализовать через срезы. Ваша задача реализовать аналоги методов append(), extend(), insert(), reverse(), используя только срезы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def my_append(list_instance: list, x: Any) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_extend(\n",
- " list_instance: list, expansion: Iterable\n",
- ") -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_insert(list_instance: list, i: int) -> None:\n",
- " # ВАШ КОД\n",
- " pass\n",
- "\n",
- "def my_reverse(list_instance: list) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 1: Сложение\n",
- "\n",
- "На вход подаются два списка, репрезентирующие числа в десятичной системе счисления. Элементы списков - числа от 0 до 9, представляющие значения разрядов числа. Самый левый разряд - самый больший. Т.е. число 123 будет представлено списком [1, 2, 3]. Ваша задача - вычислить сумму переданных чисел и вернуть список, представляюзщий эту сумму. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "def sum_two_nums(num1: list[int], num2: list[int]) -> list[int]:\n",
- " # ВАШ КОД\n",
- " return [0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "num1 = [1, 2, 3]\n",
- "num2 = [7, 7]\n",
- "\n",
- "assert sum_two_nums(num1, num2) == [2, 0, 0]\n",
- "assert sum_two_nums(num2, num1) == [2, 0, 0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 2: Объеденяй и не властвуй\n",
- "\n",
- "На вход подан список intervals, где intervals[ i ] = [$start_i$, $stop_i$]. Объедените все пересекающиеся интервалы и верните список непересекающихся интервалов, покрывающий все интервалы из intervals."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:\n",
- " # ВАШ КОД\n",
- " return []"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "intervals = [[1,3],[2,6],[8,10],[15,18]]\n",
- "assert merge_intervals(intervals) == [[1,6],[8,10],[15,18]]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 3: Удалим дубликаты\n",
- "\n",
- "Дан список nums, отсортированный в неубывающем порядке. Ваша задача удалить дублирующиеся элементы **на месте** так, чтобы каждый уникальный элемент массива имел лишь одно вхождение. При этом относительный порядок следования элементов должен остаться без изменений."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "def remove_duplicates(nums: list[int]) -> None:\n",
- " # ВАШ КОД\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "nums = [1, 1, 2]\n",
- "\n",
- "remove_duplicates(nums)\n",
- "assert nums == [1, 2]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 4: Уникальные пути\n",
- "\n",
- "Вам дано двумерное поле размера m X n. В левом верхнем углу (grid[0][0]) расположен робот. Робот старается добраться до правого нижнего угла (grid[-1][-1]). Робот может ходить только вниз или вправо. \n",
- "\n",
- "Свободные клетки и препятствия помечены в массиве grid 0 и 1 соответственно. Пути робот из верхнего левого угла в правый нижний угол могут проходить только через свободные клетки. \n",
- "\n",
- "Ваша задача - вычислить максимальное возможное количество уникальных путей из левого верхнего угла в правый нижний угол."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "def compute_unique_pathes(grid:list[list[int]]) -> int:\n",
- " # ВАШ КОД\n",
- " answer = 0\n",
- " return answer"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "grid = [\n",
- " [0, 0, 0],\n",
- " [0, 1, 0],\n",
- " [0, 0, 0]\n",
- "]\n",
- "\n",
- "assert compute_unique_pathes(grid) == 2"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Задача 5: Игра в прыжки\n",
- "\n",
- "Вам дан список jumps, состоящий из целых чисел, индексирующийся с нуля и имеющий длину n. Вы начинаете движение с позиции jumps[0]. Каждый элемент списка jumps[i] представляет собой длину максимального прыжка вперед с позиции i: если вы находитесь в позиции i, вы можете прыжком переместиться на любую позицию от i до i + jumps[i].\n",
- "\n",
- "Ваша задача - определить минимальное число прыжков, необходимое для достижения позиции n - 1."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "def jump(jumps: list[int]) -> int:\n",
- " # ВАШ КОД\n",
- " return 0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "jumps = [2,3,1,1,4]\n",
- "assert jump(jumps) == 2"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson4/interactive_conspect.ipynb b/lessons/lesson4/interactive_conspect.ipynb
deleted file mode 100644
index 875fb439..00000000
--- a/lessons/lesson4/interactive_conspect.ipynb
+++ /dev/null
@@ -1,2386 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Типы данных: Строки"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Определение строк\n",
- "\n",
- "На прошлой [лекции](../lesson3/conspect.md) мы начали изучать последовательность: обсудили определение последовательности в Python, обсудили методы, общие для последовательностей, а также рассмотрели таких представителей последовательностей, как список (tuple) и (list). Сегодня мы закончим изучение последовательностей рассмотрением строк. \n",
- "\n",
- "Строки - еще один встроенные тип данных. Строки - это неизменяемые последовательности, состоящие из символов, и используемые для предоставления текстовой информации. Строки предоставляют широкий функционал и большой набор методов, именно поэтому целесообразно обсуждать их отдельно.\n",
- "\n",
- "Начнем с определения строк в коде. В самом простом случае строки - это последовательность символов (в частности пустая), заключенная в одинарные или двойные кавычки:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "string1 = 'this is string literal'\n",
- "string2 = \"this is string literal too\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Поддержка нескольких вариантов создания строкового литерала позволяет использовать неиспользованные( :) ) кавычки в самом литерале. Ну, или если вы убежденный фанат одного типа кавычек, вы все равно можете использовать из в самом литерале с помощью экранирования:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "I'm iron man\n",
- "I'm iron man\n",
- "True\n"
- ]
- }
- ],
- "source": [
- "string1 = \"I'm iron man\"\n",
- "string2 = 'I\\'m iron man'\n",
- "\n",
- "print(string1, string2, sep='\\n')\n",
- "print(string1 == string2)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "*Интересный факт*: более предпочтительным считается использование одинарных кавычек. Однако, на работе вы все равно будете использовать те кавычки, которые прописаны в конфигурации форматтера.\n",
- "\n",
- "Вы также можете создавать многострочные строки (как бы странно это не звучало):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "this is long multi string string\n",
- "and this line is its part\n",
- "\n",
- "this is long multi string string\n",
- "and this line is its part\n"
- ]
- }
- ],
- "source": [
- "string1 = \"this is long multi string string\\n\\\n",
- "and this line is its part\"\n",
- "\n",
- "string2 = (\n",
- " \"this is long multi string string\\n\"\n",
- " \"and this line is its part\"\n",
- ")\n",
- "\n",
- "print(string1, string2, sep='\\n\\n')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако, данный подход к создание многострочных строк не приветствуется. Обычно для этих целей используются тройные кавычки:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "This is very very long line\n",
- "Or not\n",
- "But I just wanted to demonstrate this kind of strings\n",
- "\n"
- ]
- }
- ],
- "source": [
- "string = \"\"\"\\\n",
- "This is very very long line\n",
- "Or not\n",
- "But I just wanted to demonstrate \\\n",
- "this kind of strings\n",
- "\"\"\"\n",
- "\n",
- "print(string)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Вы часто можете увидеть подобные литералы в качестве многострочных комментариев:\n",
- "\n",
- "```Python\n",
- "def my_func(arg1, *args, kwarg1='word'):\n",
- " \"\"\"\n",
- " This function do usefull staff\n",
- " \n",
- " :arg1: is a number or None\n",
- " :kwarg1: is a string, equals 'word' by default\n",
- "\n",
- " :return: None\n",
- " \"\"\"\n",
- " ...\n",
- "```\n",
- "\n",
- "Необходимо обратить внимание на следующее обстоятельство: при создании многострочного строкового литерала с использованием тройных кавычек или с использованием реверс слеша, написание комментариев допустимо только после закрытия кавычек.\n",
- "\n",
- "Также обратите внимание, что в этом варианте у нас нет необходимости в добавлении специального символа перехода но новую строку '\\n', переход осуществляется автоматически при переходе на новую строку. Однако, если мы хотим перейти на новую физическую строку, но при этом продолжить писать на предыдущей физической строке, мы вынуждены использовать реверс-слеш.\n",
- "\n",
- "Помимо символа '\\n' существует ряд символов специального назначения (в англоязычной литературе они называются *escape sequences*). Вот некоторые из них, которые вы будете использовать чаще всего:\n",
- "\n",
- "|Символ|Описание|\n",
- "|--|--|\n",
- "|\\\\|продолжение написания на текуще физической строке, несмотря на фактическое наличие символа перехода на новую строку|\n",
- "|\\\\\\\\ |реверс слеш|\n",
- "|\\\\'|одинарная кавычка|\n",
- "|\\\\\"|двойная кавычка|\n",
- "|\\\\n|переход на новую строку|\n",
- "|\\\\t|таб|\n",
- "\n",
- "В обычных строках использование реверс-слеша без экранирования невозможно. Однако вы можете создать так называемую *сырую* строку, в которой все специальные символы теряют свое значение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "C:\\Users\\User\\Desktop\\teaching\n"
- ]
- }
- ],
- "source": [
- "raw_string = r'C:\\Users\\User\\Desktop\\teaching'\n",
- "\n",
- "print(raw_string)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Создание сырых строк аналогично созданию обычных строк, единственное - необходимо написать букву r или R перед самим литералом. Обычно данный тип строк используется для определения путей к файлам. Однако, подобная практика не рекомендуется. Для данной цели используйте или модуль pathlib, или модуль os.path. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Строки как последовательности\n",
- "\n",
- "### Конкатенация и повторение\n",
- "\n",
- "Как упоминалось ранее, строки являются последовательностями. Из этого следует, что мы можем конкатенировать и повторять строки:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "abobaabobaabobaabobaaboba\n",
- "aboba add this part\n"
- ]
- }
- ],
- "source": [
- "addition = \" add this part\"\n",
- "string = input('Enter some string: ')\n",
- "\n",
- "repeated = string * 5\n",
- "concatenated = string + addition\n",
- "\n",
- "print(repeated, concatenated, sep='\\n')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Т.к. строки неизменяемы, любые операции со строками приводят к созданию нового объекта строки. Даже такие операции, как составное присваивание, не изменяют текущий объект, а создают новый и перепривязывают старую ссылку к новому объекту:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "False\n"
- ]
- }
- ],
- "source": [
- "string = 'this is old string '\n",
- "id_old = id(string)\n",
- "\n",
- "string *= 5\n",
- "id_new = id(string)\n",
- "\n",
- "print(id_old == id_new)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Оператор in\n",
- "\n",
- "Также из того факта, что строки - последовательности, следует, что мы можем использовать оператор `in`, для проверки вхождения того или инога символа в строку:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n"
- ]
- }
- ],
- "source": [
- "string = 'what a wonderful world'\n",
- "print('a' in string)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Но, что гораздо интереснее, с помощью оператора `in` мы также можем не просто проверить наличие одного символа в строке, но и наличие целой подстроки: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n"
- ]
- }
- ],
- "source": [
- "string = 'what a wonderful world'\n",
- "print('world' in string)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Индексирование\n",
- "\n",
- "Ну и наконец, поскольку строки - последовательности, мы можем их индексировать. Два важных момента: первое - поскольку строки неизменяемы, мы не можем изменять как значения отдельно взятых символов, так и значения подстрок; второе - индексирование строк аналогично индексированию списков и кортежей, т.е. мы можем использовать положительные и отрицательные целые числа и срезы. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "h\n",
- "d\n",
- "dlrow lufrednow a tahw\n",
- "at a w\n",
- "\n",
- "String is immutable type so you can't change its value\n"
- ]
- }
- ],
- "source": [
- "string = 'what a wonderful world'\n",
- "\n",
- "print(\n",
- " string[1],\n",
- " string[-1],\n",
- " string[::-1],\n",
- " string[2:8],\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")\n",
- "\n",
- "try:\n",
- " string[0] = 'W'\n",
- "\n",
- "except TypeError:\n",
- " print(\n",
- " 'String is immutable type so you '\n",
- " \"can't change its value\"\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Методы строк\n",
- "\n",
- "Стоки обладают внушительным количетсвом методов. Описание всех этих методов превратилось бы в пересказывание документации, поэтому в данном разделе будут описаны часто используемые методы с основными сценариями их применения.\n",
- "\n",
- "Поскольку невозможно все время держать в голове сигнатуры всех методов, стоит отдельно упомянуть о встроенной функции `help()`, которая принимает на вход объект Python и возвращает его док-стринг. Большинство встроенных функций и методов встроенных типов данных имеют док-стринг, а потому вы можете спокойно использовать эту функцию для освежения в памяти сигнатур функций и их логики:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function id in module builtins:\n",
- "\n",
- "id(obj, /)\n",
- " Return the identity of an object.\n",
- " \n",
- " This is guaranteed to be unique among simultaneously existing objects.\n",
- " (CPython uses the object's memory address.)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Манипуляции с регистром\n",
- "\n",
- "К методам, осуществляющим манипуляции с регистром относятся следующие методы: `upper()`, `lower()`, `title()` (еще к этой группе методов относятся методы `swapcase()` и `capitalize()`, но я не встречался с их использованием в продуктовом коде, поэтому изучение данных методов оставляю читателю). Давайте сначала разберемся с функционированием данных методов, а затем рассмотрим практический пример и мотивируем их изучение.\n",
- "\n",
- "**upper()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "upper(self, /)\n",
- " Return a copy of the string converted to uppercase.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.upper)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'GVIDO VAN ROSSUM'"
- ]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "name_surname = 'Gvido Van Rossum'\n",
- "name_surname.upper()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**lower()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "lower(self, /)\n",
- " Return a copy of the string converted to lowercase.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.lower)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'gvido van rossum'"
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "name_surname = 'Gvido Van Rossum'\n",
- "name_surname.lower()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**title**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "title(self, /)\n",
- " Return a version of the string where each word is titlecased.\n",
- " \n",
- " More specifically, words start with uppercased characters and all remaining\n",
- " cased characters have lower case.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.title)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Gvido Van Rossum\n",
- "Gvido Van Rossum\n"
- ]
- }
- ],
- "source": [
- "name_surname = 'gvido van rossum'\n",
- "name_surname_up = name_surname.upper()\n",
- "\n",
- "print(\n",
- " name_surname.title(),\n",
- " name_surname_up.title(),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример на upper/lower**:\n",
- "\n",
- "Наверное, самое очевидное использование методов манипуляции с кейсом - сортировка слов в лексикографическом порядке. Поскольку при сортировки учитываются именно ASCII коды символов, попытка отсортировать список слов в разном регистре приведет к очевидному, но некорректному с точки зрения языка результату:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['Alabama', 'Appel', 'CIA', 'Paris', 'apple', 'border', 'two', 'zero']\n"
- ]
- }
- ],
- "source": [
- "words = ['CIA', 'border', 'Alabama', 'apple', 'Appel', 'zero', 'two', 'Paris']\n",
- "words.sort()\n",
- "\n",
- "print(words)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Мы все-таки хотели бы видеть, чтобы, начинающиеся с одинаковых последовательностей символов, были близко друг к другу, причем имена собственные встречались раньше. Чтобы исправить данное недоразумение, мы можем перевести все слова в один регистр и только потом отсортировать:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['Alabama', 'Appel', 'apple', 'border', 'CIA', 'Paris', 'two', 'zero']\n"
- ]
- }
- ],
- "source": [
- "words = ['CIA', 'border', 'Alabama', 'apple', 'Appel', 'zero', 'two', 'Paris']\n",
- "words.sort(key=str.upper)\n",
- "\n",
- "print(words)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Теперь мы имеем корректный с точки зрения языка результат."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Сферический пример в вакууме**:\n",
- "\n",
- "Предположим у нас есть некоторый сервис. Прежде чем пользоваться нашим сервисом, пользователи должны зарегистироваться: предоставить информацию о почте, придумать имя пользователя и пароль. Мы сохраняем данную информацию в базе данных. Причем, мы не даем зарегистироваться пользователю, если пользователь придумал неуникальный никнейм или предоставил уже имеющийся в базе почтовый адрес. С точки зрения нашей системы никнеймы: `BaNaNa`, `Banana` и `banana` - одинаковые. Как быть? А вот как: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [],
- "source": [
- "from dataclasses import dataclass, asdict\n",
- "from typing import Union"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [],
- "source": [
- "@dataclass\n",
- "class UserInfo:\n",
- " username: str\n",
- " email: str\n",
- " password: str\n",
- "\n",
- " def get_dict(self):\n",
- " return {key: val for key, val in asdict(self).items()}\n",
- "\n",
- "\n",
- "class DataBase:\n",
- " _name: str\n",
- " _users: list[UserInfo] = []\n",
- " _nicks_unique: list[str] = []\n",
- " _emails_unique: list[str] = []\n",
- "\n",
- " def __init__(self, db_name: str) -> None:\n",
- " self._name = db_name\n",
- "\n",
- " def add_users(self, new_users: list[UserInfo]) -> None:\n",
- " for user in new_users:\n",
- " self._add_user(user)\n",
- "\n",
- " def _add_user(self, user: UserInfo) -> None:\n",
- " if user.username.lower() in self._nicks_unique:\n",
- " raise RuntimeError(\n",
- " 'username should be unique; '\n",
- " f'user with nick: {user.username.lower()} '\n",
- " 'is already exist'\n",
- " )\n",
- " \n",
- " if user.email in self._emails_unique:\n",
- " raise RuntimeError(\n",
- " 'email should be unique; '\n",
- " f'user with email: {user.email} is already exist'\n",
- " )\n",
- " \n",
- " self._nicks_unique.append(user.username.lower())\n",
- " self._emails_unique.append(user.email)\n",
- " self._users.append(user)\n",
- "\n",
- " @property\n",
- " def table(self) -> list[dict]:\n",
- " return [user.get_dict() for user in self._users]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "def print_database(database: DataBase) -> None:\n",
- " for user_dict in database.table:\n",
- " print(user_dict)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'username': 'avshugan', 'email': 'avshugan.email@mail.ru', 'password': '12345678'}\n",
- "{'username': 'Ampiro', 'email': 'ampiro.email@gmail.com', 'password': 'qwerty228'}\n"
- ]
- }
- ],
- "source": [
- "data_base = DataBase('clients')\n",
- "print_database(data_base)\n",
- "\n",
- "new_users = [\n",
- " UserInfo(\n",
- " username='avshugan',\n",
- " email='avshugan.email@mail.ru',\n",
- " password='12345678'\n",
- " ),\n",
- " UserInfo(\n",
- " username='Ampiro',\n",
- " email='ampiro.email@gmail.com',\n",
- " password='qwerty228'\n",
- " )\n",
- "]\n",
- "data_base.add_users(new_users)\n",
- "print_database(data_base)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [
- {
- "ename": "RuntimeError",
- "evalue": "username should be unique; user with nick: ampiro is already exist",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson4\\interactive_conspect.ipynb Cell 44\u001b[0m line \u001b[0;36m9\n\u001b[0;32m 1\u001b[0m new_users \u001b[39m=\u001b[39m [\n\u001b[0;32m 2\u001b[0m UserInfo(\n\u001b[0;32m 3\u001b[0m username\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mAMPIRO\u001b[39m\u001b[39m'\u001b[39m,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 6\u001b[0m )\n\u001b[0;32m 7\u001b[0m ]\n\u001b[1;32m----> 9\u001b[0m data_base\u001b[39m.\u001b[39;49madd_users(new_users)\n",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson4\\interactive_conspect.ipynb Cell 44\u001b[0m line \u001b[0;36m2\n\u001b[0;32m 20\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39madd_users\u001b[39m(\u001b[39mself\u001b[39m, new_users: \u001b[39mlist\u001b[39m[UserInfo]) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 21\u001b[0m \u001b[39mfor\u001b[39;00m user \u001b[39min\u001b[39;00m new_users:\n\u001b[1;32m---> 22\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_add_user(user)\n",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson4\\interactive_conspect.ipynb Cell 44\u001b[0m line \u001b[0;36m2\n\u001b[0;32m 24\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_add_user\u001b[39m(\u001b[39mself\u001b[39m, user: UserInfo) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 25\u001b[0m \u001b[39mif\u001b[39;00m user\u001b[39m.\u001b[39musername\u001b[39m.\u001b[39mlower() \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_nicks_unique:\n\u001b[1;32m---> 26\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m(\n\u001b[0;32m 27\u001b[0m \u001b[39m'\u001b[39m\u001b[39musername should be unique; \u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 28\u001b[0m \u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39muser with nick: \u001b[39m\u001b[39m{\u001b[39;00muser\u001b[39m.\u001b[39musername\u001b[39m.\u001b[39mlower()\u001b[39m}\u001b[39;00m\u001b[39m \u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 29\u001b[0m \u001b[39m'\u001b[39m\u001b[39mis already exist\u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 30\u001b[0m )\n\u001b[0;32m 32\u001b[0m \u001b[39mif\u001b[39;00m user\u001b[39m.\u001b[39memail \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_emails_unique:\n\u001b[0;32m 33\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m(\n\u001b[0;32m 34\u001b[0m \u001b[39m'\u001b[39m\u001b[39memail should be unique; \u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 35\u001b[0m \u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39muser with email: \u001b[39m\u001b[39m{\u001b[39;00muser\u001b[39m.\u001b[39memail\u001b[39m}\u001b[39;00m\u001b[39m is already exist\u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 36\u001b[0m )\n",
- "\u001b[1;31mRuntimeError\u001b[0m: username should be unique; user with nick: ampiro is already exist"
- ]
- }
- ],
- "source": [
- "new_users = [\n",
- " UserInfo(\n",
- " username='AMPIRO',\n",
- " email='AMPIRO.email@yandex.ru',\n",
- " password='notqw15erTY'\n",
- " )\n",
- "]\n",
- "\n",
- "data_base.add_users(new_users)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Проверки символов\n",
- "\n",
- "К данным методом отсносятся следующие: `isalnum()`, `isalpha()`, `isdigit()`, `isspace()`, `istitle()` (на самостоятельное изучение предоставлены методы: `isupper()`, `islower()`, `isnumeric()`, `isprintable()`, `isidentifier()`, `isdecimal()`). Данные методы позволяют упростить проверку строки на удовлетворение всем условиям. \n",
- "\n",
- "**isalnum()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "isalnum(self, /)\n",
- " Return True if the string is an alpha-numeric string, False otherwise.\n",
- " \n",
- " A string is alpha-numeric if all characters in the string are alpha-numeric and\n",
- " there is at least one character in the string.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.isalnum)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "string = 'ab123'\n",
- "print(string.isalnum())\n",
- "\n",
- "string = 'ab,!^'\n",
- "print(string.isalnum())\n",
- "\n",
- "string = 'hello world'\n",
- "print(string.isalnum())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**isalpha()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "isalpha(self, /)\n",
- " Return True if the string is an alphabetic string, False otherwise.\n",
- " \n",
- " A string is alphabetic if all characters in the string are alphabetic and there\n",
- " is at least one character in the string.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.isalpha)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "string = 'Hello'\n",
- "print(string.isalpha())\n",
- "\n",
- "string = '123'\n",
- "print(string.isalpha())\n",
- "\n",
- "string = 'Hello, world!'\n",
- "print(string.isalpha())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**isdigit()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "isdigit(self, /)\n",
- " Return True if the string is a digit string, False otherwise.\n",
- " \n",
- " A string is a digit string if all characters in the string are digits and there\n",
- " is at least one character in the string.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.isdigit)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "False\n",
- "True\n",
- "False\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "string = 'Hello'\n",
- "print(string.isdigit())\n",
- "\n",
- "string = '123'\n",
- "print(string.isdigit())\n",
- "\n",
- "string = '-123'\n",
- "print(string.isdigit())\n",
- "\n",
- "string = 'Hello, world!'\n",
- "print(string.isdigit())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**isspace()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "isspace(self, /)\n",
- " Return True if the string is a whitespace string, False otherwise.\n",
- " \n",
- " A string is whitespace if all characters in the string are whitespace and there\n",
- " is at least one character in the string.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.isspace)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "string = '\\n\\t '\n",
- "print(string.isspace())\n",
- "\n",
- "string = ' aboba '\n",
- "print(string.isspace())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**istitle()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "istitle(self, /)\n",
- " Return True if the string is a title-cased string, False otherwise.\n",
- " \n",
- " In a title-cased string, upper- and title-case characters may only\n",
- " follow uncased characters and lowercase characters only cased ones.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.istitle)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n",
- "False\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "string = 'Bjarne Stroustrup'\n",
- "print(string.istitle())\n",
- "\n",
- "string = 'bjarne Stroustrup'\n",
- "print(string.istitle())\n",
- "\n",
- "string = 'Bjarne stroustrup'\n",
- "print(string.istitle())\n",
- "\n",
- "string = 'bjarne stroustrup'\n",
- "print(string.istitle())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования:**\n",
- "\n",
- "В данном примере я позволю себе сослаться на сферический пример в вакууме из предыдущего пункта. Просто теперь, используя данные методы, мы можем наложить более строгие ограничения на имена наших пользователей. Допустим, теперь мы требуем, чтобы уникальные ники состояли исключительно из букв английского алфавита и арабских цифр. В коде это будет выглядеть так:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [],
- "source": [
- "username = input()\n",
- "\n",
- "if not username.isalnum():\n",
- " raise ValueError(\n",
- " f'invalid username: {username}; '\n",
- " 'username should consist of digits and english letters'\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Специальные методы форматирования\n",
- "\n",
- "Следующие методы относятся к специальным методом форматирования строки: `center()`, `ljust()`, `lstrip()`, `rjust()`, `rstrip()`, `strip()`."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**center()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "center(self, width, fillchar=' ', /)\n",
- " Return a centered string of length width.\n",
- " \n",
- " Padding is done using the specified fill character (default is a space).\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.center)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " SUCCESSFULLY BUILD \n",
- "-------------------------------SUCCESSFULLY BUILD-------------------------------\n",
- "SUCCESSFULLY BUILD\n"
- ]
- }
- ],
- "source": [
- "string = 'SUCCESSFULLY BUILD'\n",
- "\n",
- "print(\n",
- " string.center(80),\n",
- " string.center(80, '-'),\n",
- " string.center(10, '='),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**ljust() / rjust()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "ljust(self, width, fillchar=' ', /)\n",
- " Return a left-justified string of length width.\n",
- " \n",
- " Padding is done using the specified fill character (default is a space).\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.ljust)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "rjust(self, width, fillchar=' ', /)\n",
- " Return a right-justified string of length width.\n",
- " \n",
- " Padding is done using the specified fill character (default is a space).\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.rjust)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "SUCCESSFULLY BUILD \n",
- "SUCCESSFULLY BUILD..........................................\n",
- "SUCCESSFULLY BUILD\n",
- "\n",
- " SUCCESSFULLY BUILD\n",
- "..........................................SUCCESSFULLY BUILD\n",
- "SUCCESSFULLY BUILD\n"
- ]
- }
- ],
- "source": [
- "string = 'SUCCESSFULLY BUILD'\n",
- "\n",
- "print(\n",
- " string.ljust(60),\n",
- " string.ljust(60, '.'),\n",
- " string.ljust(10, '='),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")\n",
- "\n",
- "print(\n",
- " string.rjust(60),\n",
- " string.rjust(60, '.'),\n",
- " string.rjust(10, '='),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования:**\n",
- "\n",
- "Данные методы могут оказаться полезными при логировании некоторой информации. Например, у вас выполняется некоторая долгая работа, чтобы понимать, что все в порядке, и ничего не вылетело, логично выводить в консоль или записывать в специальный файл отладочную информацию, которая может быть отформотирована специальным образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 74,
- "metadata": {},
- "outputs": [],
- "source": [
- "from time import sleep"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 92,
- "metadata": {},
- "outputs": [],
- "source": [
- "def simulate_some_job() -> None:\n",
- "\n",
- " success_status = 'SUCCESS'\n",
- "\n",
- " print('START BUILDING'.center(80, '='))\n",
- " message = 'compile files'\n",
- " print(message.ljust(80, '.'), end='')\n",
- " sleep(3)\n",
- " message = message.ljust(80 - len(success_status), '.') + success_status\n",
- " print('\\r' + message)\n",
- " message = 'prepare libs'\n",
- " print(message.ljust(80, '.'), end='')\n",
- " sleep(1)\n",
- " message = message.ljust(80 - len(success_status), '.') + success_status\n",
- " print('\\r' + message)\n",
- " message = 'link files'\n",
- " print(message.ljust(80, '.'), end='')\n",
- " sleep(2)\n",
- " message = message.ljust(80 - len(success_status), '.') + success_status\n",
- " print('\\r' + message)\n",
- " print('SUCCESSFULLY BUILD'.center(80, '='))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 93,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "=================================START BUILDING=================================\n",
- "compile files............................................................SUCCESS\n",
- "prepare libs.............................................................SUCCESS\n",
- "link files...............................................................SUCCESS\n",
- "===============================SUCCESSFULLY BUILD===============================\n"
- ]
- }
- ],
- "source": [
- "simulate_some_job()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**strip()/ lstrip()/ rstrip()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 94,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "strip(self, chars=None, /)\n",
- " Return a copy of the string with leading and trailing whitespace removed.\n",
- " \n",
- " If chars is given and not None, remove characters in chars instead.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.strip)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 97,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "lstrip(self, chars=None, /)\n",
- " Return a copy of the string with leading whitespace removed.\n",
- " \n",
- " If chars is given and not None, remove characters in chars instead.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.lstrip)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 100,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "rstrip(self, chars=None, /)\n",
- " Return a copy of the string with trailing whitespace removed.\n",
- " \n",
- " If chars is given and not None, remove characters in chars instead.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.rstrip)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 102,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "aboba\n",
- "aboba \n",
- " aboba\n",
- "\n",
- "defgfed\n",
- "defgfedcba\n",
- "abcdefgfed\n",
- "\n"
- ]
- }
- ],
- "source": [
- "string = ' aboba '\n",
- "print(\n",
- " string.strip(),\n",
- " string.lstrip(),\n",
- " string.rstrip(),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")\n",
- "\n",
- "string = 'abcdefgfedcba'\n",
- "print(\n",
- " string.strip('abc'),\n",
- " string.lstrip('abc'),\n",
- " string.rstrip('abc'),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования:**\n",
- "\n",
- "Данные методы могут оказаться полезными, когда, скажем, вам известно форматирование строки, и вы хотите извлечь из строки определенную информацию, однако в строке может встретиться случайное число ведущих пробелов. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 106,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " id\n",
- "1234567\n"
- ]
- }
- ],
- "source": [
- "id_position = slice(4, 11)\n",
- "string = ' id: 1234567; name: Mike'\n",
- "\n",
- "print(\n",
- " string[id_position],\n",
- " string.lstrip()[id_position],\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Деление и объединение\n",
- "\n",
- "К методам деления и объединения строк, которые мы будем рассматривать относятся следующие методы: `split()`, `join()`.\n",
- "\n",
- "**split()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 107,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "split(self, /, sep=None, maxsplit=-1)\n",
- " Return a list of the substrings in the string, using sep as the separator string.\n",
- " \n",
- " sep\n",
- " The separator used to split the string.\n",
- " \n",
- " When set to None (the default value), will split on any whitespace\n",
- " character (including \\\\n \\\\r \\\\t \\\\f and spaces) and will discard\n",
- " empty strings from the result.\n",
- " maxsplit\n",
- " Maximum number of splits (starting from the left).\n",
- " -1 (the default value) means no limit.\n",
- " \n",
- " Note, str.split() is mainly useful for data that has been intentionally\n",
- " delimited. With natural text that includes punctuation, consider using\n",
- " the regular expression module.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.split)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 114,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['Hello', 'wonderful', 'world', '!']\n",
- "['Hello', '', 'wonderful', 'world', '', '', '', '!']\n",
- "['Hello ', 'nderful ', 'rld !']\n",
- "['Hello', 'wonderful', 'world !']\n"
- ]
- }
- ],
- "source": [
- "string = 'Hello wonderful world !'\n",
- "print(\n",
- " string.split(),\n",
- " string.split(' '),\n",
- " string.split('wo'),\n",
- " string.split(maxsplit=2),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования:**\n",
- "\n",
- "Метод split() может показаться очень полезным, для разбиения текста на отдельные слова, что и было продемонстрировано выше. В частности, этот метод будет полезен вам на курсе по машинному обучению."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**join()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 115,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "join(self, iterable, /)\n",
- " Concatenate any number of strings.\n",
- " \n",
- " The string whose method is called is inserted in between each given string.\n",
- " The result is returned as a new string.\n",
- " \n",
- " Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.join)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 116,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "apple grape orange\n",
- "apple; grape; orange\n",
- "applegrapeorange\n"
- ]
- }
- ],
- "source": [
- "words = ['apple', 'grape', 'orange']\n",
- "print(\n",
- " ' '.join(words),\n",
- " '; '.join(words),\n",
- " ''.join(words),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования**:\n",
- "\n",
- "Данный метод может показаться полезным для формирования строк из перечислений для логирования или сообщений об ошибки. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Подстроки\n",
- "\n",
- "В данном разделе перечислены методы работы с подстроками.\n",
- "\n",
- "**endswith()/ startswith()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 117,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "endswith(...)\n",
- " S.endswith(suffix[, start[, end]]) -> bool\n",
- " \n",
- " Return True if S ends with the specified suffix, False otherwise.\n",
- " With optional start, test S beginning at that position.\n",
- " With optional end, stop comparing S at that position.\n",
- " suffix can also be a tuple of strings to try.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.endswith)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 120,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "startswith(...)\n",
- " S.startswith(prefix[, start[, end]]) -> bool\n",
- " \n",
- " Return True if S starts with the specified prefix, False otherwise.\n",
- " With optional start, test S beginning at that position.\n",
- " With optional end, stop comparing S at that position.\n",
- " prefix can also be a tuple of strings to try.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.startswith)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 125,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "True\n",
- "False\n",
- "\n",
- "True\n",
- "True\n",
- "False\n",
- "\n"
- ]
- }
- ],
- "source": [
- "string = 'hello, world'\n",
- "\n",
- "print(\n",
- " string.endswith('world'),\n",
- " string.endswith('world', 6),\n",
- " string.endswith('world', 6, 9),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")\n",
- "\n",
- "print(\n",
- " string.startswith('hello'),\n",
- " string.startswith('hello', -12, -5),\n",
- " string.startswith('hello', 6, 9),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**find()/ rfind()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 126,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "find(...)\n",
- " S.find(sub[, start[, end]]) -> int\n",
- " \n",
- " Return the lowest index in S where substring sub is found,\n",
- " such that sub is contained within S[start:end]. Optional\n",
- " arguments start and end are interpreted as in slice notation.\n",
- " \n",
- " Return -1 on failure.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.find)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 129,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "rfind(...)\n",
- " S.rfind(sub[, start[, end]]) -> int\n",
- " \n",
- " Return the highest index in S where substring sub is found,\n",
- " such that sub is contained within S[start:end]. Optional\n",
- " arguments start and end are interpreted as in slice notation.\n",
- " \n",
- " Return -1 on failure.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.rfind)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 134,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2\n",
- "4\n",
- "2\n",
- "-1\n",
- "\n",
- "4\n",
- "2\n",
- "4\n",
- "-1\n"
- ]
- }
- ],
- "source": [
- "string = 'banana'\n",
- "\n",
- "print(\n",
- " string.find('na'),\n",
- " string.find('na', 3),\n",
- " string.find('na', -4),\n",
- " string.find('na', 0, 3),\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- ")\n",
- "\n",
- "print(\n",
- " string.rfind('na'),\n",
- " string.rfind('na', 0, 5),\n",
- " string.rfind('na', -4),\n",
- " string.rfind('na', 0, 3),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Использование**:\n",
- "\n",
- "Очевидно, для проверки, обладает ли строка тем или иным суффиксом/префиксом.\n",
- "\n",
- "**index()/ rindex()**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 135,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "index(...)\n",
- " S.index(sub[, start[, end]]) -> int\n",
- " \n",
- " Return the lowest index in S where substring sub is found,\n",
- " such that sub is contained within S[start:end]. Optional\n",
- " arguments start and end are interpreted as in slice notation.\n",
- " \n",
- " Raises ValueError when the substring is not found.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.index)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 136,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "rindex(...)\n",
- " S.rindex(sub[, start[, end]]) -> int\n",
- " \n",
- " Return the highest index in S where substring sub is found,\n",
- " such that sub is contained within S[start:end]. Optional\n",
- " arguments start and end are interpreted as in slice notation.\n",
- " \n",
- " Raises ValueError when the substring is not found.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.rindex)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 142,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2\n",
- "4\n",
- "2\n",
- "substring not found\n",
- "\n",
- "4\n",
- "2\n",
- "4\n",
- "substring not found\n"
- ]
- }
- ],
- "source": [
- "string = 'banana'\n",
- "\n",
- "try:\n",
- " print(\n",
- " string.index('na'),\n",
- " string.index('na', 3),\n",
- " string.index('na', -4),\n",
- " sep='\\n'\n",
- " )\n",
- " print(string.index('na', 0, 3))\n",
- "except ValueError as error:\n",
- " print(str(error), end='\\n\\n')\n",
- "\n",
- "try:\n",
- " print(\n",
- " string.rindex('na'),\n",
- " string.rindex('na', 0, 5),\n",
- " string.rindex('na', -4),\n",
- " sep='\\n'\n",
- " )\n",
- " print(string.rindex('na', 0, 3))\n",
- "except ValueError as error:\n",
- " print(str(error))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### replace()\n",
- "\n",
- "Метод replace не вошел ни в одну из категорий, но при этом является очень важным и частоиспользуемым."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 143,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "replace(self, old, new, count=-1, /)\n",
- " Return a copy with all occurrences of substring old replaced by new.\n",
- " \n",
- " count\n",
- " Maximum number of occurrences to replace.\n",
- " -1 (the default value) means replace all occurrences.\n",
- " \n",
- " If the optional argument count is given, only the first count occurrences are\n",
- " replaced.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(str.replace)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Пример использования:**\n",
- "\n",
- "Вам пригодиться метод replace для наивной токенизации текстов в NLP(natural language processing). Для обучения некоторых глубоких моделей вам может потребоваться разбить текст на слова. Если сделать это с помощью split, то частью слов будут являться знаки препинания, что, разумеется, нежелательно. Тут вам и приходит на помощь метод replace, благодаря которому можно заменить нежелательные символы на пустые строки. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 148,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['This', 'is', 'a', 'real', 'text.', 'It', 'has', 'commas', 'and', 'dots.', 'For', 'example,', 'this', 'sentance', 'has', 'comma', 'and', 'dot.']\n",
- "\n",
- "This is a real text It has commas and dots For example this sentance has comma and dot\n",
- "['This', 'is', 'a', 'real', 'text', 'It', 'has', 'commas', 'and', 'dots', 'For', 'example', 'this', 'sentance', 'has', 'comma', 'and', 'dot']\n"
- ]
- }
- ],
- "source": [
- "string = (\n",
- " 'This is a real text. It has commas and dots. '\n",
- " 'For example, this sentance has comma and dot.'\n",
- ")\n",
- "\n",
- "print(string.split(), end='\\n\\n')\n",
- "\n",
- "delete_signs = ['.', ',']\n",
- "\n",
- "for delete_sign in delete_signs:\n",
- " string = string.replace(delete_sign, '')\n",
- "\n",
- "print(\n",
- " string,\n",
- " string.split(),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Наборы символов\n",
- "\n",
- "В модуле `string` стандартной библиотеки Python содержаться все необходимы наборы символов, которые могут быть вам полезны. Так, для примера выше вам не нужно создавать отдельный список с перечислением всех знаков пунктуации, а достаточно использовать соответствующий набор из модуля string.\n",
- "\n",
- "**Важно**: не перезаписывайте значения переменных из модуля string. Многие методы строк полагаются на эти константы, их перезапись может привести к плачевным последствиям в вашем коде. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 149,
- "metadata": {},
- "outputs": [],
- "source": [
- "import string"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 151,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n",
- "abcdefghijklmnopqrstuvwxyz\n",
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n",
- "0123456789\n",
- "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n"
- ]
- }
- ],
- "source": [
- "print(\n",
- " string.ascii_letters,\n",
- " string.ascii_lowercase,\n",
- " string.ascii_uppercase,\n",
- " string.digits,\n",
- " string.punctuation,\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Форматирование строк\n",
- "\n",
- "В Python существует возможность удобного форматирования строк: вставки в определенное место строки вычисленных значений в определенном формате. Данная возможность может быть очень полезна для целей логирования информации или создания полезных сообщений об ошибках. Форматирование строки может осуществляться несколькими способами: с помощью метода `format()`, который будет разобран в этом разделе, с помощью f-строк, и с помощью олдскульного С-подобного форматирования, которое мы рассматривать не будем."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 163,
- "metadata": {},
- "outputs": [],
- "source": [
- "from random import randint\n",
- "from typing import Any\n",
- "from enum import Enum\n",
- "\n",
- "class Statuses(Enum):\n",
- " success = 'success'\n",
- " fail = 'fail'"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 223,
- "metadata": {},
- "outputs": [],
- "source": [
- "def simulate_calculation(value):\n",
- " time = randint(1, 20)\n",
- " time_limit = 10\n",
- "\n",
- " if time > time_limit:\n",
- " return Statuses.fail, None, time\n",
- "\n",
- " return Statuses.success, value, time "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Форматирование в простейшем варианте"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 208,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[INFO]: operation status: None; operation result: 15; taken time: Statuses.fail;\n"
- ]
- }
- ],
- "source": [
- "msg_template = (\n",
- " '[INFO]: operation status: {}; operation result: {}; '\n",
- " 'taken time: {};'\n",
- ")\n",
- "\n",
- "result = simulate_calculation(42)\n",
- "print(msg_template.format(*result))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 209,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[INFO]: operation status: None; operation result: 20; taken time: Statuses.fail;\n"
- ]
- }
- ],
- "source": [
- "msg_template = (\n",
- " '[INFO]: operation status: {2}; operation result: {1}; '\n",
- " 'taken time: {0};'\n",
- ")\n",
- "\n",
- "result = simulate_calculation(42)[::-1]\n",
- "print(msg_template.format(*result))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 210,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[INFO]: operation status: success; operation result: 2; taken time: 7.4;\n"
- ]
- }
- ],
- "source": [
- "msg_template = (\n",
- " '[INFO]: operation status: {}; operation result: {[1]}; '\n",
- " 'taken time: {};'\n",
- ")\n",
- "\n",
- "print(msg_template.format('success', [1, 2], 7.4))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 212,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[INFO]: operation status: success; operation result: 42; taken time: 2;\n"
- ]
- }
- ],
- "source": [
- "msg_template = (\n",
- " '[INFO]: operation status: {.value}; operation result: {}; '\n",
- " 'taken time: {};'\n",
- ")\n",
- "\n",
- "result = simulate_calculation(42)\n",
- "print(msg_template.format(*result))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Отступы"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 226,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "calculate()........................................................fail\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'calculate(){.value:.>60}'\n",
- "status, _, _ = simulate_calculation(42)\n",
- "\n",
- "print(msg_template.format(status))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 227,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "calculate()success.....................................................\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'calculate(){.value:.<60}'\n",
- "status, _, _ = simulate_calculation(42)\n",
- "\n",
- "print(msg_template.format(status))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 228,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "calculate()............................fail............................\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'calculate(){.value:.^60}'\n",
- "status, _, _ = simulate_calculation(42)\n",
- "\n",
- "print(msg_template.format(status))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Точность"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 239,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "VALUE: 3.142;\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'VALUE: {:.3f};'\n",
- "\n",
- "_, value, _ = simulate_calculation(3.141592)\n",
- "\n",
- "if value:\n",
- " print(msg_template.format(value))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 240,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "VALUE: 3.14;\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'VALUE: {:.3g};'\n",
- "\n",
- "_, value, _ = simulate_calculation(3.141592)\n",
- "\n",
- "if value:\n",
- " print(msg_template.format(value))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 241,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "VALUE: str;\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'VALUE: {:.3s};'\n",
- "\n",
- "_, value, _ = simulate_calculation('string')\n",
- "\n",
- "if value:\n",
- " print(msg_template.format(value))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Вложенное форматирование"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 243,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "VALUE: 3.1416;\n",
- "VALUE: 3.142;\n",
- "VALUE: 3.14;\n"
- ]
- }
- ],
- "source": [
- "msg_template = 'VALUE: {number: >10.{precision}f};'\n",
- "\n",
- "print(msg_template.format(number=3.1415926, precision=4))\n",
- "print(msg_template.format(number=3.1415926, precision=3))\n",
- "print(msg_template.format(number=3.1415926, precision=2))"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson4/sem4_312/task5_test_sys.py b/lessons/lesson4/sem4_312/task5_test_sys.py
deleted file mode 100644
index 92aa42d7..00000000
--- a/lessons/lesson4/sem4_312/task5_test_sys.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from testcases import testcases
-
-def parser(string, valid_pairs):
- corr_str = string.replace('>', '> ')
- corr_str = corr_str.replace('<', ' <')
- msv_str = corr_str.split()
- ans = []
- for i in range(len(msv_str)-2):
- if (msv_str[i], msv_str[i+2]) in valid_pairs:
- if msv_str[i+1] not in ans:
- ans.append(msv_str[i+1])
- return ans
-
-
-
-testcases_list = testcases["parser"]
-work_function = parser
-
-
-########################################################
-if __name__ == "__main__":
- success_count = 0
- cases_faild = []
-
- for case_num, case in enumerate(testcases_list):
- args = case["input"].copy()
- case_ans = case["output"]
-
- print(f"case {case_num+1}/{len(testcases_list)}")
- for i, arg in enumerate(args):
- print(f"arg_{i+1}: {arg}")
-
- output = work_function(*args)
- print(f"\texpected: {case_ans}")
- print(f"\tgot: {output}")
-
- if output == case_ans:
- print("OK".center(20,"="), end="\n\n")
- success_count += 1
- else:
- print("NOT".center(20,"~"), end="\n\n")
- cases_faild.append(case_num+1)
-
- print(f"tests passed: {success_count}/{len(testcases_list)}")
- print(f"cases faild: {cases_faild}")
diff --git a/lessons/lesson4/sem4_312/task6_test_sys.py b/lessons/lesson4/sem4_312/task6_test_sys.py
deleted file mode 100644
index 38694ead..00000000
--- a/lessons/lesson4/sem4_312/task6_test_sys.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from testcases import testcases
-
-def check_comand(user_comand: str, comands: list[str]) -> bool:
- cnt = 0
-
- #ошибка №1: недостаточный ввод
- for i in range(len(comands)):
- for j in range(len(comands[i])):
- check_str = ''
- for k in range(len(comands[i])):
- if k != j:
- check_str += comands[i][k]
- if user_comand == check_str:
- cnt += 1
- if cnt > 1:
- return False
-
- #ошибка №2: чрезмерный ввод
- for i in range(len(user_comand)):
- check_str = ''
- for j in range(len(user_comand)):
- if i != j:
- check_str += user_comand[j]
- if check_str in comands:
- cnt += 1
- if cnt > 1:
- return False
-
- #ошибка №3: замена символа
- for i in range(len(user_comand)):
- check_str_1 = ''
- for j in range(len(user_comand)): # смотрим, после удаления какого символа
- if j != i: # записиь пользователя будет недостаточной, как в ошибке №1
- check_str_1 += user_comand[j]
- for j in range(len(comands)):
- for k in range(len(comands[j])):
- check_str_2 = ''
- for s in range(len(comands[j])):
- if k != s:
- check_str_2 += comands[j][s]
- if check_str_1 == check_str_2:
- cnt += 1
- if cnt > 1:
- return False
- return cnt == 1
-
-
-
-testcases_list = testcases["check_comand"]
-work_function = check_comand
-
-
-########################################################
-if __name__ == "__main__":
- success_count = 0
- cases_faild = []
-
- for case_num, case in enumerate(testcases_list):
- args = case["input"].copy()
- case_ans = case["output"]
-
- print(f"case {case_num+1}/{len(testcases_list)}")
- for i, arg in enumerate(args):
- print(f"arg_{i+1}: {arg}")
-
- output = work_function(*args)
- print(f"\texpected: {case_ans}")
- print(f"\tgot: {output}")
-
- if output == case_ans:
- print("OK".center(20,"="), end="\n\n")
- success_count += 1
- else:
- print("NOT".center(20,"~"), end="\n\n")
- cases_faild.append(case_num+1)
-
- print(f"tests passed: {success_count}/{len(testcases_list)}")
- print(f"cases faild: {cases_faild}")
\ No newline at end of file
diff --git a/lessons/lesson4/sem4_312/tasks.ipynb b/lessons/lesson4/sem4_312/tasks.ipynb
deleted file mode 100644
index 44c5b476..00000000
--- a/lessons/lesson4/sem4_312/tasks.ipynb
+++ /dev/null
@@ -1,422 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Семинар: строки"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Разминка\n",
- "\n",
- "Реализовать две функции: первая удаляет все цифры в строке, вторая - все буквы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def delete_digits(string: str) -> str:\n",
- " # ваш код\n",
- " pass\n",
- "\n",
- "\n",
- "def delete_letters(string: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert delete_digits('123abnd4FDhj32') == 'abndFDhj'\n",
- "assert delete_letters('123abnd4FDhj32') == '123432'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 1: Наивный Шифр\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Мы решили зашифровать слово, состоящее из букв английского алфавита в нижнем регистре. Но поскольку наши познания в криптографии невелики для шифрования решено было использовать один из самых простых шифров: перестановка над английским алфавитом. Перестановка записывается следующим образом: заполняется список из 26 элементов; номер ячейки соответствует номеру буквы в полученной перестановке; содержимое ячейки - ASCII код буквы. Ваша задача - реализовать алгоритм, который будет шифровывать заданное слово по заданной перестановке.\n",
- "\n",
- "**Вход**:\n",
- "- word - строка, состоящая только из букв англиского алфавита в нижнем регистре; \n",
- "- permutation - список, состоящий из 26 элементов; содержимое списка - ASCII-коды букв английского алфавита в нижнем регистре;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- зашифрованное слово - строка;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "def encode(word: str, permutation: list[int]) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "permutation = [\n",
- " 122, 98, 99, 100, 101, 102, 103, 104, 105,\n",
- " 106, 107, 108, 109, 110, 111, 112, 113, 114,\n",
- " 115, 116, 117, 118, 119, 120, 121, 97\n",
- "]\n",
- "\n",
- "word = 'aboba'\n",
- "\n",
- "assert 'zbobz' == encode(word, permutation)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Объяснение:** в перестановке мы переставили буквы 'a' и 'z' местами."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 2: Переставь слова\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Дана последовательность символов: строка, состоящая из заглавных и строчных букв английского алфавита, знаков препинания и пробелов. Причем, строка составлена не самым грамотным пользователем, а потому количество пробелов между непробельными символами может быть произвольным. Также неисключено наличие пробелов в начале и в конце строки. Необходимо найти самое длинное слово в строке, самое короткое слово в строке и поменять их местами. В качестве ответа вернуть строку с переставленными словами. Пробелы между словами, а также в начале и конце строки необходимо оставить нетронутыми.\n",
- "\n",
- "Если слов одинковой длины несколько, необходимо взять первое встретевшееся.\n",
- "\n",
- "*Примечание*: словом считается любая последовательность символов, не содержащая пробелов.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из букв английского алфавита, знаков препинания и пробелов; \n",
- "\n",
- "**Выход:**\n",
- "\n",
- "- строка, в которой самое длинное слово и самое короткое слово переставлены; \n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "def swap_words(sentence: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert ' dd b c a ' == swap_words(' a b c dd ')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 3: Правильная скобочная последовательность\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Правильная скобочная последовательность определяется следующим образом:\n",
- "\n",
- "- пустая строка - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, взятая в круглые скобки - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, к которой приписана слева или справа правильная скобочная последовательность — правильная скобочная последовательность;\n",
- "\n",
- "Итак, на вход подается строка, состоящая только из символов \"(\", \")\". Ваша задача - определить является ли поданная строка правильной скобочной последовательностью или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- sequence - строка, состоящая из круглых скобок;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение, True, если строка - правильная скобочная последовательность, False - иначе;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "def is_correct_bracket_seq(sequence: str) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert is_correct_bracket_seq('')\n",
- "assert is_correct_bracket_seq('()()')\n",
- "assert not is_correct_bracket_seq(')(')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 4: Странный калькулятор\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Назовем странным калькулятором калькулятор, который работает следующим образом:\n",
- "\n",
- "- на вход калькулятору подается строка, в которой описано некоторое алгебраическое выражение; \n",
- "- операндами этого выражения являются исключительно целые числа, записанные в следующей форме: каждая цифра числа записана английским словом, обозначающим эту цифру; сами цифры, составляющие запись числа, записаны подряд. Например число десять будет иметь запись `onezero`; \n",
- "- в состав строки входят только операторы сложения и умножения; \n",
- "- операнды и операторы разделены пробелами; \n",
- "- калькулятор вычисляет описанное таким образом выражение и вызвращает результат - целое число; \n",
- "\n",
- "Ваша задача реализовать такой калькулятор.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из слов 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', символов '+' и '-' и пробелов; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- целое число - результат выполнения выражения;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def calculate(expression: str) -> int:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert calculate('-one + two + threefive - onezero') == 26"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 5: Парсер\n",
- "\n",
- "Необходимо реализовать следующий алгоритм парсинга документов:\n",
- "\n",
- "- на вход алгоритму подается строка, которую необходимо распарсить, и пары специальных символов, которые поддерживаются парсером; например: \\, \\; \n",
- "- строка состоит из специальных символов и строк, окруженных специальными символами; \n",
- "- валидными считаются слова, которые заключены в правильную пару служебных символов, и служебные символы этой пары входят в список символов, поддерживаемых парсером; например, слово, заключенное в служебные символы: \\word\\, будет валидным, если \\ и \\ поддерживаются парсером - а слово, заключенное в служебные символы: \\word\\ - не будет валидным ни в каком случае; \n",
- "- парсер выделяет валидные слова, и сохраняет уникальные валидные слова в список; \n",
- "\n",
- "Ваша задача - реализовать описанный алгоритм парсинга.\n",
- "\n",
- "**Вход:** \n",
- "\n",
- "- строка, состоящая из специальных символов следующего формата: \\<[ / ]english_letter>, - и из букв английского алфавита; \n",
- "- список пар(кортежей) - валидных служебных конструкций; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- список уникальных валидных слов; \n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def parse(\n",
- " string: str, valid_pairs: list[tuple[str, str]]\n",
- ") -> list[str]:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "string = (\n",
- " \"
float
frozenset\"\n",
- " \"
list
list\"\n",
- ")\n",
- "valid_pairs = [(\"\", \"\"), (\"\", \"\"), (\"\", \"\")]\n",
- "\n",
- "assert parse(string, valid_pairs) == [\"frozenset\", \"list\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 6: Умная консоль\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Вы работаете с консолью, которая имеет некий набор команд. Консоль достаточно умная и умеет исправлять ошибки пользователя, если ошибка незначительная. Незначительными считаются ошибки одного из трёх типов:\n",
- "\n",
- "- ввод одного лишнего символа; \n",
- "- пропуск одного символа; \n",
- "- один неправильно введенный символ; \n",
- "\n",
- "Притом описанные ошибки не должны вызывать неоднозначность. Так, например комманда \"gt\" с набором команд [cd, ls, git] будет восстановлена до команды git, но та же команда с набором команд [cd, ls, git, get] может соответствовать как команде \"get\", так и команде \"git\" и восстановлена не будет.\n",
- "\n",
- "Если команда написана с незначительной ошибкой, то консоль её автоматически исправляет и выполняет. Реализуйте алгоритм check_comand, который проверяет, выполнит ли консоль с заданным набором команд команду пользователя или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- user_comand - строка, команда, введенная пользователем; \n",
- "- comands - список строк, команды, поддерживаемые консолью; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение: True, если команда будет выполнена, False - иначе;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def check_comand(user_comand: str, comands: list[str]) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert check_comand('gt', ['cd', 'ls', 'git']) \n",
- "assert not check_comand('gt', ['cd', 'ls', 'git', 'get'])"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson4/sem4_312/testcases.py b/lessons/lesson4/sem4_312/testcases.py
deleted file mode 100644
index 0e5ee6f5..00000000
--- a/lessons/lesson4/sem4_312/testcases.py
+++ /dev/null
@@ -1,186 +0,0 @@
-testcases = {
- "parser": [
- {
- "input": [
- "
float
frozenset
list
list",
- [("", ""), ("", ""), ("", "")]
- ],
- "output": ["frozenset", "list"]
- },
- {
- "input": [
- "<\\a>float<\\b>double<\\c>complex",
- [("", ""), ("", ""), ("", "")]
- ],
- "output": []
- },
- {
- "input": [
- "floatdoublecomplex",
- [("", ""), ("", ""), ("", "")]
- ],
- "output": ["complex"]
- },
- {
- "input": [
- "floatdoublecomplex",
- [("", ""), ("", ""), ("", "")]
- ],
- "output": []
- },
- {
- "input": [
- "
this_is_float",
- [("
", "")]
- ],
- "output": ["this_is_float"]
- },
- {
- "input": [
- "
this_is_float
",
- [("", "")]
- ],
- "output": []
- },
- {
- "input": [
- "thisisgoodexample",
- [("", ""), ("", ""), ("", ""), ("", "")]
- ],
- "output": ["this", "is", "good", "example"]
- },
- {
- "input": [
- "thisisgoodexample",
- [("", ""), ("", ""), ("", ""), ("", "")]
- ],
- "output": ["is", "good"]
- },
- {
- "input": [
- "complexint",
- [("", ""), ("", "")]
- ],
- "output": ["int"]
- },
- {
- "input": [
- "complexint",
- [("", ""), ("", "")]
- ],
- "output": []
- }
- ],
-
- "check_comand": [
- # блок укороченных команд
- {
- "input": [
- "gt", ["cd", "ls", "git"]
- ],
- "output": True
- },
- {
- "input": [
- "gt", ["cd", "ls", "git", "get"]
- ],
- "output": False
- },
- {
- "input": [
- "c", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "d", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "it", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "gi", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
-
- # блок удлиненных команд
- {
- "input": [
- "getting", ["cd", "ls", "git", "get"]
- ],
- "output": False
- },
- {
- "input": [
- "wget", ["cd", "ls", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "geto", ["cd", "ls", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "geet", ["cd", "ls", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "geet", ["cd", "ls", "git"]
- ],
- "output": False
- },
-
- # блок ошибок в одной букве
- {
- "input": [
- "get", ["cd", "ls", "git"]
- ],
- "output": True
- },
- {
- "input": [
- "get", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
- {
- "input": [
- "gid", ["cd", "ls", "git", "get", "gil"]
- ],
- "output": False
- },
- {
- "input": [
- "gid", ["cd", "ls", "git", "get"]
- ],
- "output": True
- },
-
- # блок обычных команд
- {
- "input": [
- "wget", ["cd", "ls", "get", "wget"]
- ],
- "output": True
- },
- {
- "input": [
- "rm", ["cd", "ls", "git", "wget"]
- ],
- "output": False
- }
- ]
-}
diff --git a/lessons/lesson4/sem4_313/tasks.ipynb b/lessons/lesson4/sem4_313/tasks.ipynb
deleted file mode 100644
index 44c5b476..00000000
--- a/lessons/lesson4/sem4_313/tasks.ipynb
+++ /dev/null
@@ -1,422 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Семинар: строки"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Разминка\n",
- "\n",
- "Реализовать две функции: первая удаляет все цифры в строке, вторая - все буквы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def delete_digits(string: str) -> str:\n",
- " # ваш код\n",
- " pass\n",
- "\n",
- "\n",
- "def delete_letters(string: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert delete_digits('123abnd4FDhj32') == 'abndFDhj'\n",
- "assert delete_letters('123abnd4FDhj32') == '123432'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 1: Наивный Шифр\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Мы решили зашифровать слово, состоящее из букв английского алфавита в нижнем регистре. Но поскольку наши познания в криптографии невелики для шифрования решено было использовать один из самых простых шифров: перестановка над английским алфавитом. Перестановка записывается следующим образом: заполняется список из 26 элементов; номер ячейки соответствует номеру буквы в полученной перестановке; содержимое ячейки - ASCII код буквы. Ваша задача - реализовать алгоритм, который будет шифровывать заданное слово по заданной перестановке.\n",
- "\n",
- "**Вход**:\n",
- "- word - строка, состоящая только из букв англиского алфавита в нижнем регистре; \n",
- "- permutation - список, состоящий из 26 элементов; содержимое списка - ASCII-коды букв английского алфавита в нижнем регистре;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- зашифрованное слово - строка;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "def encode(word: str, permutation: list[int]) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "permutation = [\n",
- " 122, 98, 99, 100, 101, 102, 103, 104, 105,\n",
- " 106, 107, 108, 109, 110, 111, 112, 113, 114,\n",
- " 115, 116, 117, 118, 119, 120, 121, 97\n",
- "]\n",
- "\n",
- "word = 'aboba'\n",
- "\n",
- "assert 'zbobz' == encode(word, permutation)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Объяснение:** в перестановке мы переставили буквы 'a' и 'z' местами."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 2: Переставь слова\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Дана последовательность символов: строка, состоящая из заглавных и строчных букв английского алфавита, знаков препинания и пробелов. Причем, строка составлена не самым грамотным пользователем, а потому количество пробелов между непробельными символами может быть произвольным. Также неисключено наличие пробелов в начале и в конце строки. Необходимо найти самое длинное слово в строке, самое короткое слово в строке и поменять их местами. В качестве ответа вернуть строку с переставленными словами. Пробелы между словами, а также в начале и конце строки необходимо оставить нетронутыми.\n",
- "\n",
- "Если слов одинковой длины несколько, необходимо взять первое встретевшееся.\n",
- "\n",
- "*Примечание*: словом считается любая последовательность символов, не содержащая пробелов.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из букв английского алфавита, знаков препинания и пробелов; \n",
- "\n",
- "**Выход:**\n",
- "\n",
- "- строка, в которой самое длинное слово и самое короткое слово переставлены; \n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "def swap_words(sentence: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert ' dd b c a ' == swap_words(' a b c dd ')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 3: Правильная скобочная последовательность\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Правильная скобочная последовательность определяется следующим образом:\n",
- "\n",
- "- пустая строка - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, взятая в круглые скобки - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, к которой приписана слева или справа правильная скобочная последовательность — правильная скобочная последовательность;\n",
- "\n",
- "Итак, на вход подается строка, состоящая только из символов \"(\", \")\". Ваша задача - определить является ли поданная строка правильной скобочной последовательностью или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- sequence - строка, состоящая из круглых скобок;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение, True, если строка - правильная скобочная последовательность, False - иначе;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "def is_correct_bracket_seq(sequence: str) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert is_correct_bracket_seq('')\n",
- "assert is_correct_bracket_seq('()()')\n",
- "assert not is_correct_bracket_seq(')(')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 4: Странный калькулятор\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Назовем странным калькулятором калькулятор, который работает следующим образом:\n",
- "\n",
- "- на вход калькулятору подается строка, в которой описано некоторое алгебраическое выражение; \n",
- "- операндами этого выражения являются исключительно целые числа, записанные в следующей форме: каждая цифра числа записана английским словом, обозначающим эту цифру; сами цифры, составляющие запись числа, записаны подряд. Например число десять будет иметь запись `onezero`; \n",
- "- в состав строки входят только операторы сложения и умножения; \n",
- "- операнды и операторы разделены пробелами; \n",
- "- калькулятор вычисляет описанное таким образом выражение и вызвращает результат - целое число; \n",
- "\n",
- "Ваша задача реализовать такой калькулятор.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из слов 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', символов '+' и '-' и пробелов; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- целое число - результат выполнения выражения;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def calculate(expression: str) -> int:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert calculate('-one + two + threefive - onezero') == 26"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 5: Парсер\n",
- "\n",
- "Необходимо реализовать следующий алгоритм парсинга документов:\n",
- "\n",
- "- на вход алгоритму подается строка, которую необходимо распарсить, и пары специальных символов, которые поддерживаются парсером; например: \\, \\; \n",
- "- строка состоит из специальных символов и строк, окруженных специальными символами; \n",
- "- валидными считаются слова, которые заключены в правильную пару служебных символов, и служебные символы этой пары входят в список символов, поддерживаемых парсером; например, слово, заключенное в служебные символы: \\word\\, будет валидным, если \\ и \\ поддерживаются парсером - а слово, заключенное в служебные символы: \\word\\ - не будет валидным ни в каком случае; \n",
- "- парсер выделяет валидные слова, и сохраняет уникальные валидные слова в список; \n",
- "\n",
- "Ваша задача - реализовать описанный алгоритм парсинга.\n",
- "\n",
- "**Вход:** \n",
- "\n",
- "- строка, состоящая из специальных символов следующего формата: \\<[ / ]english_letter>, - и из букв английского алфавита; \n",
- "- список пар(кортежей) - валидных служебных конструкций; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- список уникальных валидных слов; \n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def parse(\n",
- " string: str, valid_pairs: list[tuple[str, str]]\n",
- ") -> list[str]:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "string = (\n",
- " \"
float
frozenset\"\n",
- " \"
list
list\"\n",
- ")\n",
- "valid_pairs = [(\"\", \"\"), (\"\", \"\"), (\"\", \"\")]\n",
- "\n",
- "assert parse(string, valid_pairs) == [\"frozenset\", \"list\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 6: Умная консоль\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Вы работаете с консолью, которая имеет некий набор команд. Консоль достаточно умная и умеет исправлять ошибки пользователя, если ошибка незначительная. Незначительными считаются ошибки одного из трёх типов:\n",
- "\n",
- "- ввод одного лишнего символа; \n",
- "- пропуск одного символа; \n",
- "- один неправильно введенный символ; \n",
- "\n",
- "Притом описанные ошибки не должны вызывать неоднозначность. Так, например комманда \"gt\" с набором команд [cd, ls, git] будет восстановлена до команды git, но та же команда с набором команд [cd, ls, git, get] может соответствовать как команде \"get\", так и команде \"git\" и восстановлена не будет.\n",
- "\n",
- "Если команда написана с незначительной ошибкой, то консоль её автоматически исправляет и выполняет. Реализуйте алгоритм check_comand, который проверяет, выполнит ли консоль с заданным набором команд команду пользователя или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- user_comand - строка, команда, введенная пользователем; \n",
- "- comands - список строк, команды, поддерживаемые консолью; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение: True, если команда будет выполнена, False - иначе;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def check_comand(user_comand: str, comands: list[str]) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert check_comand('gt', ['cd', 'ls', 'git']) \n",
- "assert not check_comand('gt', ['cd', 'ls', 'git', 'get'])"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson4/sem4_314/tasks.ipynb b/lessons/lesson4/sem4_314/tasks.ipynb
deleted file mode 100644
index 44c5b476..00000000
--- a/lessons/lesson4/sem4_314/tasks.ipynb
+++ /dev/null
@@ -1,422 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Семинар: строки"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Разминка\n",
- "\n",
- "Реализовать две функции: первая удаляет все цифры в строке, вторая - все буквы."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def delete_digits(string: str) -> str:\n",
- " # ваш код\n",
- " pass\n",
- "\n",
- "\n",
- "def delete_letters(string: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert delete_digits('123abnd4FDhj32') == 'abndFDhj'\n",
- "assert delete_letters('123abnd4FDhj32') == '123432'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 1: Наивный Шифр\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Мы решили зашифровать слово, состоящее из букв английского алфавита в нижнем регистре. Но поскольку наши познания в криптографии невелики для шифрования решено было использовать один из самых простых шифров: перестановка над английским алфавитом. Перестановка записывается следующим образом: заполняется список из 26 элементов; номер ячейки соответствует номеру буквы в полученной перестановке; содержимое ячейки - ASCII код буквы. Ваша задача - реализовать алгоритм, который будет шифровывать заданное слово по заданной перестановке.\n",
- "\n",
- "**Вход**:\n",
- "- word - строка, состоящая только из букв англиского алфавита в нижнем регистре; \n",
- "- permutation - список, состоящий из 26 элементов; содержимое списка - ASCII-коды букв английского алфавита в нижнем регистре;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- зашифрованное слово - строка;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "def encode(word: str, permutation: list[int]) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "permutation = [\n",
- " 122, 98, 99, 100, 101, 102, 103, 104, 105,\n",
- " 106, 107, 108, 109, 110, 111, 112, 113, 114,\n",
- " 115, 116, 117, 118, 119, 120, 121, 97\n",
- "]\n",
- "\n",
- "word = 'aboba'\n",
- "\n",
- "assert 'zbobz' == encode(word, permutation)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Объяснение:** в перестановке мы переставили буквы 'a' и 'z' местами."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 2: Переставь слова\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Дана последовательность символов: строка, состоящая из заглавных и строчных букв английского алфавита, знаков препинания и пробелов. Причем, строка составлена не самым грамотным пользователем, а потому количество пробелов между непробельными символами может быть произвольным. Также неисключено наличие пробелов в начале и в конце строки. Необходимо найти самое длинное слово в строке, самое короткое слово в строке и поменять их местами. В качестве ответа вернуть строку с переставленными словами. Пробелы между словами, а также в начале и конце строки необходимо оставить нетронутыми.\n",
- "\n",
- "Если слов одинковой длины несколько, необходимо взять первое встретевшееся.\n",
- "\n",
- "*Примечание*: словом считается любая последовательность символов, не содержащая пробелов.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из букв английского алфавита, знаков препинания и пробелов; \n",
- "\n",
- "**Выход:**\n",
- "\n",
- "- строка, в которой самое длинное слово и самое короткое слово переставлены; \n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "def swap_words(sentence: str) -> str:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert ' dd b c a ' == swap_words(' a b c dd ')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 3: Правильная скобочная последовательность\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Правильная скобочная последовательность определяется следующим образом:\n",
- "\n",
- "- пустая строка - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, взятая в круглые скобки - правильная скобочная последовательность; \n",
- "- правильная скобочная последовательность, к которой приписана слева или справа правильная скобочная последовательность — правильная скобочная последовательность;\n",
- "\n",
- "Итак, на вход подается строка, состоящая только из символов \"(\", \")\". Ваша задача - определить является ли поданная строка правильной скобочной последовательностью или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- sequence - строка, состоящая из круглых скобок;\n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение, True, если строка - правильная скобочная последовательность, False - иначе;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "def is_correct_bracket_seq(sequence: str) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert is_correct_bracket_seq('')\n",
- "assert is_correct_bracket_seq('()()')\n",
- "assert not is_correct_bracket_seq(')(')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 4: Странный калькулятор\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Назовем странным калькулятором калькулятор, который работает следующим образом:\n",
- "\n",
- "- на вход калькулятору подается строка, в которой описано некоторое алгебраическое выражение; \n",
- "- операндами этого выражения являются исключительно целые числа, записанные в следующей форме: каждая цифра числа записана английским словом, обозначающим эту цифру; сами цифры, составляющие запись числа, записаны подряд. Например число десять будет иметь запись `onezero`; \n",
- "- в состав строки входят только операторы сложения и умножения; \n",
- "- операнды и операторы разделены пробелами; \n",
- "- калькулятор вычисляет описанное таким образом выражение и вызвращает результат - целое число; \n",
- "\n",
- "Ваша задача реализовать такой калькулятор.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- строка, состоящая из слов 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', символов '+' и '-' и пробелов; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- целое число - результат выполнения выражения;\n",
- "\n",
- "**Решение**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def calculate(expression: str) -> int:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert calculate('-one + two + threefive - onezero') == 26"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 5: Парсер\n",
- "\n",
- "Необходимо реализовать следующий алгоритм парсинга документов:\n",
- "\n",
- "- на вход алгоритму подается строка, которую необходимо распарсить, и пары специальных символов, которые поддерживаются парсером; например: \\, \\; \n",
- "- строка состоит из специальных символов и строк, окруженных специальными символами; \n",
- "- валидными считаются слова, которые заключены в правильную пару служебных символов, и служебные символы этой пары входят в список символов, поддерживаемых парсером; например, слово, заключенное в служебные символы: \\word\\, будет валидным, если \\ и \\ поддерживаются парсером - а слово, заключенное в служебные символы: \\word\\ - не будет валидным ни в каком случае; \n",
- "- парсер выделяет валидные слова, и сохраняет уникальные валидные слова в список; \n",
- "\n",
- "Ваша задача - реализовать описанный алгоритм парсинга.\n",
- "\n",
- "**Вход:** \n",
- "\n",
- "- строка, состоящая из специальных символов следующего формата: \\<[ / ]english_letter>, - и из букв английского алфавита; \n",
- "- список пар(кортежей) - валидных служебных конструкций; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- список уникальных валидных слов; \n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def parse(\n",
- " string: str, valid_pairs: list[tuple[str, str]]\n",
- ") -> list[str]:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "string = (\n",
- " \"
float
frozenset\"\n",
- " \"
list
list\"\n",
- ")\n",
- "valid_pairs = [(\"\", \"\"), (\"\", \"\"), (\"\", \"\")]\n",
- "\n",
- "assert parse(string, valid_pairs) == [\"frozenset\", \"list\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача 6: Умная консоль\n",
- "\n",
- "**Условие**:\n",
- "\n",
- "Вы работаете с консолью, которая имеет некий набор команд. Консоль достаточно умная и умеет исправлять ошибки пользователя, если ошибка незначительная. Незначительными считаются ошибки одного из трёх типов:\n",
- "\n",
- "- ввод одного лишнего символа; \n",
- "- пропуск одного символа; \n",
- "- один неправильно введенный символ; \n",
- "\n",
- "Притом описанные ошибки не должны вызывать неоднозначность. Так, например комманда \"gt\" с набором команд [cd, ls, git] будет восстановлена до команды git, но та же команда с набором команд [cd, ls, git, get] может соответствовать как команде \"get\", так и команде \"git\" и восстановлена не будет.\n",
- "\n",
- "Если команда написана с незначительной ошибкой, то консоль её автоматически исправляет и выполняет. Реализуйте алгоритм check_comand, который проверяет, выполнит ли консоль с заданным набором команд команду пользователя или нет.\n",
- "\n",
- "**Вход**:\n",
- "\n",
- "- user_comand - строка, команда, введенная пользователем; \n",
- "- comands - список строк, команды, поддерживаемые консолью; \n",
- "\n",
- "**Выход**:\n",
- "\n",
- "- булево значение: True, если команда будет выполнена, False - иначе;\n",
- "\n",
- "**Решение:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def check_comand(user_comand: str, comands: list[str]) -> bool:\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Тесты:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert check_comand('gt', ['cd', 'ls', 'git']) \n",
- "assert not check_comand('gt', ['cd', 'ls', 'git', 'get'])"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson5/interactive_conspect.ipynb b/lessons/lesson5/interactive_conspect.ipynb
deleted file mode 100644
index d4569a52..00000000
--- a/lessons/lesson5/interactive_conspect.ipynb
+++ /dev/null
@@ -1,1853 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Типы данных: Словари и Множества\n",
- "\n",
- "На этой лекции мы закончим разбирать встроенные типы данных в Python и поговорим о словарях (dict) и множествах (set, frozenset).\n",
- "\n",
- "## Словари\n",
- "\n",
- "### Знакомство со словарями\n",
- "\n",
- "Словари являются неупорядоченными изменяемыми коллекциями пар элементов вида ключ-значение. О словарях можно думать, как об ассоциативных массивах, т.е. о списках, чьи индексы - ключи, на первый взгляд, являются произвольными объектами. Так ключом словаря может быть объект типа int, float, str, frozenset, функция или даже объект пользовательского типа данных. Однако не все так просто, ибо на объекты, которым разрешено выступать в роли ключей, накладывается одно важное ограничение - объекты должны быть хешируемыми. Что это означает? А означает это следующее:\n",
- "\n",
- "- Значение [хэш-функции](https://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F) объекта должно быть определено, и не изменяться на протяжении всего времени выполнения программы. Подробнее о хэш функциях вам расскажут в курсе Алгоритмы и Структуры Данных, сейчас хэш функцию можно воспринимать, как некоторый черный ящик, которыей принимает на вход некоторый объект и ставит в соответствие ему последовательность бит фиксированной длины - т.е. число из фиксированного диапазона. Для того, чтобы объект Python был хэшируемым необходимо, чтобы в нем была реализована специальная функция `__hash__()`, т.е. чтобы интерпретатор мог выполнять вызов встроенной функции `hash()` с вашим объектом; \n",
- "- Объект должен поддерживать использование оператора ==;\n",
- "- Если два объекта равны: obj1 == obj2, то должно выполняться равенство: hash(obj1) == hash(obj2); \n",
- "\n",
- "Если все три условия выполнены, то объект считается хэшируемым и может быть использован в качестве ключа. Числовые типы данных, строки, замороженные множества всегда хэшируемы. Если все элементы кортежа хэшируемы, то и кортеж является хэшируемым:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "object value: 1; object type: int; hash function value: 1;\n",
- "object value: 3.14; object type: float; hash function value: 322818021289917443;\n",
- "object value: string; object type: str; hash function value: -7057234837228677891;\n",
- "object value: frozenset({1, 2, 3}); object type: frozenset; hash function value: -272375401224217160;\n",
- "hashable tuple hash: -6984807964728689118\n"
- ]
- },
- {
- "ename": "TypeError",
- "evalue": "unhashable type: 'list'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 2\u001b[0m line \u001b[0;36m1\n\u001b[0;32m 12\u001b[0m \u001b[39m# TypeError\u001b[39;00m\n\u001b[0;32m 13\u001b[0m tuple_unhashable \u001b[39m=\u001b[39m (\u001b[39m0\u001b[39m, [\u001b[39m1\u001b[39m])\n\u001b[1;32m---> 14\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mhash\u001b[39;49m(tuple_unhashable))\n",
- "\u001b[1;31mTypeError\u001b[0m: unhashable type: 'list'"
- ]
- }
- ],
- "source": [
- "objects = [1, 3.14, 'string', frozenset((1, 2, 3))]\n",
- "\n",
- "for obj in objects:\n",
- " print(\n",
- " f'object value: {obj}; object type: {type(obj).__name__}; '\n",
- " f'hash function value: {hash(obj)};'\n",
- " )\n",
- "\n",
- "tuple_hashable = tuple(objects)\n",
- "print(f'hashable tuple hash: {hash(tuple_hashable)}')\n",
- "\n",
- "# TypeError\n",
- "tuple_unhashable = (0, [1])\n",
- "print(hash(tuple_unhashable))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Ключи словря являются уникальными. Попытка добавления пары с существующим ключом приведет к изменению значения по этому ключу. На объекты, которые хранятся в словаре - значения, - никаких ограничений не накладывается.\n",
- "\n",
- "Давайте рассмотрим создание словарей в коде:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "dicts = [\n",
- " {'x':42, 'y':3.14, 'z':7},\n",
- " {1:2, 3:4},\n",
- " {1:'za', 'br':23},\n",
- " {},\n",
- " dict(x=42, y=3.14, z=7),\n",
- " dict([(1, 2), (3, 4)]),\n",
- " dict([(1,'za'), ('br',23)]),\n",
- " dict(),\n",
- " dict([(letter, ord(letter)) for letter in 'abcde']),\n",
- " {letter: ord(letter) for letter in 'abcde'},\n",
- " {\n",
- " letter: ord(letter) for letter in 'abcde'\n",
- " if ord(letter) % 2\n",
- " },\n",
- " dict([('x', 1), ('y', 2), ('x', 126), ('y', 365)]),\n",
- " dict.fromkeys('abc', 2),\n",
- " dict.fromkeys('abc')\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "=================================CREATED DICTS==================================\n",
- "dict: {'x': 42, 'y': 3.14, 'z': 7};\n",
- "dict: {1: 2, 3: 4};\n",
- "dict: {1: 'za', 'br': 23};\n",
- "dict: {};\n",
- "dict: {'x': 42, 'y': 3.14, 'z': 7};\n",
- "dict: {1: 2, 3: 4};\n",
- "dict: {1: 'za', 'br': 23};\n",
- "dict: {};\n",
- "dict: {'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101};\n",
- "dict: {'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101};\n",
- "dict: {'a': 97, 'c': 99, 'e': 101};\n",
- "dict: {'x': 126, 'y': 365};\n",
- "dict: {'a': 2, 'b': 2, 'c': 2};\n",
- "dict: {'a': None, 'b': None, 'c': None};\n",
- "================================================================================\n"
- ]
- }
- ],
- "source": [
- "print('CREATED DICTS'.center(80, '='))\n",
- "for dict_example in dicts:\n",
- " print(f'{type(dict_example).__name__}: {dict_example};')\n",
- "\n",
- "print(''.center(80, '='))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Словари и методы последовательностей или \"Почему словари не последовательности\"\n",
- "\n",
- "Почти все операции над последовательностями, кроме конкатенации и повторения (подумайте, почему), применима и к словарям. Но тут есть свои нюансы.\n",
- "\n",
- "**len()**:\n",
- "\n",
- "Функция `len()`, работает вполне понятным образом - определяет количество пар ключ-значение, которое хранится в словаре:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "dict len: 2\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "\n",
- "print(f'dict len: {len(my_dict)}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Оператор in**:\n",
- "\n",
- "Словари поддерживают оператор `in`, однако в отличие от последовательностей, метод in проверяет вхождение объекта в словарь не в качестве элемента, а в качестве ключа. Т.е. in будет возвращать True только в том случае, если в словаре сожержиться ключ, с тем же значением, что и аргумент оператора:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "\n",
- "print('a' in my_dict)\n",
- "print('c' in my_dict)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**__getitem__()**\n",
- "\n",
- "Также словари поддерживают доступ к элементам через квадратные скобки. Аргументом здесь является объект-ключ. Если словарь содержит элемент с данным ключом, то результатом выполнения данного обращения будет являться этот хранящийся ссылка на хранящийся объект, иначе - исключение. Поскольку словари - изменяемый тип данных, используя эту нотацию, вы можете перезаписывать элементы словаря, добавлять новые, или удалять существующие с помощью встроенного оператора `del`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1\n",
- "{'a': [1, 2, 3], 'b': 2}\n",
- "{'a': [1, 2, 3], 'b': 2, 'c': 1}\n",
- "{'a': [1, 2, 3], 'c': 1}\n"
- ]
- },
- {
- "ename": "KeyError",
- "evalue": "'b'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 11\u001b[0m line \u001b[0;36m1\n\u001b[0;32m 10\u001b[0m \u001b[39mdel\u001b[39;00m my_dict[\u001b[39m'\u001b[39m\u001b[39mb\u001b[39m\u001b[39m'\u001b[39m]\n\u001b[0;32m 11\u001b[0m \u001b[39mprint\u001b[39m(my_dict)\n\u001b[1;32m---> 14\u001b[0m \u001b[39mprint\u001b[39m(my_dict[\u001b[39m'\u001b[39;49m\u001b[39mb\u001b[39;49m\u001b[39m'\u001b[39;49m])\n",
- "\u001b[1;31mKeyError\u001b[0m: 'b'"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "print(my_dict['a'])\n",
- "\n",
- "my_dict['a'] = [1, 2, 3]\n",
- "print(my_dict)\n",
- "\n",
- "my_dict['c'] = 1\n",
- "print(my_dict)\n",
- "\n",
- "del my_dict['b']\n",
- "print(my_dict)\n",
- "\n",
- "\n",
- "print(my_dict['b'])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Итерируемость**:\n",
- "\n",
- "Словари являются итерируемыми объектами. Итератор, созданный из словаря, на каждой итерации будет возвращать хранящийся в нем ключ:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "a\n",
- "b\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "\n",
- "for key in my_dict:\n",
- " print(key)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Почему словари не последовательности**\n",
- "\n",
- "Итак, почему же словари - не последовательности, ведь формально, словари реализуют интерфейс последовательностей: они итерируемы, они поддерживают вызов функции len(), а также доступ к элементами через квадратные скобочки? Словари не последовательности, поскольку словари - неупорядоченные колекции, что позволяет индексировать их элементы произвольными хэшируемыми объектами, в то время как последовательности являются упорядоченными коллекциями и индексируются исключительно целыми числами. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Методы словарей\n",
- "\n",
- "Обсудим методы словарей и ситуации в которых они могут быть полезны.\n",
- "\n",
- "**copy()**\n",
- "\n",
- "Немодифицирующий метод словаря. Создает поверхностную копию словаря. Понятие поверхностной копии означает, что формально создается новый объект словаря, но при этом его содержимое - указатели на те же самые объекты, что и указатели в исходном словаре."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "copy(...)\n",
- " D.copy() -> a shallow copy of D\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.copy)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "original: {'num': 1, 'list': [1, 2, 3]};\n",
- "copy: {'num': 1, 'list': [1, 2, 3]};\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'num': 1, 'list': [1, 2, 3]}\n",
- "my_dict_copy = my_dict.copy()\n",
- "\n",
- "print(\n",
- " f'original: {my_dict};',\n",
- " f'copy: {my_dict_copy};',\n",
- " id(my_dict) == id(my_dict_copy),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "original: {'num': 1, 'list': [1, 2, 3, 123]};\n",
- "copy: {'num': 1, 'list': [1, 2, 3, 123]};\n"
- ]
- }
- ],
- "source": [
- "my_dict_copy['list'].append(123)\n",
- "\n",
- "print(\n",
- " f'original: {my_dict};',\n",
- " f'copy: {my_dict_copy};',\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**get()**\n",
- "\n",
- "Метод get() является немодифицирующим методом."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "get(self, key, default=None, /)\n",
- " Return the value for key if key is in the dictionary, else default.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.get)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Метод get() может оказаться очень полезным для упрощения кода и избавления от ненужных ветвлений. Например, представим, что у нас есть некоторая функция, которая выполняет полезную работу, например итерационно оптимизирует некоторую функцию, т.е. старается за определенное число итераций найти минимум некоторой функции. Если нашей функции удается найти минимум за отведенное число итераций она возвращает нам статус и результат в виде словаря:\n",
- "\n",
- "```Python\n",
- "{\n",
- " \"status\": \"success\",\n",
- " \"result\": 42\n",
- "}\n",
- "```\n",
- "\n",
- "Если же функции не удается найти минимум за отведенное число итераций, или в процессе выполнения происходит какая-либо ошибка, мы получаем ответ в формате:\n",
- "\n",
- "```Python\n",
- "{\n",
- " \"status\": \"fail\"\n",
- "}\n",
- "```\n",
- "\n",
- "Так бы мог выглядит типичный код обработки ответа от нашей функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "import random\n",
- "\n",
- "from typing import Any\n",
- "\n",
- "\n",
- "def simulate_optimization() -> dict[str, Any]:\n",
- " if random.randint(0, 1):\n",
- " return {\n",
- " \"status\": \"success\",\n",
- " \"result\": random.randint(-100, 100)\n",
- " }\n",
- " \n",
- " return {\n",
- " \"status\": \"fail\"\n",
- " }"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "optimization status: fail\n",
- "optimization result: number of iterations exceeded\n"
- ]
- }
- ],
- "source": [
- "result = simulate_optimization()\n",
- "\n",
- "print(f'optimization status: {result[\"status\"]}')\n",
- "\n",
- "if \"result\" in result:\n",
- " print(f'optimization result: {result[\"result\"]}')\n",
- "\n",
- "else:\n",
- " print('optimization result: number of iterations exceeded')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Но с посощью метода get() можно избавиться от ненужного ветвления:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "optimization status: success\n",
- "optimization result: -71\n"
- ]
- }
- ],
- "source": [
- "result = simulate_optimization()\n",
- "\n",
- "print(\n",
- " f'optimization status: {result[\"status\"]}',\n",
- " f'optimization result: {result.get(\"result\", \"number of iterations exceeded\")}',\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**items() / keys() / values()**\n",
- "\n",
- "Метод items() является немодифицирующим методом."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "items(...)\n",
- " D.items() -> a set-like object providing a view on D's items\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.items)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "keys(...)\n",
- " D.keys() -> a set-like object providing a view on D's keys\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.keys)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "values(...)\n",
- " D.values() -> an object providing a view on D's values\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.values)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [],
- "source": [
- "my_dict = {'a': 'A', 'b': [1]}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "('a', 'A')\n",
- "('b', [1])\n",
- "\n",
- "key = 'a': value = 'A';\n",
- "key = 'b': value = [1];\n"
- ]
- }
- ],
- "source": [
- "for pair in my_dict.items():\n",
- " print(pair)\n",
- "\n",
- "print('')\n",
- "\n",
- "for key, value in my_dict.items():\n",
- " print(f'{key = }: {value = };')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "a\n",
- "b\n",
- "\n",
- "a\n",
- "b\n"
- ]
- }
- ],
- "source": [
- "for key in my_dict.keys():\n",
- " print(key)\n",
- "\n",
- "print('')\n",
- "\n",
- "for key in my_dict:\n",
- " print(key)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "A\n",
- "[1]\n"
- ]
- }
- ],
- "source": [
- "for val in my_dict.values():\n",
- " print(val)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Эти методы имеют много применений. Например, они могут использоваться для форматированного вывода содержимого словаря, как вы можете видеть это в примере сверху. Или для создания списка значений словаря."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**clear()**\n",
- "\n",
- "Модифицирующий метод."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "clear(...)\n",
- " D.clear() -> None. Remove all items from D.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.clear)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'a': 1, 'b': 2}\n",
- "{}\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "print(my_dict)\n",
- "\n",
- "my_dict.clear()\n",
- "print(my_dict)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**pop()**\n",
- "\n",
- "Модифицирующий метод."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "pop(...)\n",
- " D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n",
- " \n",
- " If the key is not found, return the default if given; otherwise,\n",
- " raise a KeyError.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.pop)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'a': 1, 'b': 2}\n",
- "poped value: 2; dict: {'a': 1};\n",
- "poped value: None; dict: {'a': 1};\n"
- ]
- },
- {
- "ename": "KeyError",
- "evalue": "'b'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 40\u001b[0m line \u001b[0;36m1\n\u001b[0;32m 7\u001b[0m popped_value \u001b[39m=\u001b[39m my_dict\u001b[39m.\u001b[39mpop(\u001b[39m'\u001b[39m\u001b[39mb\u001b[39m\u001b[39m'\u001b[39m, \u001b[39mNone\u001b[39;00m)\n\u001b[0;32m 8\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39mpoped value: \u001b[39m\u001b[39m{\u001b[39;00mpopped_value\u001b[39m}\u001b[39;00m\u001b[39m; dict: \u001b[39m\u001b[39m{\u001b[39;00mmy_dict\u001b[39m}\u001b[39;00m\u001b[39m;\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m---> 10\u001b[0m my_dict\u001b[39m.\u001b[39;49mpop(\u001b[39m'\u001b[39;49m\u001b[39mb\u001b[39;49m\u001b[39m'\u001b[39;49m)\n",
- "\u001b[1;31mKeyError\u001b[0m: 'b'"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "print(my_dict)\n",
- "\n",
- "popped_value = my_dict.pop('b')\n",
- "print(f'poped value: {popped_value}; dict: {my_dict};')\n",
- "\n",
- "popped_value = my_dict.pop('b', None)\n",
- "print(f'poped value: {popped_value}; dict: {my_dict};')\n",
- "\n",
- "my_dict.pop('b')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Данный метод также позволяет избежать использование ненужного ветвления.\n",
- "\n",
- "Было:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "dict[\"c\"] value: 3; dict: {'a': 1, 'b': 2}\n"
- ]
- }
- ],
- "source": [
- "my_dict ={'a': 1, 'b': 2, 'c': 3}\n",
- "\n",
- "if 'c' in my_dict:\n",
- " value_c = my_dict['c']\n",
- " del my_dict['c']\n",
- "\n",
- "else:\n",
- " value_c = None\n",
- "\n",
- "print(f'dict[\"c\"] value: {value_c}; dict: {my_dict}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Стало:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "dict[\"c\"] value: None; dict: {'a': 1, 'b': 2}\n"
- ]
- }
- ],
- "source": [
- "value_c = my_dict.pop('c', None)\n",
- "\n",
- "print(f'dict[\"c\"] value: {value_c}; dict: {my_dict}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**popitem()**\n",
- "\n",
- "Модифицирующий метод."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "popitem(self, /)\n",
- " Remove and return a (key, value) pair as a 2-tuple.\n",
- " \n",
- " Pairs are returned in LIFO (last-in, first-out) order.\n",
- " Raises KeyError if the dict is empty.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.popitem)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "key = 'c'; value = 3;\n",
- "key = 'b'; value = 2;\n",
- "key = 'a'; value = 1;\n"
- ]
- }
- ],
- "source": [
- "my_dict ={'a': 1, 'b': 2, 'c': 3}\n",
- "\n",
- "while len(my_dict):\n",
- " key, value = my_dict.popitem()\n",
- " print(f'{key = }; {value = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Данный метод может быть полезным, если вам необходимо перебрать все элементы словаря, хранение которого в памяти слишком затратны, произвести с ними некоторые действия, и удалить из словаря. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**setdefault()**\n",
- "\n",
- "Модифицирующий метод."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "setdefault(self, key, default=None, /)\n",
- " Insert key with a value of default if key is not in the dictionary.\n",
- " \n",
- " Return the value for key if key is in the dictionary, else default.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.setdefault)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "value = 1; dict: {'a': 1, 'b': 2};\n",
- "value = None; dict: {'a': 1, 'b': 2, 'c': None};\n",
- "value = 42; dict: {'a': 1, 'b': 2, 'c': None, 'd': 42};\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1, 'b': 2}\n",
- "\n",
- "value = my_dict.setdefault('a')\n",
- "print(f'{value = }; dict: {my_dict};')\n",
- "\n",
- "value = my_dict.setdefault('c')\n",
- "print(f'{value = }; dict: {my_dict};')\n",
- "\n",
- "value = my_dict.setdefault('d', 42)\n",
- "print(f'{value = }; dict: {my_dict};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Этот метод также подойдет для оптимизации кода и отказа от использования ненужных ветвлений. Предположим, мы принимает на вход некоторую последовательность. Предположим, последовательность нуклиотидов. Наша задача определить позиции расположения каждого нуклеотида в этой последовательности.\n",
- "\n",
- "**Неэффективный путь решения:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_nucleatide_appearance(dna_sequence: str) -> dict[str, list[int]]:\n",
- " appearances = {}\n",
- "\n",
- " for i, nucleatide in enumerate(dna_sequence, start=1):\n",
- " nucleatide_appearances: list = appearances.get(nucleatide, [])\n",
- " nucleatide_appearances.append(i)\n",
- " appearances[nucleatide] = nucleatide_appearances\n",
- "\n",
- " return appearances"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 76,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'a': [1, 6, 7, 10], 'g': [2, 8], 'c': [3, 4, 5], 't': [9, 11]}\n"
- ]
- }
- ],
- "source": [
- "dna_sequence = 'agcccaagtat'\n",
- "\n",
- "print(get_nucleatide_appearance(dna_sequence))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Эффективный путь решения:**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 77,
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_nucleatide_appearance(dna_sequence: str) -> dict[str, list[int]]:\n",
- " appearances = {}\n",
- "\n",
- " for i, nucleatide in enumerate(dna_sequence, start=1):\n",
- " appearances.setdefault(nucleatide, []).append(i)\n",
- "\n",
- " return appearances"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 78,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'a': [1, 6, 7, 10], 'g': [2, 8], 'c': [3, 4, 5], 't': [9, 11]}\n"
- ]
- }
- ],
- "source": [
- "dna_sequence = 'agcccaagtat'\n",
- "\n",
- "print(get_nucleatide_appearance(dna_sequence))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**update()**\n",
- "\n",
- "Модифицирующий метод."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 79,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "update(...)\n",
- " D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n",
- " If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n",
- " If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n",
- " In either case, this is followed by: for k in F: D[k] = F[k]\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(dict.update)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 84,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'a': 42, 'b': 2, 'c': 3, 'd': 5}\n"
- ]
- }
- ],
- "source": [
- "my_dict = {'a': 1}\n",
- "\n",
- "my_dict.update({'a': 42, 'b': 2})\n",
- "my_dict.update([('c', 3), ('d', 4)])\n",
- "my_dict.update(d=5)\n",
- "\n",
- "print(my_dict)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Множества\n",
- "\n",
- "Множества - непорядоченная коллекция уникальных элементов. Элементы множества обязательно должны быть хэшируемыми. В Python есть два типа множеств - `set` - изменяемое множество, - и `frozenset` - неизменяемое множество. Отсюда следует, что set не может являться элементом set'a, а frozenset - может. Все методы и операции frozenset - это немодифицирующие операции set'a, поэтому дальше мы будем обсуждать изменяемые множества, держа в голове, что их немодифицирующие методы применимы в отношении немодифицируемых множест. \n",
- "\n",
- "Основной сценарий использования множеств - определение уникальных элементов в итерируемом объекте. Также, за счет удобных операций над множествами, множества могут оказаться полезными в некоторых алгоритмах, использующих понятия непересекающихся множеств. Так, например, они могут быть полезными при реализации алгоритма Прима построения минимального остовного дерева.\n",
- "\n",
- "Создание множеств:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 85,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "type: set; value: {42, 3.14, 'hello'}\n",
- "type: set; value: {100}\n",
- "type: set; value: set()\n",
- "type: frozenset; value: frozenset({42, 3.14, 'hello'})\n",
- "type: frozenset; value: frozenset({100})\n",
- "type: frozenset; value: frozenset()\n"
- ]
- }
- ],
- "source": [
- "sets = [\n",
- " {42, 3.14, 'hello'},\n",
- " {100},\n",
- " set(),\n",
- " frozenset([42, 3.14, 'hello']),\n",
- " frozenset([100]),\n",
- " frozenset()\n",
- "]\n",
- "\n",
- "for sets_item in sets:\n",
- " print(f'type: {type(sets_item).__name__}; value: {sets_item}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Методы множеств и операции над ними\n",
- "\n",
- "Множества являются итерируемыми объектами. Они поддерживают вызов функции `len`, которая возвращает количество элементов множества, или, выражаясь математически, мощность множества. Также поддерживается вызов оператора `in`, который позволяет определить, принадлежит ли тот или иной объект данному множеству. \n",
- "\n",
- "**Немодифицирующие**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**copy()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 86,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "copy(...)\n",
- " Return a shallow copy of a set.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.copy)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 89,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "original: {1, 'string'};\n",
- "copy: {1, 'string'};\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "my_set = {1, 'string'}\n",
- "my_set_copy = my_set.copy()\n",
- "\n",
- "print(\n",
- " f'original: {my_set};',\n",
- " f'copy: {my_set_copy};',\n",
- " id(my_set) == id(my_set_copy),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**difference()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 92,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "difference(...)\n",
- " Return the difference of two or more sets as a new set.\n",
- " \n",
- " (i.e. all elements that are in this set but not the others.)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.difference)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 100,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{5, 6, 7, 8, 9}\n",
- "{5, 6, 7, 8, 9}\n",
- "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n",
- "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10))\n",
- "set2 = set(range(5))\n",
- "\n",
- "print(set1.difference(set2))\n",
- "print(set1 - set2)\n",
- "\n",
- "print(set1.difference(set()))\n",
- "print(set1.difference({11}))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**intersection()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 95,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "intersection(...)\n",
- " Return the intersection of two sets as a new set.\n",
- " \n",
- " (i.e. all elements that are in both sets.)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.intersection)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 98,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{0, 1, 2, 3, 4}\n",
- "{0, 1, 2, 3, 4}\n",
- "set()\n",
- "set()\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10))\n",
- "set2 = set(range(5))\n",
- "\n",
- "print(set1.intersection(set2))\n",
- "print(set1 & set2)\n",
- "\n",
- "print(set1.intersection(set()))\n",
- "print(set1.intersection({11}))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**issubset**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 101,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "issubset(...)\n",
- " Report whether another set contains this set.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.issubset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 108,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "False\n",
- "False\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10))\n",
- "set2 = set(range(5))\n",
- "\n",
- "print(set1.issubset(set2))\n",
- "print(set1 <= set2)\n",
- "\n",
- "print(set1.issubset(set()))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**issuperset**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 105,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "issuperset(...)\n",
- " Report whether this set contains another set.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.issuperset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 107,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "True\n",
- "True\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10))\n",
- "set2 = set(range(5))\n",
- "\n",
- "print(set1.issuperset(set2))\n",
- "print(set1 >= set2)\n",
- "\n",
- "print(set1.issuperset(set()))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**union**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 113,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "union(...)\n",
- " Return the union of sets as a new set.\n",
- " \n",
- " (i.e. all elements that are in either set.)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.union)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 114,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n",
- "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10))\n",
- "set2 = set(range(5))\n",
- "\n",
- "print(set1.union(set2))\n",
- "print(set1 | set2)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**symmetric_difference()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 109,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "symmetric_difference(...)\n",
- " Return the symmetric difference of two sets as a new set.\n",
- " \n",
- " (i.e. all elements that are in exactly one of the sets.)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.symmetric_difference)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 112,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{5, 6, 7, 8, 9, 10, 11, 12, 15}\n",
- "{5, 6, 7, 8, 9, 10, 11, 12, 15}\n"
- ]
- }
- ],
- "source": [
- "set1 = set(range(10)) | set(range(13, 16))\n",
- "set2 = set(range(5)) | set(range(10, 15))\n",
- "\n",
- "print(set1.symmetric_difference(set2))\n",
- "print(set1 ^ set2)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Модифицирующие**\n",
- "\n",
- "Все операторы, результатами операций с которыми являются новые множества, могут образовывать операторы составного присваивания: `|=`, `-=`, `|=`, `&=`. Несложно догадаться, что именно делают эти операторы. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**add()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 116,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "add(...)\n",
- " Add an element to a set.\n",
- " \n",
- " This has no effect if the element is already present.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.add)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 117,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{1, 2, 3, 'a'}\n"
- ]
- },
- {
- "ename": "TypeError",
- "evalue": "unhashable type: 'list'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 88\u001b[0m line \u001b[0;36m8\n\u001b[0;32m 4\u001b[0m my_set\u001b[39m.\u001b[39madd(\u001b[39m'\u001b[39m\u001b[39ma\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m 6\u001b[0m \u001b[39mprint\u001b[39m(my_set)\n\u001b[1;32m----> 8\u001b[0m my_set\u001b[39m.\u001b[39;49madd([\u001b[39m1\u001b[39;49m, \u001b[39m2\u001b[39;49m, \u001b[39m3\u001b[39;49m])\n",
- "\u001b[1;31mTypeError\u001b[0m: unhashable type: 'list'"
- ]
- }
- ],
- "source": [
- "my_set = {1, 2, 3}\n",
- "\n",
- "my_set.add(3)\n",
- "my_set.add('a')\n",
- "\n",
- "print(my_set)\n",
- "\n",
- "my_set.add([1, 2, 3])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**clear()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 118,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "set()\n"
- ]
- }
- ],
- "source": [
- "my_set = {1, 2, 3}\n",
- "my_set.clear()\n",
- "\n",
- "print(my_set)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**discard()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 119,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "discard(...)\n",
- " Remove an element from a set if it is a member.\n",
- " \n",
- " Unlike set.remove(), the discard() method does not raise\n",
- " an exception when an element is missing from the set.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.discard)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 120,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{2, 3}\n"
- ]
- }
- ],
- "source": [
- "my_set = {1, 2, 3}\n",
- "my_set.discard(1)\n",
- "my_set.discard(42)\n",
- "\n",
- "print(my_set)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**pop()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 121,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on method_descriptor:\n",
- "\n",
- "pop(...)\n",
- " Remove and return an arbitrary set element.\n",
- " Raises KeyError if the set is empty.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(set.pop)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 122,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "popped value: 1\n",
- "popped value: 2\n",
- "popped value: 3\n"
- ]
- }
- ],
- "source": [
- "my_set = {1, 2, 3}\n",
- "\n",
- "while len(my_set):\n",
- " print(f'popped value: {my_set.pop()}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 123,
- "metadata": {},
- "outputs": [
- {
- "ename": "KeyError",
- "evalue": "'pop from an empty set'",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 97\u001b[0m line \u001b[0;36m1\n\u001b[1;32m----> 1\u001b[0m my_set\u001b[39m.\u001b[39;49mpop()\n",
- "\u001b[1;31mKeyError\u001b[0m: 'pop from an empty set'"
- ]
- }
- ],
- "source": [
- "my_set.pop()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**remove()**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 124,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{2, 3}\n"
- ]
- },
- {
- "ename": "KeyError",
- "evalue": "42",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson5\\interactive_conspect.ipynb Cell 99\u001b[0m line \u001b[0;36m5\n\u001b[0;32m 2\u001b[0m my_set\u001b[39m.\u001b[39mremove(\u001b[39m1\u001b[39m)\n\u001b[0;32m 3\u001b[0m \u001b[39mprint\u001b[39m(my_set)\n\u001b[1;32m----> 5\u001b[0m my_set\u001b[39m.\u001b[39;49mremove(\u001b[39m42\u001b[39;49m)\n",
- "\u001b[1;31mKeyError\u001b[0m: 42"
- ]
- }
- ],
- "source": [
- "my_set = {1, 2, 3}\n",
- "my_set.remove(1)\n",
- "print(my_set)\n",
- "\n",
- "my_set.remove(42)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson5/sem5_312/T5_integer2roman.py b/lessons/lesson5/sem5_312/T5_integer2roman.py
deleted file mode 100644
index 242b8bb4..00000000
--- a/lessons/lesson5/sem5_312/T5_integer2roman.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-Задача 5: Перовод из десятичной системы в Римскую
-
-Римская система записи чисел использует следующие 7 символов: 'I', 'V', 'X', 'L', 'C', 'D' и 'M'.
-
-Символ | Значение
-I | 1
-V | 5
-X | 10
-L | 50
-C | 100
-D | 500
-M | 1000
-
-Например, число 2 в Римской системе записывается как 'II',
-просто приписывание двух единиц рядом.
-Число 12 записывается как 'XII'. Другими словами это 'X' + 'II'.
-Число 27 записывается как 'XXVII', что предстваляется как 'XX' + 'V' + 'II'.
-
-Римские цифры обычно пишутся от наибольшего к наименьшему слева направо.
-Однако число 4 это НЕ 'IIII'. Вместо этого число 4 записывается как 'IV'.
-Поскольку меньшее число стоит перед большим (единица перед пятью),
-мы вычитаем 1 и получаем 4.
-Аналогичный принцип применяется для числа 9, которое пишется так: 'IX'.
-Существует 6 ситуаций, где используется такое вычитание:
- 1) IV = 4, IX = 9
- 2) XL = 40, XC = 90
- 3) CD = 400, CM = 900
-
-Ваша задача перевести десятичное число в Римскую систему записи
-"""
-
-
-def intToRoman(num: int) -> str:
- """ Перевод числа из десятичной системы записи в Римскую
-
- Вход:
- num: int
- натуральное число, которое нужно записать в Римской системе
-
- Выход:
- roman: str
- Римская запись входящего числа
- """
- pass
-
-
-if __name__ == "__main__":
- assert intToRoman(3) == "III"
- assert intToRoman(58) == "LVIII"
- assert intToRoman(1994) == "MCMXCIV"
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_312/T6_longest_substring.py b/lessons/lesson5/sem5_312/T6_longest_substring.py
deleted file mode 100644
index 8d982834..00000000
--- a/lessons/lesson5/sem5_312/T6_longest_substring.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Задача 6: самая длинная подстрока без повторяющихся символов
-
-Вам дана строка s.
-Найдите длину ДЛИННЕЙШЕЙШЕГО ПОДСЛОВА,
-в котором нет повторяющихся символов
-
-
-def1:
- слово w называется ПОДСЛОВОМ слова s, если
- существуют слова x и y, такие что: s = x * w * y,
- т.е. x - это префикс слова s, а y - суффикс.
-
-def2:
- слово, состоящее только из уникальных символов,
- будем называть ОСОБЫМ.
-
-
-Нужно найти длину самого длинного особого подслова
-"""
-
-def lengthOfLongestSubstring(s: str) -> int:
- """ Подсчёт длины самого длинного подслова
- без повторяющихся символов
-
- Вход:
- s : str
- исходная строка, в которой ищется особое подслово
-
- Выход:
- subs_len: int
- длина максимального особого подслова
- """
- pass
-
-
-if __name__ == "__main__":
- assert lengthOfLongestSubstring('abcabcbb') == 3
- assert lengthOfLongestSubstring('bbbbb') == 1
- assert lengthOfLongestSubstring('pwwkew') == 3 # 'pwke' является подпоследовательностью,
- # но не подсловом
diff --git a/lessons/lesson5/sem5_312/t1_unique_simbols.py b/lessons/lesson5/sem5_312/t1_unique_simbols.py
deleted file mode 100644
index 80591786..00000000
--- a/lessons/lesson5/sem5_312/t1_unique_simbols.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-Задача 1: подсчёт количества уникальных символов в строке
-"""
-
-def unique(string: str) -> int:
- """ Подсчёт количества уникальных символов в строке
-
- Вход:
- string: str
- исследуемая строка
-
- Выход:
- count: int
- количество уникальных символов в строке
- """
- return len(set(string))
-
-if __name__ == "__main__":
- assert unique("мама") == 2
- assert unique("qwerty") == 6
- assert unique("aaa") == 1
-
diff --git a/lessons/lesson5/sem5_312/t2_compare.py b/lessons/lesson5/sem5_312/t2_compare.py
deleted file mode 100644
index afbb7cbf..00000000
--- a/lessons/lesson5/sem5_312/t2_compare.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-Задача 2: сравнение слов
-
-Вам даны 2 слова: word1 и word2
-Проверьте, возможно ли из уникальных
-Букв первого слова составить второе.
-"""
-
-def is_anagram(word1: str, word2: str) -> bool:
- """ Проверка возможности составления слова word2
- из букв слова word1
-
- Вход:
- word1: str
- слово, из букв которого надо составить требуемое слово
- word2: str
- слово, которое требуется состваить из букв первого слова
-
- Выход:
- is_anagram: bool
- Отчёт о возможности составления нужного слова.
- True, если слово word2 можно составить
- из уникальных букв слова word1. Иначе, False
- """
- set1 = set(word1)
- set2 = set(word2)
- return set2.issubset(set1)
-
-
-if __name__ == "__main__":
- assert is_anagram("abc", "aabc") == True
- assert is_anagram("aac", "aabc") == False
- assert is_anagram("abc", "aa") == True
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_312/t3_scolarships.py b/lessons/lesson5/sem5_312/t3_scolarships.py
deleted file mode 100644
index bd73cd19..00000000
--- a/lessons/lesson5/sem5_312/t3_scolarships.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-Задача 3: Раздача стипендий
-
-В университете царит бюрократия и ведётся множество различных списков студентов.
-За выдающиеся результаты в учёбе и отсутствие выговоров студентов включают в список лучших студентов,
-а за активную социальную деятельность - в список социально активных студентов.
-Также ведутся список студентов, получающих плохие оценки, и список студентов,
-получивших дисциплинарные взыскания.
-
-Каждый год студентам этого университета раздаётся стипендия, назначаемая специальной комиссией.
-При этом члены комиссии выбирают стипендиатов на своё усмотрение, но есть несколько правил:
-
- 1) Все лучшие студенты обязаны получить стипендию
- 2) Среди социально активных студентов, которые не являются при этом лучшими, получить стипендию может не больше половины
- 3) Среди студентов с дисциплинарными взысканиями стипендию может получить не больше одного человека
- 4) Студенты с плохими оценками не могут получить стипендию
- 5) Среди студентов, не включенных в списки лучших, худших или социально активных, получить стипендию могут не более трёх человек
-
-Ваша задача проверить, соответствует ли предоставленный стипендиальной комиссией список правилам университета.
-"""
-
-def is_scolarship_correct(best_students, active_students, delinquent_studens, lagging_students, all_students, scolarships):
- """ Проверка корректности распределения стипендий по соц.группам студентов.
-
- Вход:
- best_students: list
- список лучших студентов
- active_students: list
- список социально активных студентов
- delinquent_students: list
- список студентов с дисциплинарками
- lagging_students: list
- список отстающих студентов
- all_students: list
- список всех студентов университета
- scolarships: list
- список студентов, выдвинутых на получение стипендии
-
- Выход:
- is_correct: bool
- Отчёт о корректности распределения стипендий.
- True - если правила соблюдены, а иначе False.
- """
- pass
-
-
-if __name__ == "__main__":
- pass
diff --git a/lessons/lesson5/sem5_312/t4_work_control.py b/lessons/lesson5/sem5_312/t4_work_control.py
deleted file mode 100644
index 4055b7f9..00000000
--- a/lessons/lesson5/sem5_312/t4_work_control.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-Задача 4: Контроль работы
-
-Мистер G - ответственный за контроль количества проделанной работы сотрудниками некоторой фирмы.
-Вся информация хранится в базе данных, с которой работает G.
-Работа с БД организована запросно-ответным образом. Запросы бывают нескольких типов:
- 1) Приём нового сотрудника на работу:
- $ <ник> +
- Идентификационный номер G (как оператор БД) устанавливает сам.
- Ник придумывает сам трудоустраивающийся и сообщает вам.
- 2) Увольнение сотрудника:
- $ <ник> -
- 3) Учёт рабочих часов сотрудника:
- $ <ник> <число>
- 4) Получение статуса работы
- $ status
- Отчёт имеет вид многосрочного вывода на экран следующей информации:
- <ник> <количество отработанных часов>
-
-ЗАМЕЧАНИЕ:
- в приведённом выше описании знак доллара является приглашением
- на ввод и не входит в считываемую вход-строку
-
-ГАРАНТИРУЕТСЯ,
- что id вводится правильно,
- а при приёме и увольнении ошибок в нике быть не может
-
-Однако ники некоторых работников настолько сложны, что при попытке
-учесть их рабочие часы мистер G вводит их ники неправильно.
-
-Для регулирования этой проблемы было решено следующее:
-за каждый такой неправильный ввод начисляется 1 косяк обрабатываемому сотруднику (за слишком сложный ник).
-Сотрудники, кто имеет 3 и больше косяков подлежат увольнению.
-
-Никто не гарантирует, что мистер G не совершает других ошибок при вводе запросов.
-
-Ваша задача: написать программу, которая будет корректно обрабатывать запросы мистера G
-"""
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_313/t1_unique_simbols.py b/lessons/lesson5/sem5_313/t1_unique_simbols.py
deleted file mode 100644
index 884650f1..00000000
--- a/lessons/lesson5/sem5_313/t1_unique_simbols.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-Задача 1: подсчёт количества уникальных символов в строке
-"""
-
-def unique(string: str) -> int:
- """ Подсчёт количества уникальных символов в строке
-
- Вход:
- string: str
- исследуемая строка
-
- Выход:
- count: int
- количество уникальных символов в строке
- """
- pass
-
-if __name__ == "__main__":
- pass
diff --git a/lessons/lesson5/sem5_313/t2_compare.py b/lessons/lesson5/sem5_313/t2_compare.py
deleted file mode 100644
index 3075a55f..00000000
--- a/lessons/lesson5/sem5_313/t2_compare.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-Задача 2: сравнение слов
-
-Вам даны 2 слова: word1 и word2
-Проверьте, возможно ли из уникальных
-Букв первого слова составить второе.
-"""
-
-def is_anagram(word1: str, word2: str) -> bool:
- """ Проверка возможности составления слова word2
- из букв слова word1
-
- Вход:
- word1: str
- слово, из букв которого надо составить требуемое слово
- word2: str
- слово, которое требуется состваить из букв первого слова
-
- Выход:
- is_anagram: bool
- Отчёт о возможности составления нужного слова.
- True, если слово word2 можно составить
- из уникальных букв слова word1. Иначе, False
- """
- pass
-
-
-if __name__ == "__main__":
- pass
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_313/t3_scolarships.py b/lessons/lesson5/sem5_313/t3_scolarships.py
deleted file mode 100644
index bd73cd19..00000000
--- a/lessons/lesson5/sem5_313/t3_scolarships.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-Задача 3: Раздача стипендий
-
-В университете царит бюрократия и ведётся множество различных списков студентов.
-За выдающиеся результаты в учёбе и отсутствие выговоров студентов включают в список лучших студентов,
-а за активную социальную деятельность - в список социально активных студентов.
-Также ведутся список студентов, получающих плохие оценки, и список студентов,
-получивших дисциплинарные взыскания.
-
-Каждый год студентам этого университета раздаётся стипендия, назначаемая специальной комиссией.
-При этом члены комиссии выбирают стипендиатов на своё усмотрение, но есть несколько правил:
-
- 1) Все лучшие студенты обязаны получить стипендию
- 2) Среди социально активных студентов, которые не являются при этом лучшими, получить стипендию может не больше половины
- 3) Среди студентов с дисциплинарными взысканиями стипендию может получить не больше одного человека
- 4) Студенты с плохими оценками не могут получить стипендию
- 5) Среди студентов, не включенных в списки лучших, худших или социально активных, получить стипендию могут не более трёх человек
-
-Ваша задача проверить, соответствует ли предоставленный стипендиальной комиссией список правилам университета.
-"""
-
-def is_scolarship_correct(best_students, active_students, delinquent_studens, lagging_students, all_students, scolarships):
- """ Проверка корректности распределения стипендий по соц.группам студентов.
-
- Вход:
- best_students: list
- список лучших студентов
- active_students: list
- список социально активных студентов
- delinquent_students: list
- список студентов с дисциплинарками
- lagging_students: list
- список отстающих студентов
- all_students: list
- список всех студентов университета
- scolarships: list
- список студентов, выдвинутых на получение стипендии
-
- Выход:
- is_correct: bool
- Отчёт о корректности распределения стипендий.
- True - если правила соблюдены, а иначе False.
- """
- pass
-
-
-if __name__ == "__main__":
- pass
diff --git a/lessons/lesson5/sem5_313/t4_work_control.py b/lessons/lesson5/sem5_313/t4_work_control.py
deleted file mode 100644
index 4055b7f9..00000000
--- a/lessons/lesson5/sem5_313/t4_work_control.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-Задача 4: Контроль работы
-
-Мистер G - ответственный за контроль количества проделанной работы сотрудниками некоторой фирмы.
-Вся информация хранится в базе данных, с которой работает G.
-Работа с БД организована запросно-ответным образом. Запросы бывают нескольких типов:
- 1) Приём нового сотрудника на работу:
- $ <ник> +
- Идентификационный номер G (как оператор БД) устанавливает сам.
- Ник придумывает сам трудоустраивающийся и сообщает вам.
- 2) Увольнение сотрудника:
- $ <ник> -
- 3) Учёт рабочих часов сотрудника:
- $ <ник> <число>
- 4) Получение статуса работы
- $ status
- Отчёт имеет вид многосрочного вывода на экран следующей информации:
- <ник> <количество отработанных часов>
-
-ЗАМЕЧАНИЕ:
- в приведённом выше описании знак доллара является приглашением
- на ввод и не входит в считываемую вход-строку
-
-ГАРАНТИРУЕТСЯ,
- что id вводится правильно,
- а при приёме и увольнении ошибок в нике быть не может
-
-Однако ники некоторых работников настолько сложны, что при попытке
-учесть их рабочие часы мистер G вводит их ники неправильно.
-
-Для регулирования этой проблемы было решено следующее:
-за каждый такой неправильный ввод начисляется 1 косяк обрабатываемому сотруднику (за слишком сложный ник).
-Сотрудники, кто имеет 3 и больше косяков подлежат увольнению.
-
-Никто не гарантирует, что мистер G не совершает других ошибок при вводе запросов.
-
-Ваша задача: написать программу, которая будет корректно обрабатывать запросы мистера G
-"""
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_313/t5_integer2roman.py b/lessons/lesson5/sem5_313/t5_integer2roman.py
deleted file mode 100644
index 242b8bb4..00000000
--- a/lessons/lesson5/sem5_313/t5_integer2roman.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-Задача 5: Перовод из десятичной системы в Римскую
-
-Римская система записи чисел использует следующие 7 символов: 'I', 'V', 'X', 'L', 'C', 'D' и 'M'.
-
-Символ | Значение
-I | 1
-V | 5
-X | 10
-L | 50
-C | 100
-D | 500
-M | 1000
-
-Например, число 2 в Римской системе записывается как 'II',
-просто приписывание двух единиц рядом.
-Число 12 записывается как 'XII'. Другими словами это 'X' + 'II'.
-Число 27 записывается как 'XXVII', что предстваляется как 'XX' + 'V' + 'II'.
-
-Римские цифры обычно пишутся от наибольшего к наименьшему слева направо.
-Однако число 4 это НЕ 'IIII'. Вместо этого число 4 записывается как 'IV'.
-Поскольку меньшее число стоит перед большим (единица перед пятью),
-мы вычитаем 1 и получаем 4.
-Аналогичный принцип применяется для числа 9, которое пишется так: 'IX'.
-Существует 6 ситуаций, где используется такое вычитание:
- 1) IV = 4, IX = 9
- 2) XL = 40, XC = 90
- 3) CD = 400, CM = 900
-
-Ваша задача перевести десятичное число в Римскую систему записи
-"""
-
-
-def intToRoman(num: int) -> str:
- """ Перевод числа из десятичной системы записи в Римскую
-
- Вход:
- num: int
- натуральное число, которое нужно записать в Римской системе
-
- Выход:
- roman: str
- Римская запись входящего числа
- """
- pass
-
-
-if __name__ == "__main__":
- assert intToRoman(3) == "III"
- assert intToRoman(58) == "LVIII"
- assert intToRoman(1994) == "MCMXCIV"
\ No newline at end of file
diff --git a/lessons/lesson5/sem5_313/t6_longest_substring.py b/lessons/lesson5/sem5_313/t6_longest_substring.py
deleted file mode 100644
index 8d982834..00000000
--- a/lessons/lesson5/sem5_313/t6_longest_substring.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Задача 6: самая длинная подстрока без повторяющихся символов
-
-Вам дана строка s.
-Найдите длину ДЛИННЕЙШЕЙШЕГО ПОДСЛОВА,
-в котором нет повторяющихся символов
-
-
-def1:
- слово w называется ПОДСЛОВОМ слова s, если
- существуют слова x и y, такие что: s = x * w * y,
- т.е. x - это префикс слова s, а y - суффикс.
-
-def2:
- слово, состоящее только из уникальных символов,
- будем называть ОСОБЫМ.
-
-
-Нужно найти длину самого длинного особого подслова
-"""
-
-def lengthOfLongestSubstring(s: str) -> int:
- """ Подсчёт длины самого длинного подслова
- без повторяющихся символов
-
- Вход:
- s : str
- исходная строка, в которой ищется особое подслово
-
- Выход:
- subs_len: int
- длина максимального особого подслова
- """
- pass
-
-
-if __name__ == "__main__":
- assert lengthOfLongestSubstring('abcabcbb') == 3
- assert lengthOfLongestSubstring('bbbbb') == 1
- assert lengthOfLongestSubstring('pwwkew') == 3 # 'pwke' является подпоследовательностью,
- # но не подсловом
diff --git a/lessons/lesson5/sem5_314/tasks.ipynb b/lessons/lesson5/sem5_314/tasks.ipynb
deleted file mode 100644
index 87dd5bae..00000000
--- a/lessons/lesson5/sem5_314/tasks.ipynb
+++ /dev/null
@@ -1,446 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "487da553-d858-41c6-aef5-ac7f7e701989",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 1"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9265fc1c-8bee-427e-a399-9c9a32f4dc6c",
- "metadata": {},
- "source": [
- "Подсчёт количества уникальных символов в строке"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "528a7a7e-152e-40d0-af00-8f9d571e2e69",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "def unique(string: str) -> int:\n",
- " \"\"\" Подсчёт количества уникальных символов в строке\n",
- "\n",
- " Вход:\n",
- " string: str\n",
- " исследуемая строка\n",
- " \n",
- " Выход:\n",
- " count: int\n",
- " количество уникальных символов в строке\n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "72047499-9909-4547-afe5-ddd8bb54e226",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "assert unique(\"aaaaaaaa\") == 1\n",
- "assert unique(\"abab\") == 2\n",
- "assert unique(\"abcd\") == 4"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a735932b-ecb6-4d0a-9967-75ea47fc6829",
- "metadata": {},
- "source": [
- "## Задача 2 (Сравнение слов)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "6c5de12e-ca51-4b36-9395-60da5960a089",
- "metadata": {},
- "source": [
- "Вам даны 2 слова: word1 и word2. Проверьте, возможно ли из уникальных букв первого слова составить второе."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "92d1c49b-85a3-4cb6-b942-87327c73ca80",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "def is_anagram(word1: str, word2: str) -> bool:\n",
- " \"\"\" Проверка возможности составления слова word2\n",
- " из букв слова word1\n",
- "\n",
- " Вход:\n",
- " word1: str\n",
- " слово, из букв которого надо составить требуемое слово\n",
- " word2: str\n",
- " слово, которое требуется состваить из букв первого слова\n",
- " \n",
- " Выход:\n",
- " is_anagram: bool\n",
- " Отчёт о возможности составления нужного слова.\n",
- " True, если слово word2 можно составить \n",
- " из уникальных букв слова word1. Иначе, False \n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b4749e1c-fba5-4f8c-89c8-1e9a4b3953ef",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert is_anagram(\"abcd\", \"abac\")\n",
- "assert not is_anagram(\"abcde\", \"dl\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b7df19d7-a5e1-4cc9-92bc-02ddffd7da6c",
- "metadata": {},
- "source": [
- "## Задача 3 (Cамая длинная подстрока без повторяющихся символоворигинал)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "08a0bcd7-6594-418e-a5b4-3f2d90b89778",
- "metadata": {},
- "source": [
- "Вам дана строка s. \n",
- "Найдите длину ДЛИННЕЙШЕЙШЕГО ПОДСЛОВА, \n",
- "в котором нет повторяющихся символов\n",
- "\n",
- "\n",
- "def1:\n",
- " слово w называется ПОДСЛОВОМ слова s, если\n",
- " существуют слова x и y, такие что: s = x * w * y,\n",
- " т.е. x - это префикс слова s, а y - суффикс.\n",
- "\n",
- "def2:\n",
- " слово, состоящее только из уникальных символов,\n",
- " будем называть ОСОБЫМ.\n",
- "\n",
- " \n",
- "Нужно найти длину самого длинного особого подслова"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "48320deb-95dc-4aa3-8f19-4d45b4a67348",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "def lengthOfLongestSubstring(s: str) -> int:\n",
- " \"\"\" Подсчёт длины самого длинного подслова\n",
- " без повторяющихся символов\n",
- "\n",
- " Вход:\n",
- " s : str\n",
- " исходная строка, в которой ищется особое подслово\n",
- " \n",
- " Выход:\n",
- " subs_len: int\n",
- " длина максимального особого подслова \n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "40d6c8ff-fb2a-4475-b67e-7d4810e33471",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert lengthOfLongestSubstring('abcabcbb') == 3\n",
- "assert lengthOfLongestSubstring('bbbbb') == 1\n",
- "assert lengthOfLongestSubstring('pwwkew') == 3"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bb88e8c7-e5d2-44e8-b637-33a4755c8e09",
- "metadata": {},
- "source": [
- "## Задача 4 (Раздача стипендий)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c5941773-c2a9-4bd7-8440-85a4e0613280",
- "metadata": {},
- "source": [
- "В университете царит бюрократия и ведётся множество различных списков студентов. За выдающиеся результаты в учёбе и отсутствие выговоров студентов включают в список лучших студентов, а за активную социальную деятельность - в список социально активных студентов. Также ведутся список студентов, получающих плохие оценки, и список студентов, получивших дисциплинарные взыскания. Каждый год студентам этого университета раздаётся стипендия, назначаемая специальной комиссией. При этом члены комиссии выбирают стипендиатов на своё усмотрение, но есть несколько правил:\n",
- "- Все лучшие студенты обязаны получить стипендию\n",
- "- Среди социально активных студентов, которые не являются при этом лучшими, получить стипендию может не больше половины\n",
- "- Среди студентов с дисциплинарными взысканиями стипендию может получить не больше одного человека\n",
- "- Студенты с плохими оценками не могут получить стипендию\n",
- "- Среди студентов, не включенных в списки лучших, худших или социально активных, получить стипендию могут не более трёх человек\n",
- "\n",
- "Ваша задача проверить, соответствует ли предоставленный стипендиальной комиссией список правилам университета.\n",
- "\n",
- "На вход подаются:\n",
- "best_students - список лучших студентов\n",
- "active_students - список социально активных студентов\n",
- "delinquent_students - список студентов с дисциплинарками\n",
- "lagging_students - список отстающих студентов\n",
- "all_students - список всех студентов университета\n",
- "scolarships - список студентов, выдвинутых на получение стипендии\n",
- "\n",
- "Выход:\n",
- "True - если список соответствует правилам\n",
- "False - если список не соответствует правилам"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a69a673b-270b-41c5-8354-94b994ab3548",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "def is_scolarship_correct(best_students, active_students, delinquent_studens, lagging_students, all_students, scolarships):\n",
- " \"\"\" Проверка корректности распределения стипендий по соц.группам студентов.\n",
- " \n",
- " Вход:\n",
- " best_students: list\n",
- " список лучших студентов\n",
- " active_students: list\n",
- " список социально активных студентов\n",
- " delinquent_students: list\n",
- " список студентов с дисциплинарками\n",
- " lagging_students: list\n",
- " список отстающих студентов\n",
- " all_students: list\n",
- " список всех студентов университета\n",
- " scolarships: list\n",
- " список студентов, выдвинутых на получение стипендии\n",
- " \n",
- " Выход:\n",
- " is_correct: bool\n",
- " Отчёт о корректности распределения стипендий.\n",
- " True - если правила соблюдены, а иначе False.\n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a798010c-0461-41d8-96d6-d58b633919de",
- "metadata": {},
- "outputs": [],
- "source": [
- "all_students = [\"Орехов Максим\", \"Морозова Мия\", \"Семенов Александр\", \"Горбунов Виктор\", \"Владимиров Фёдор\",\n",
- " \"Любимова Виктория\", \"Иванов Марк\", \"Кузнецова Дарья\", \"Кузнецова Екатерина\", \"Осипов Михаил\",\n",
- " \"Лебедев Александр\", \"Меркулов Артём\", \"Беляева Вера\", \"Дорохов Никита\", \"Власов Владимир\",\n",
- " \"Семенова Мария\", \"Михайлов Савва\", \"Карасев Артём\", \"Мухин Михаил\", \"Белякова Юлия\",\n",
- " \"Судаков Фёдор\", \"Власов Матвей\", \"Суслова Алина\", \"Королева Амелия\", \"Панин Дмитрий\"]\n",
- "\n",
- "best_students = [\"Любимова Виктория\", \"Карасев Артём\", \"Власов Матвей\", \"Панин Дмитрий\"]\n",
- "\n",
- "active_students = [\"Любимова Виктория\", \"Карасев Артём\", \"Кузнецова Дарья\", \"Кузнецова Екатерина\", \"Осипов Михаил\", \n",
- " \"Лебедев Александр\"]\n",
- "\n",
- "delinquent_studens = [\"Мухин Михаил\", \"Белякова Юлия\"]\n",
- "\n",
- "lagging_students = [\"Горбунов Виктор\", \"Владимиров Фёдор\"]\n",
- "\n",
- "scolarships = [\"Любимова Виктория\", \"Карасев Артём\", \"Власов Матвей\", \"Панин Дмитрий\", \"Кузнецова Екатерина\", \"Судаков Фёдор\"]\n",
- "\n",
- "assert is_scolarship_correct(best_students, active_students, delinquent_studens, lagging_students, all_students, scolarships)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "78ecb73a-6bfb-487a-8255-0eaee89634cc",
- "metadata": {},
- "source": [
- "## Задача 5 (Контроль работы)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "53e048e4-87c4-428b-bab0-fa5cd0aaafbf",
- "metadata": {},
- "source": [
- "Мистер G - ответственный за контроль количества проделанной работы сотрудниками некоторой фирмы.\n",
- "Вся информация хранится в базе данных, с которой работает G.\n",
- "Работа с БД организована запросно-ответным образом. Запросы бывают нескольких типов:\n",
- "\n",
- " 1) Приём нового сотрудника на работу:\n",
- " $ <ник> +\n",
- " Идентификационный номер G (как оператор БД) устанавливает сам. \n",
- " Ник придумывает сам трудоустраивающийся и сообщает вам.\n",
- " \n",
- " 2) Увольнение сотрудника:\n",
- " $ <ник> -\n",
- " \n",
- " 3) Учёт рабочих часов сотрудника:\n",
- " $ <ник> <число>\n",
- " \n",
- " 4) Получение статуса работы\n",
- " $ status\n",
- " Отчёт имеет вид многосрочного вывода на экран следующей информации:\n",
- " <ник> <количество отработанных часов>\n",
- "\n",
- "ЗАМЕЧАНИЕ: \n",
- " в приведённом выше описании знак доллара является приглашением \n",
- " на ввод и не входит в считываемую вход-строку\n",
- "\n",
- "ГАРАНТИРУЕТСЯ, \n",
- " что id вводится правильно, \n",
- " а при приёме и увольнении ошибок в нике быть не может\n",
- "\n",
- "Однако ники некоторых работников настолько сложны, что при попытке\n",
- "учесть их рабочие часы мистер G вводит их ники неправильно.\n",
- "\n",
- "Для регулирования этой проблемы было решено следующее: \n",
- "за каждый такой неправильный ввод начисляется 1 косяк обрабатываемому сотруднику (за слишком сложный ник).\n",
- "Сотрудники, кто имеет 3 и больше косяков подлежат увольнению.\n",
- "\n",
- "Никто не гарантирует, что мистер G не совершает других ошибок при вводе запросов.\n",
- "\n",
- "Ваша задача: написать программу, которая будет корректно обрабатывать запросы мистера G"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ede11b29-119b-4a63-b157-b0298df238fb",
- "metadata": {},
- "outputs": [],
- "source": [
- "while True:\n",
- " #YOUR CODE"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "22c415a4-4056-4ffa-b928-7480ffd39408",
- "metadata": {},
- "source": [
- "## Задача 6 (Перовод из десятичной системы в Римскую оригинал)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "6d239a98-8045-40cb-8dde-3ed6e08a5a10",
- "metadata": {},
- "source": [
- "Римская система записи чисел использует следующие 7 символов: 'I', 'V', 'X', 'L', 'C', 'D' и 'M'.\n",
- "\n",
- "Символ | Значение\n",
- "\n",
- "I | 1\n",
- "\n",
- "V | 5\n",
- "\n",
- "X | 10\n",
- "\n",
- "L | 50\n",
- "\n",
- "C | 100\n",
- "\n",
- "D | 500\n",
- "\n",
- "M | 1000\n",
- "\n",
- "Например, число 2 в Римской системе записывается как 'II',\n",
- "просто приписывание двух единиц рядом.\n",
- "Число 12 записывается как 'XII'. Другими словами это 'X' + 'II'.\n",
- "Число 27 записывается как 'XXVII', что предстваляется как 'XX' + 'V' + 'II'.\n",
- "\n",
- "Римские цифры обычно пишутся от наибольшего к наименьшему слева направо.\n",
- "Однако число 4 это НЕ 'IIII'. Вместо этого число 4 записывается как 'IV'.\n",
- "Поскольку меньшее число стоит перед большим (единица перед пятью), \n",
- "мы вычитаем 1 и получаем 4.\n",
- "Аналогичный принцип применяется для числа 9, которое пишется так: 'IX'.\n",
- "Существует 6 ситуаций, где используется такое вычитание:\n",
- " 1) IV = 4, IX = 9\n",
- " 2) XL = 40, XC = 90\n",
- " 3) CD = 400, CM = 900\n",
- "\n",
- "Ваша задача перевести десятичное число в Римскую систему записи"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "556191fe-38c6-4c23-ab72-88d52d2e6fb2",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "def intToRoman(num: int) -> str:\n",
- " \"\"\" Перевод числа из десятичной системы записи в Римскую\n",
- "\n",
- " Вход:\n",
- " num: int\n",
- " натуральное число, которое нужно записать в Римской системе\n",
- " \n",
- " Выход:\n",
- " roman: str\n",
- " Римская запись входящего числа \n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "05829fd8-9fc6-4773-9243-e80891fdb63f",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert intToRoman(3) == \"III\"\n",
- "assert intToRoman(58) == \"LVIII\"\n",
- "assert intToRoman(1994) == \"MCMXCIV\""
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson6/conspect.md b/lessons/lesson6/conspect.md
deleted file mode 100644
index 0c4b1cd5..00000000
--- a/lessons/lesson6/conspect.md
+++ /dev/null
@@ -1,291 +0,0 @@
-# Исключения
-
-**Содержание**
-
-- [Вступление](#вступление)
-- [try-блок](#try-блок)
- - [try-except](#try-except)
- - [try-finally](#try-finally)
-- [Механизм распространения ошибок](#механизм-распространения)
-- [raise](#raise)
-- [Иерархия исключений](#иерархия-исключений)
-
-## Вступление
-
-В демонстрационных примерах и вставках кода из предыдущих лекций мы неоднократно встречались с исключениями и механизмом их обработки. Сегодня пришло время детальней рассмотреть эту тему и разобраться с исключениями в Python.
-
-Исключения в Python можно определить следующим образом - это объекты, которые сообщают об ошибках или об аномалиях. Когда интерпретатор Python сталкивается с какой-либо ошибкой, он возбуждает исключение, т.е. сообщает о возникновении нештатной ситуации, передавая исключение в механизм распространения ошибки (люди, знакомые с ML, привет).
-
-Встроенные объекты языка могут самостоятельно порождать исключения, в зависимости от той или иной ситуации, однако в Python существует специальный механизм явного возбуждения исключений. Для этих целей служит специальный оператор `raise`, о чем мы подробнее поговорим ниже.
-
-Помимо возбуждения исключений, в Python, что логично, реализован механизм их обработки. Под обработкой исключений подразумевается их получение и осуществление необходимых мер для решения конкретных нештатных ситуаций. Обработка исключений позволяет предотвратить аварийное завершение программы с последующей трассировкой стека и выводом сообщения об ошибке.
-
-Что интересно, Python использует исключения не только для индикации ошибок и аварийных ситуации, но и для индикации специальных событий. Так, например, объект `StopIteration`, который мы обсуждали, говоря об итерируемых объектах, является исключением, хотя формально всего лишь сообщает интерпретатору, что в процессе итерирования мы подошли к концу итератора.
-
-Исходя из этого, стоит уделять особое внимание обработки исключений, и тщательно выбирать стратегию работы с различными ситуацийми, которые могут приводить к их появлению.
-
-## try-блок
-
-`try-блок` обеспечивает тот самый механизм обработки исключений в языке Python, о котором упомяналось в предыдущем разделе. Этот составноу утверждение существует в двух формах:
-
-- инструкция try, за которой следует хотя бы одна инструкция `excpet` (и необязательно else);
-- инструкция try, за которой слудет блок `finally`;
-
-### try-except
-
-В общем виде try-except блок выглядит следующим образом:
-```python
-try:
- do_dangerous_work()
-except [expression [as target]]:
- handle_exception()
-[else:
- do_something()]
-```
-
-В квардратные скобки здесь заключены необязательные конструкции. Данная форма try-блока может иметь более одного except-блока.
-
-Тело каждого except-блока называется *обработчиком ошибок*. expression - класс-исключение (тип данных, производный от `BaseException`; подробнее об этом мы поговорим, разбирая классы), или кортеж, состоящий из классов исключений. Код внутри тела того или иного except-блока выполняется только в том случае, если тип исключения, возбужденного в теле try-блока, совпадает с выражением except-блока. target - необязательный идентефикатор, с котором может быть связан порожденный в try-блоке и перехваченный except-блоком объект исключения. C помощью target перехваченный объект-исключение может быть использован в теле обработчика ошибок и даже возбужден повторно.
-
-Ниже приведен пример простого try-except блока:
-
-```python
-try:
- value = 1 / 0
-except ZeroDivisionError:
- print('handle zero division')
-```
-
-Если try-except блок имеет несколько except-блоков, механизм распространения ошибки будет последовательно проверять совпадение типа возбужденного исключения с выражениями представленных except-блоков. Выполнено будет только тело первого совпавшего except-блока.
-
-*Пример*:
-```python
-my_dict = {}
-
-try:
- value = my_dict['num'] / 0
-
-except ZeroDivisionError:
- print('handle zero division')
-
-except KeyError:
- print('handle key error')
-```
-
-В данном примере будет выполнен except-блок, соответсвующий исключению `KeyError`. Это произайдет, поскольку сначала интерпретатор попытается получить значение, лежащее в словаре под ключом 'num'. Поскольку данного ключа в словаре нет, возбуждается исключение KeyError, которое последовательно сопоставляется с положениями except-блоков. Исключение типа KeyError не относится к типу ZeroDivisionError, поэтому первый обработчик ошибок игнорируется. Во втором же случае, тип выражения в положении except-блока совпадает с типом возбужденного исключения, поэтому второй обработчик ошибок будет выполнен и на этом распространение ошибки завершится.
-
-Последний except-блок может не содержать в себе никакого выражения. Такой обработчик будет работать со всеми исключениями, которые добирутся до него в процессе распространения ошибки. Подобные безусловные обработчики довольно редки и обычно встречаются в декораторах, где заранее низвестно, с каким типом исключений прийдется работать.
-
-*Совет*:
-
- Старайтесь избегать подобных безусловных обработчиков, используйте их только в том случае, если вы планируете повторно возбудить полученное исключение в их теле. В противном случае, вы рискуете столкнуться с проблемой слишком широкой обработки ошибок, которая скроет от вас действительно критические аномалии вашего кода и в разы усложнит процесс отладки.
-
-Если ни один из указанных обработчиков ошибок не подойдет для обработки возбужденного исключения, исключение будет распространено дальше. Если try-блок вложен в другой try-блок (напрямую, или опосредованно, например, находясь в теле вызываемой функции), сначала будут проверены обработчики ошибок внутреннего блока, и если ни один из них не подойдет, исключение будет распространено на внешний блок. Этот факт стоит держать в голове, поскольку он может стать причиной нежелательного поведения.
-
-*Пример*:
-```python
-try:
- try:
- value = 1 / 0
-
- except:
- print('handle all exceptions')
-
-except ZeroDivisionError:
- print('handle zero division')
-```
-
-В данном примере исключение, возбужденное во внутреннем блоке, будет обработано внутренним except-блоком, посколько он является более общим, и не важно, что внешний блок является более подходящим для обработки возбужденного исключения. Соответсвенно, дальше исключение распространено не будет и тело внешнего обработчика ошибок не выполнится никогда.
-
-Данны пример приводит нас к следующему выводу:
-
- Всегда помещайте более специализированные обработчики ошибок до более общих. Ведь если поступить иначе, более общий обработчик будет экранировать собой более специализированный, что может приводить к неожиданным результатам.
-
-*Пример*:
-```python
-my_dict = {}
-
-try:
- try:
- value = my_dict['num'] / 0
-
- except ZeroDivisionError:
- print('handle zero division')
-
-except KeyError:
- print('handle key error')
-```
-
-В данном примере, напротив, будет выполнен внешний блок-обработчик, поскольку тип возбужденного исключения не соответствует положению внутреннего обработчика.
-
-Как упомяналось выше, try-except блок может завершаться необязательном else-блоком. В контексте обработки ошибок else-блок имеет то же значение, что и в контексте [циклов](../lesson2/conspect.md). else-блок имеет скорее значение then и выполняется только в том случае, если тело try-блока выполнилось без возбуждения исключения, без прерывания и без операции возврата `return` (подробнее обсудим в разговоре о функциях). else-блок является достаточно полезным, и используется для выполнения блока кода, который зависит от операций, производимых в теле try-блока.
-
-*Неправильный пример*:
-
-```python
-try:
- value = 1 / my_dict['num']
-
-except:
- print('handle exception')
-
-print(f'{value = }')
-```
-
-Данные пример демонстрирует, насколько важен else-блок. Ведь, да, в случае возникновения исключения оно будет обработано, но переменная value не будет создана, и попытка вывести ее в функции print приведет к вызовову нового исключения. Гораздо правильнее было бы написать это следующим образом:
-
-*Правильный пример*:
-```python
-try:
- value = my_dict['num']
-
-except:
- print('handle exception')
-
-else:
- print(f'{value = }')
-```
-
-В данном примере вывод значения value будет происходить только в том случае, если try-блок был выполнен без возбуждения какого-либо исключения. Иначе исключение будет обработано, а else-блок не будет выполнен.
-
-### try-finally
-
-В самом простом виде try-finally блок выглядит следующим образом:
-
-```python
-try:
- do_something()
-finally:
- do_another_things()
-```
-
-Эта форма должна иметь всего одного инструкцию finally и не может иметь else-блока если отсутствуют обработчики ошибок.
-
-finally-блок называется уборщиком (в оригинале clean-up handler). Код, который составляет тело уборщика выполняется вне зависимости от того, было ли возбуждено исключение или нет. Если в процессе распространения исключение было обработано, тело finally все равно будет выполнено, если - нет, finally-блок выполнится, но после него последует дальнейшее распространение ошибки.
-
-try-finally блок предлагает устойчивую и явную конструкцию для выполнения кода, который должен быть выполнен в любом случае. Обычно, в роли этого кода выступает освобождение некоторого ресурса: закрытие файлового дескриптора, закрытие подключения к базе данных или очистка UDP-сокета. Однако более безопасный и предпочтительный путь работы с подобными ресурcами - контекстные менеджеры.
-
-*Пример использования finally*:
-
-```python
-file = open(path_to_file, 'w')
-
-try:
- write_to_file(file, data)
-
-finally:
- file.close()
-```
-
-*Пример использования контекстных менеджеров*:
-
-```python
-with open(path_to_file, 'w') as file:
- write_to_file(file, data)
-```
-
-Как было упомянуто выше, вы можете поместить обработчик ошибок в try-finally блок. В таком случае он будет иметь следующи вид:
-
-```python
-try:
- do_something()
-except:
- handle_exception()
-finally:
- clean_up()
-```
-
-Блок finally будет выполнен в любом случае, независимо от того, было обработано исключение или нет, а потому код из приведенного примера эквивалентен следующему коду:
-
-```python
-try:
- try:
- do_something()
- except:
- handle_exception()
-finally:
- clean_up()
-```
-
-В try-finally блок может быть помещено несколько обработчиков исключений. Также, после обработчиков исключений, но до уборшика может быть помещен блок else.
-
-
-## Механизм распространения ошибок
-
-Когда происходит возбуждение исключения, обычный поток выполнения программы прерывается, дальше контроль выполнения программы переходит к механизму обработки исключений. Интерпретатор Python начинает искать подходящий обработчик ошибок по принципу, изложенному выше. Возможные варианты обработки ошибок предоставляются try-блоком. Обработчики ошибок имеют дело как с исключениями, явно возбужденными в теле try-блока, так и с исключениями возбужденными неявным образом в телах функций, вызванных в try-блоке. Если в try-блоке определен обработчик ошибок, подходящий для обработки возбужденного исключения, осуществялется процедура обработки исключения, после чего механизм обработки исключения останавливается, поток выполнения программы продолжает свою работу с блоков кода, следующих за try-блоком.
-
-Если утверждение, в ходе выполнения которого было возбуждено исключение, не имеет подходящего обработчика, исключение распространяется вверх по стеку вызова функций до тех пор пока не будет найден подходящий обработчик. В случае, если стек вызовов будет исчерапан, а подходящий обработчки так и не будет найден, программа будет аварийно завершена. Аварийное завершение сопровождается трассеровкой стека, содержащей детальную информацию о местах в коде, спровоцировавших возбуждение исключения, и вывода сообщения об ошибке.
-
-Ниже приведен небольшой пример, иллюстрирующий описанный принцип:
-
-```python
-def f():
- print('in f, before 1/0')
- 1/0
- print('in f, after 1/0')
-
-def g():
- print('in g, before f()')
- f()
- print('in g, after f()')
-
-def h():
- print('in h, before g()')
- try:
- g()
- print('in h, after g()')
- except ZeroDivisionError:
- print('ZD exception caught')
- print('function h ends')
-```
-
-*Вывод*:
-```console
-in h, before g()
-in g, before f()
-in f, before 1/0
-ZD exception caught
-function h ends
-```
-
-## raise
-
-Положение raise используется для явного возбуждения raise. В общем случае raise имеет следующий синтаксис:
-
-```python
-raise [expression]
-```
-
-Осуществлять вызов raise без выражений могут только обработчики исключений. Данный синтаксис соответствует повторному возбуждению исключения, полученного обработчиком. В этот момент выполнение тела обработчика прерывается, а механизм распространения ошибки возобновляет свою работу. Подобный вариант использования raise может быть полезен, когда обработка исключения подразумевает, например, освобождение некоторого ресурса, но не подразумевает прекращение распространения ошибки.
-
-Если же выражение присутствует, оно должно иметь тип исключения (или быть наследником BaseException, но этот разговор мы прибережем до прохождения темы ООП).
-
-*Пример*:
-
-```python
-def cross_product(seq1, seq2):
- if not seq1 or not seq2:
- raise ValueError("sequence should 't be empty")
-
- return [(x1, x2) for x1 in seq1 for x2 in seq2]
-```
-
-После возбуждения исключения ответственность за его распространение или обработку ложится на вызывающею сторону.
-
-*Совет*:
-
- Используйте raise только для возбуждения дополнительных исключений, т.е. в тех случаях, когда неявного возбуждения исключений со стороны интерпретатора не происходило бы. Не стоит использовать raise для вызова тех исключений, которые были бы возбуждены интерпретатором в любом случае.
-
-## Иерархия исключений
-
-В данном разделе будет изложена только самая общая информация об иерархии исключений в Python. За подробностями по мере необходимости лучше обращаться напрямую к [документации](https://docs.python.org/3.11/library/exceptions.html).
-
-Самый общий класс ошибок, который является родителем для прочих исключений - класс `BaseException`. Он не используется напрямую, только для создания новых классов ошибок.
-
-BaseException имеет четыре дочерних класса:
-
-- `Exception` - класс, который обобщает все остальные исключения, типа KerError, ZeroDivisionError и т.д.;
-- `GeneratorError` - специальное исключение, которое используется для возвращения значений из сопрограмм;
-- `KeyboardInterrupt` - специальное исключение, которое возбуждается после прерывания программы с помощью клавиш ctrl+C;
-- `SystemExit` - вызывается функцией `sys.exit()` для остановки работы интерпретатора;
-
diff --git "a/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb" "b/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb"
deleted file mode 100644
index 55b34759..00000000
--- "a/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb"
+++ /dev/null
@@ -1,1028 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "66b27650",
- "metadata": {},
- "source": [
- "# 1. Введение"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "01784ecd",
- "metadata": {},
- "source": [
- "## Простая задача:\n",
- "Напишите программу ввода целого числа пользователем."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "id": "e3a7bf03",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: -123_456\n"
- ]
- }
- ],
- "source": [
- "number = int(input('Введите целое число: '))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "4f54d8a6",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 123\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка, что каждый символ - цифра\n",
- "number = input('Введите целое число: ')\n",
- "if number.isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "1f4d4486",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: -123\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка лишних пробелов\n",
- "number = input('Введите целое число: ')\n",
- "if number.strip().isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "id": "e14e61d2",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +1232\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка первого знака -\n",
- "number = input('Введите целое число: ')\n",
- "number = number.strip()\n",
- "if (number[0] == '-' or number[0].isdigit()) and number[1:].isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "id": "4f9c63a6",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_456\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка первого знака +\n",
- "number = input('Введите целое число: ')\n",
- "number = number.strip()\n",
- "if (number[0] in '-+' or number[0].isdigit()) and number[1:].isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a4662f8f",
- "metadata": {},
- "source": [
- "Этот процесс можно продолжать долго. В оперделённый момент мы можем получить такой страшный код:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "id": "da2a6d2e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_345\n"
- ]
- }
- ],
- "source": [
- "number = input('Введите целое число: ').strip()\n",
- "if number and (number[0] in '+-' or number[0].isdecimal()) and \\\n",
- " all(number[i].isdecimal() or number[i] == '_' for i in range(1, len(number))) and \\\n",
- " number[-1].isdecimal():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3e8738ac",
- "metadata": {},
- "source": [
- "И это ещё не предель... \n",
- "Очевидно, задача оказалась сложнее, чем казалось на первый взгляд."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0a61b083",
- "metadata": {},
- "source": [
- "## Две основные стратегии обработки ошибок:\n",
- "\n",
- "### 1. LBYL (Look Before You Leap) — «Семь раз отмерь, один раз отрежь».\n",
- "Данный подход предполагает, что мы должны избегать ошибок. То есть мы должны сначала проверить, правильные ли у нас данные и можем ли мы с ними работать, а потом уже переходить к их обработке. \n",
- "\n",
- "С бытовой точки зрения этот подход кажется естественным и логичным. Однако, как показал предыдущий пример - не всегда... \n",
- "\n",
- "### 2. EAFP (Easier to Ask for Forgiveness than Permission) — «Легче попросить прощения, чем разрешения». \n",
- "Подход сводится к тому, чтобы попробовать выполнить какую-либо операцию. Если\n",
- "выполнить действие не получится, тогда нужно уже предпринимать какие-либо меры."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "71e850e5",
- "metadata": {},
- "source": [
- "## Основные типы исключений\n",
- "\n",
- "Вы уже не раз сталкивались с исключениями. Но давайте встретимся ещё раз:\n",
- "\n",
- "1. поделите на ноль\n",
- "2. во время команды input() остановите выполенение\n",
- "3. обратитесь к неизвестной переменной\n",
- "4. обратитесь к неизвестному атрибуту\n",
- "5. импортируйте библиотеку nuMMMpy\n",
- "6. приведите строку `qwe` к целочисленному типу\n",
- "7. вызовите функцию print() только с одной ковычкой"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "b03e6c25",
- "metadata": {},
- "outputs": [],
- "source": [
- "#1\n",
- "3 / 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "id": "ce15633c",
- "metadata": {},
- "outputs": [],
- "source": [
- "#2\n",
- "s = input()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "c59d0cd6",
- "metadata": {},
- "outputs": [],
- "source": [
- "#3\n",
- "print(some_var)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "id": "1f3ea27b",
- "metadata": {},
- "outputs": [],
- "source": [
- "#4\n",
- "some_var = 3\n",
- "some_var.some_attr"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "id": "e50ef4c5",
- "metadata": {},
- "outputs": [],
- "source": [
- "#5\n",
- "import nuMMMpy"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "id": "cb4cca37",
- "metadata": {},
- "outputs": [],
- "source": [
- "#6\n",
- "int('qwe')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "id": "b6c88c85",
- "metadata": {},
- "outputs": [],
- "source": [
- "#7\n",
- "print(\"Hello)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76ac10e1",
- "metadata": {},
- "source": [
- "[В документации к языку](https://docs.python.org/3/library/exceptions.html) Достаточно подробно описана иерархия классов исключений. \n",
- "Все исключения наследуются от базового класса `BaseException`, а все «обычные» исключения наследуется от класса `Exception`:\n",
- "\n",
- "* BaseException:\n",
- " * BaseExceptionGroup\n",
- " * GeneratorExit\n",
- " * KeyboardInterrupt\n",
- " * SystemExit\n",
- " * Exception"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83bdd436",
- "metadata": {},
- "source": [
- "Класс `Exception` содержит также несколько уровней иерархии (см. картинку): \n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83ae32d7",
- "metadata": {},
- "source": [
- "# 2. Обработка исключений в Pyhton\n",
- "\n",
- "## Пример использования \n",
- "\n",
- "Вернёмся к рассмотренной ранее задаче: ввод целого числа. Решение задачи в стиле EAFP может выглядеть следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "id": "e49da539",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: \n",
- "Это не целое число, попробуйте ещё раз\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except:\n",
- " print('Это не целое число, попробуйте ещё раз')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d9247b32",
- "metadata": {},
- "source": [
- "Однако писать просто `except` - плохо (не всегда, но плохо)..."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "59ccc0d4",
- "metadata": {},
- "source": [
- "## Краткий шаблон синтаксиса оператора `try-except`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "cec54c5a",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "except Exception as e: # указываем тип ошибки\n",
- " # обработка ошибки\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cbb278a6",
- "metadata": {},
- "source": [
- "Перепишем решение по этому шаблону:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "1e385b3e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: q\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: 'q'\n",
- "['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']\n",
- "\n",
- "\n",
- "1\n",
- "invalid literal for int() with base 10: 'q'\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except ValueError as e:\n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- " print(dir(e), sep='\\n')\n",
- " print()\n",
- " print(type(e.args))\n",
- " print(len(e.args))\n",
- " print(e.args[0])\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2121bba2",
- "metadata": {},
- "source": [
- "А что будет если прервать работу программы с клавиатуры? Исключение сработает? Что нужно сделать чтоб сработало?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "id": "a7d3e597",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 123q\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: '123q'\n",
- "Inappropriate argument value (of correct type).\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except (ValueError, KeyboardInterrupt) as e:\n",
- " if isinstance(e, ValueError):\n",
- " print(\"Это не целое число, попробуйте ещё раз\")\n",
- " print(e)\n",
- " print(e.__doc__)\n",
- " \n",
- " if isinstance(e, KeyboardInterrupt):\n",
- " print(\"Остановка работы через клавиатуру\")\n",
- " print(e)\n",
- " print(e.__doc__)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c680edf6",
- "metadata": {},
- "source": [
- "А можно ли не перечислять `ValueError, KeyboardInterrupt`? Вспомните иерархию классов."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "id": "4c5b77d9",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 2*2\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: '2*2'\n",
- "Inappropriate argument value (of correct type).\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except BaseException as e:\n",
- " if isinstance(e, ValueError):\n",
- " print(\"Это не целое число, попробуйте ещё раз\")\n",
- " print(e)\n",
- " print(e.__doc__)\n",
- " \n",
- " if isinstance(e, KeyboardInterrupt):\n",
- " print(\"Остановка работы через клавиатуру\")\n",
- " print(e)\n",
- " print(e.__doc__)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3a3831fa",
- "metadata": {},
- "source": [
- "***Но с такими обобщениями нужно быть аакуратнее!!!***"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "414ae602",
- "metadata": {},
- "source": [
- "## Стандарный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "75a09c5c",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "except Exception as e: # указываем тип ошибки\n",
- " # обработка ошибки\n",
- " pass\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5a963bbc",
- "metadata": {},
- "source": [
- "Решим задачу используя этот шаблон:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "6b7d7c82",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: й\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: 'й'\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except ValueError as e: \n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "fba4055a",
- "metadata": {},
- "source": [
- "Однако, можно обеспечить ту же функциональность без блоков `else` и `finally`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "fd1622c0",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: \n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: ''\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- " print('число введено успешно')\n",
- "except ValueError as e:\n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- " \n",
- "print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "601056d7",
- "metadata": {},
- "source": [
- "Однако первый вариант синтаксиса предпочтительнее: там явным образом делятся \"положительный\" и \"негативный\" сценарии."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d6a8a559",
- "metadata": {},
- "source": [
- "## Самый полный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "8fa16a26",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "\n",
- "except (Exception1, Exception2) as e: # указываем тип ошибки\n",
- " # обработка ошибок 1 и 2\n",
- " pass\n",
- "except Exception3:\n",
- " # обработка ошибки 3\n",
- " pass\n",
- "except Exception: # вспомните иерархию классов\n",
- " # обработка всех остальных ошибок\n",
- " pass\n",
- "\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2ada632b",
- "metadata": {},
- "source": [
- "В таком случае получим следующее решение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "id": "c636c27d",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 7\n",
- "дошли до строчки с nummmber\n",
- "name 'nummmber' is not defined\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- " if number % 7 == 0:\n",
- " print(nummmber)\n",
- " else:\n",
- " print(f\"10 / {number-1} = {10 / (number-1)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(e)\n",
- " \n",
- "except NameError as e:\n",
- " print(\"дошли до строчки с nummmber\")\n",
- " print(e)\n",
- " \n",
- " \n",
- "except Exception as e:\n",
- " print(\"вероятнее всего это деление на ноль\")\n",
- " print(e)\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "e663c067",
- "metadata": {},
- "source": [
- "Отлично! \n",
- "\n",
- "Мы научились обрабатывать основные типы исключений!"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "88cce6e8",
- "metadata": {},
- "source": [
- "## Генерация исключений. Оператор raise"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f1e0fba3",
- "metadata": {},
- "source": [
- "Создавать исключения можно с помощью оператора `raise`.\n",
- "\n",
- "Попробуйте созадать исключение на ровном месте:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "id": "00100cc6",
- "metadata": {},
- "outputs": [
- {
- "ename": "IndexError",
- "evalue": "('some massege', 1, 2)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6003/2602018470.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"some massege\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m: ('some massege', 1, 2)"
- ]
- }
- ],
- "source": [
- "raise IndexError(\"some massege\", 1, 2)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2263e90c",
- "metadata": {},
- "source": [
- "Попытаемся обработать сгенерированное исключение. \n",
- "Добавьте функцию, которая бросит исключение, если введённое число меньше нуля. \n",
- "Добавьте функцию, которая просто бросает исключение..."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "id": "05156301",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите неотрицательное целое число: 100\n",
- "вероятнее всего просто Exception от my_raiser\n",
- "\n",
- "('just exception', 1)\n",
- "1\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "def check(num):\n",
- " if num < 0:\n",
- " raise ValueError(f\"Число {num} меньше нуля!!!\", num)\n",
- " \n",
- "def my_raiser(a):\n",
- " raise Exception(\"just exception\", a)\n",
- " \n",
- "\n",
- "try:\n",
- " number = int(input('Введите неотрицательное целое число: '))\n",
- " check(number)\n",
- " \n",
- " if number > 10:\n",
- " my_raiser(1)\n",
- " else:\n",
- " print(f\"10 / {number} = {10 / (number)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "except ZeroDivisionError as e:\n",
- " print(\"поделили на ноль\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- " \n",
- "except Exception as e:\n",
- " print(\"вероятнее всего просто Exception от my_raiser\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0b708880",
- "metadata": {},
- "source": [
- "Не рекомендуется писать `raise Exception(...)`, потому что эта запись никак не отображает суть исключения."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ad0caa6a",
- "metadata": {},
- "source": [
- "## Исключния создаваемые пользователем"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9b23fc80",
- "metadata": {},
- "source": [
- "Если вы хотите создать свой тип исключений (что рекоммендуется), то нужно, как вы могли догаться создать __наследника класса Exception__ и при вызове оператора `raise` бросать соответсвующее исключение.\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "4880be1b",
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyCustomError(Exception):\n",
- " def my_work(self):\n",
- " print(\"ErrorErrorError\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "20cc7d3d",
- "metadata": {},
- "source": [
- "Скопируйте предыдущий код, только теперь в функции `my_raiser` генерируйте особое исключение."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "36dd6dc1",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите неотрицательное целое число: 100\n",
- "Поймали пользовательскую ошибку\n",
- "\n",
- "('just exception', 1)\n",
- "ErrorErrorError\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "def check(num):\n",
- " if num < 0:\n",
- " raise ValueError(f\"Число {num} меньше нуля!!!\", num)\n",
- " \n",
- "def my_raiser(a):\n",
- " raise MyCustomError(\"just exception\", a)\n",
- " \n",
- "\n",
- "try:\n",
- " number = int(input('Введите неотрицательное целое число: '))\n",
- " check(number)\n",
- " \n",
- " if number > 10:\n",
- " my_raiser(1)\n",
- " else:\n",
- " print(f\"10 / {number} = {10 / (number)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "except ZeroDivisionError as e:\n",
- " print(\"поделили на ноль\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- "except MyCustomError as e:\n",
- " print(\"Поймали пользовательскую ошибку\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " e.my_work()\n",
- " \n",
- "except Exception as e:\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "89c538a4",
- "metadata": {},
- "source": [
- "## Задача на обработку исключений:\n",
- "\n",
- "Дан список целых чисел. Пользователю предлагается ввести:\n",
- "1) номер числа из данного списка; \n",
- "2) число, на которое следует поделить число, выбранное из списка. \n",
- "\n",
- "Предусмотреть ситуации: \n",
- "1) числа с таким номером нет; \n",
- "2) в качестве делителя введен ноль; \n",
- "3) другие возможные ошибки "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5ce4bf9b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "dfdd9113",
- "metadata": {},
- "source": [
- "## Замечание: проблема метлы\n",
- "\n",
- "Главный вопрос, который встает перед нами в этой ситуации: где именно обрабатывать исключение? Общий принцип — чем ближе к месту, тем лучше. Но не всегда можно обработать исключение непосредственно там, где оно возникает.\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "В конце иерархии есть сотрудник без подчиненных — например, дворник. Если вдруг\n",
- "его метла будет сломана, то он должен понять, может ли он сам справиться с этой\n",
- "проблемой. \n",
- "1) ДА: он может починить метлу самостоятельно и продолжить работу. Тогда никаких дополнительных действий совершать не нужно. \n",
- "\n",
- "\n",
- "2) НЕТ: он обращается к своему начальнику. Если тот не может решить проблему, то он обращается к своему начальнику. И так выше, не доводя до сведения генерального директора. \n",
- "\n",
- "\n",
- "__Важно!__ \n",
- "Важно найти правильный уровень, который будет достаточно высоким, чтобы\n",
- "решить проблему и справиться с исключениями (имеет достаточно полномочий). Но не будет слишком высоким, чтобы внутренние детали реализации мелких функций не\n",
- "влияли на код, который находится на высоком уровне."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": true
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git "a/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb" "b/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb"
deleted file mode 100644
index af9d6a43..00000000
--- "a/lessons/lesson6/sem6_312/.ipynb_checkpoints/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213-checkpoint.ipynb"
+++ /dev/null
@@ -1,652 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "66b27650",
- "metadata": {},
- "source": [
- "# 1. Введение"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "01784ecd",
- "metadata": {},
- "source": [
- "## Простая задача:\n",
- "Напишите программу ввода целого числа пользователем."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e3a7bf03",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "4f54d8a6",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка, что каждый символ - цифра"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "1f4d4486",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка лишних пробелов"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "e14e61d2",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка первого знака -\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "4f9c63a6",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка первого знака +\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a4662f8f",
- "metadata": {},
- "source": [
- "Этот процесс можно продолжать долго. В оперделённый момент мы можем получить такой страшный код:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "id": "da2a6d2e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_345\n"
- ]
- }
- ],
- "source": [
- "number = input('Введите целое число: ').strip()\n",
- "if number and (number[0] in '+-' or number[0].isdecimal()) and \\\n",
- " all(number[i].isdecimal() or number[i] == '_' for i in range(1, len(number))) and \\\n",
- " number[-1].isdecimal():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3e8738ac",
- "metadata": {},
- "source": [
- "И это ещё не предель... \n",
- "Очевидно, задача оказалась сложнее, чем казалось на первый взгляд."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0a61b083",
- "metadata": {},
- "source": [
- "## Две основные стратегии обработки ошибок:\n",
- "\n",
- "### 1. LBYL (Look Before You Leap) — «Семь раз отмерь, один раз отрежь».\n",
- "Данный подход предполагает, что мы должны избегать ошибок. То есть мы должны сначала проверить, правильные ли у нас данные и можем ли мы с ними работать, а потом уже переходить к их обработке. \n",
- "\n",
- "С бытовой точки зрения этот подход кажется естественным и логичным. Однако, как показал предыдущий пример - не всегда... \n",
- "\n",
- "### 2. EAFP (Easier to Ask for Forgiveness than Permission) — «Легче попросить прощения, чем разрешения». \n",
- "Подход сводится к тому, чтобы попробовать выполнить какую-либо операцию. Если\n",
- "выполнить действие не получится, тогда нужно уже предпринимать какие-либо меры."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "71e850e5",
- "metadata": {},
- "source": [
- "## Основные типы исключений\n",
- "\n",
- "Вы уже не раз сталкивались с исключениями. Но давайте встретимся ещё раз:\n",
- "\n",
- "1. поделите на ноль\n",
- "2. во время команды input() остановите выполенение\n",
- "3. обратитесь к неизвестной переменной\n",
- "4. обратитесь к неизвестному атрибуту\n",
- "5. импортируйте библиотеку nuMMMpy\n",
- "6. приведите строку `qwe` к целочисленному типу\n",
- "7. вызовите функцию print() только с одной ковычкой"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "b03e6c25",
- "metadata": {},
- "outputs": [],
- "source": [
- "#1\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "id": "ce15633c",
- "metadata": {},
- "outputs": [],
- "source": [
- "#2\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "c59d0cd6",
- "metadata": {},
- "outputs": [],
- "source": [
- "#3\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "id": "1f3ea27b",
- "metadata": {},
- "outputs": [],
- "source": [
- "#4\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "id": "e50ef4c5",
- "metadata": {},
- "outputs": [],
- "source": [
- "#5\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "id": "cb4cca37",
- "metadata": {},
- "outputs": [],
- "source": [
- "#6\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "id": "b6c88c85",
- "metadata": {},
- "outputs": [],
- "source": [
- "#7\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76ac10e1",
- "metadata": {},
- "source": [
- "[В документации к языку](https://docs.python.org/3/library/exceptions.html) Достаточно подробно описана иерархия классов исключений. \n",
- "Все исключения наследуются от базового класса `BaseException`, а все «обычные» исключения наследуется от класса `Exception`:\n",
- "\n",
- "* BaseException:\n",
- " * BaseExceptionGroup\n",
- " * GeneratorExit\n",
- " * KeyboardInterrupt\n",
- " * SystemExit\n",
- " * Exception"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83bdd436",
- "metadata": {},
- "source": [
- "Класс `Exception` содержит также несколько уровней иерархии (см. картинку): \n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83ae32d7",
- "metadata": {},
- "source": [
- "# 2. Обработка исключений в Pyhton\n",
- "\n",
- "## Пример использования \n",
- "\n",
- "Вернёмся к рассмотренной ранее задаче: ввод целого числа. Решение задачи в стиле EAFP может выглядеть следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e49da539",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "d9247b32",
- "metadata": {},
- "source": [
- "Однако писать просто `except` - плохо (не всегда, но плохо)..."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "59ccc0d4",
- "metadata": {},
- "source": [
- "## Краткий шаблон синтаксиса оператора `try-except`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "cec54c5a",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "cbb278a6",
- "metadata": {},
- "source": [
- "Перепишем решение по этому шаблону:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1e385b3e",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "2121bba2",
- "metadata": {},
- "source": [
- "А что будет если прервать работу программы с клавиатуры? Исключение сработает? Что нужно сделать чтоб сработало?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a7d3e597",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "c680edf6",
- "metadata": {},
- "source": [
- "А можно ли не перечислять `ValueError, KeyboardInterrupt`? Вспомните иерархию классов."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4c5b77d9",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "3a3831fa",
- "metadata": {},
- "source": [
- "***Но с такими обобщениями нужно быть аакуратнее!!!***"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "414ae602",
- "metadata": {},
- "source": [
- "## Стандарный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "75a09c5c",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "5a963bbc",
- "metadata": {},
- "source": [
- "Решим задачу используя этот шаблон:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6b7d7c82",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "fba4055a",
- "metadata": {},
- "source": [
- "Однако, можно обеспечить ту же функциональность без блоков `else` и `finally`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "fd1622c0",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "601056d7",
- "metadata": {},
- "source": [
- "Однако первый вариант синтаксиса предпочтительнее: там явным образом делятся \"положительный\" и \"негативный\" сценарии."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d6a8a559",
- "metadata": {},
- "source": [
- "## Самый полный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "8fa16a26",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "\n",
- "except (Exception1, Exception2) as e: # указываем тип ошибки\n",
- " # обработка ошибок 1 и 2\n",
- " pass\n",
- "except Exception3:\n",
- " # обработка ошибки 3\n",
- " pass\n",
- "except Exception: # вспомните иерархию классов\n",
- " # обработка всех остальных ошибок\n",
- " pass\n",
- "\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2ada632b",
- "metadata": {},
- "source": [
- "В таком случае получим следующее решение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c636c27d",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "e663c067",
- "metadata": {},
- "source": [
- "Отлично! \n",
- "\n",
- "Мы научились обрабатывать основные типы исключений!"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "88cce6e8",
- "metadata": {},
- "source": [
- "## Генерация исключений. Оператор raise"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f1e0fba3",
- "metadata": {},
- "source": [
- "Создавать исключения можно с помощью оператора `raise`.\n",
- "\n",
- "Попробуйте созадать исключение на ровном месте:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "id": "00100cc6",
- "metadata": {},
- "outputs": [
- {
- "ename": "IndexError",
- "evalue": "('some massege', 1, 2)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6003/2602018470.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"some massege\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m: ('some massege', 1, 2)"
- ]
- }
- ],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "2263e90c",
- "metadata": {},
- "source": [
- "Попытаемся обработать сгенерированное исключение. \n",
- "Добавьте функцию, которая бросит исключение, если введённое число меньше нуля. \n",
- "Добавьте функцию, которая просто бросает исключение..."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "05156301",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "0b708880",
- "metadata": {},
- "source": [
- "Не рекомендуется писать `raise Exception(...)`, потому что эта запись никак не отображает суть исключения."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ad0caa6a",
- "metadata": {},
- "source": [
- "## Исключния создаваемые пользователем"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9b23fc80",
- "metadata": {},
- "source": [
- "Если вы хотите создать свой тип исключений (что рекоммендуется), то нужно, как вы могли догаться создать __наследника класса Exception__ и при вызове оператора `raise` бросать соответсвующее исключение.\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "4880be1b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "20cc7d3d",
- "metadata": {},
- "source": [
- "Скопируйте предыдущий код, только теперь в функции `my_raiser` генерируйте особое исключение."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "36dd6dc1",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "89c538a4",
- "metadata": {},
- "source": [
- "## Задача на обработку исключений:\n",
- "\n",
- "Дан список целых чисел. Пользователю предлагается ввести:\n",
- "1) номер числа из данного списка; \n",
- "2) число, на которое следует поделить число, выбранное из списка. \n",
- "\n",
- "Предусмотреть ситуации: \n",
- "1) числа с таким номером нет; \n",
- "2) в качестве делителя введен ноль; \n",
- "3) другие возможные ошибки "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5ce4bf9b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "dfdd9113",
- "metadata": {},
- "source": [
- "## Замечание: проблема метлы\n",
- "\n",
- "Главный вопрос, который встает перед нами в этой ситуации: где именно обрабатывать исключение? Общий принцип — чем ближе к месту, тем лучше. Но не всегда можно обработать исключение непосредственно там, где оно возникает.\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "В конце иерархии есть сотрудник без подчиненных — например, дворник. Если вдруг\n",
- "его метла будет сломана, то он должен понять, может ли он сам справиться с этой\n",
- "проблемой. \n",
- "1) ДА: он может починить метлу самостоятельно и продолжить работу. Тогда никаких дополнительных действий совершать не нужно. \n",
- "\n",
- "\n",
- "2) НЕТ: он обращается к своему начальнику. Если тот не может решить проблему, то он обращается к своему начальнику. И так выше, не доводя до сведения генерального директора. \n",
- "\n",
- "\n",
- "__Важно!__ \n",
- "Важно найти правильный уровень, который будет достаточно высоким, чтобы\n",
- "решить проблему и справиться с исключениями (имеет достаточно полномочий). Но не будет слишком высоким, чтобы внутренние детали реализации мелких функций не\n",
- "влияли на код, который находится на высоком уровне."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git "a/lessons/lesson6/sem6_312/08 \320\240\320\260\320\261\320\276\321\202\320\260 \321\201 \320\276\321\210\320\270\320\261\320\272\320\260\320\274\320\270.pdf" "b/lessons/lesson6/sem6_312/08 \320\240\320\260\320\261\320\276\321\202\320\260 \321\201 \320\276\321\210\320\270\320\261\320\272\320\260\320\274\320\270.pdf"
deleted file mode 100644
index 6b258a28..00000000
Binary files "a/lessons/lesson6/sem6_312/08 \320\240\320\260\320\261\320\276\321\202\320\260 \321\201 \320\276\321\210\320\270\320\261\320\272\320\260\320\274\320\270.pdf" and /dev/null differ
diff --git a/lessons/lesson6/sem6_312/Exception_tree.png b/lessons/lesson6/sem6_312/Exception_tree.png
deleted file mode 100644
index 2f4c40ee..00000000
Binary files a/lessons/lesson6/sem6_312/Exception_tree.png and /dev/null differ
diff --git a/lessons/lesson6/sem6_312/organization.png b/lessons/lesson6/sem6_312/organization.png
deleted file mode 100644
index 20751dc2..00000000
Binary files a/lessons/lesson6/sem6_312/organization.png and /dev/null differ
diff --git a/lessons/lesson6/sem6_312/t4_work_control.py b/lessons/lesson6/sem6_312/t4_work_control.py
deleted file mode 100644
index 4055b7f9..00000000
--- a/lessons/lesson6/sem6_312/t4_work_control.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-Задача 4: Контроль работы
-
-Мистер G - ответственный за контроль количества проделанной работы сотрудниками некоторой фирмы.
-Вся информация хранится в базе данных, с которой работает G.
-Работа с БД организована запросно-ответным образом. Запросы бывают нескольких типов:
- 1) Приём нового сотрудника на работу:
- $ <ник> +
- Идентификационный номер G (как оператор БД) устанавливает сам.
- Ник придумывает сам трудоустраивающийся и сообщает вам.
- 2) Увольнение сотрудника:
- $ <ник> -
- 3) Учёт рабочих часов сотрудника:
- $ <ник> <число>
- 4) Получение статуса работы
- $ status
- Отчёт имеет вид многосрочного вывода на экран следующей информации:
- <ник> <количество отработанных часов>
-
-ЗАМЕЧАНИЕ:
- в приведённом выше описании знак доллара является приглашением
- на ввод и не входит в считываемую вход-строку
-
-ГАРАНТИРУЕТСЯ,
- что id вводится правильно,
- а при приёме и увольнении ошибок в нике быть не может
-
-Однако ники некоторых работников настолько сложны, что при попытке
-учесть их рабочие часы мистер G вводит их ники неправильно.
-
-Для регулирования этой проблемы было решено следующее:
-за каждый такой неправильный ввод начисляется 1 косяк обрабатываемому сотруднику (за слишком сложный ник).
-Сотрудники, кто имеет 3 и больше косяков подлежат увольнению.
-
-Никто не гарантирует, что мистер G не совершает других ошибок при вводе запросов.
-
-Ваша задача: написать программу, которая будет корректно обрабатывать запросы мистера G
-"""
\ No newline at end of file
diff --git "a/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb" "b/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb"
deleted file mode 100644
index 55b34759..00000000
--- "a/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[solved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb"
+++ /dev/null
@@ -1,1028 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "66b27650",
- "metadata": {},
- "source": [
- "# 1. Введение"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "01784ecd",
- "metadata": {},
- "source": [
- "## Простая задача:\n",
- "Напишите программу ввода целого числа пользователем."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "id": "e3a7bf03",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: -123_456\n"
- ]
- }
- ],
- "source": [
- "number = int(input('Введите целое число: '))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "4f54d8a6",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 123\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка, что каждый символ - цифра\n",
- "number = input('Введите целое число: ')\n",
- "if number.isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "1f4d4486",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: -123\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка лишних пробелов\n",
- "number = input('Введите целое число: ')\n",
- "if number.strip().isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "id": "e14e61d2",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +1232\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка первого знака -\n",
- "number = input('Введите целое число: ')\n",
- "number = number.strip()\n",
- "if (number[0] == '-' or number[0].isdigit()) and number[1:].isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "id": "4f9c63a6",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_456\n",
- "Это не целое число, попробуйте еще раз...\n"
- ]
- }
- ],
- "source": [
- "# проверка первого знака +\n",
- "number = input('Введите целое число: ')\n",
- "number = number.strip()\n",
- "if (number[0] in '-+' or number[0].isdigit()) and number[1:].isdigit():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a4662f8f",
- "metadata": {},
- "source": [
- "Этот процесс можно продолжать долго. В оперделённый момент мы можем получить такой страшный код:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "id": "da2a6d2e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_345\n"
- ]
- }
- ],
- "source": [
- "number = input('Введите целое число: ').strip()\n",
- "if number and (number[0] in '+-' or number[0].isdecimal()) and \\\n",
- " all(number[i].isdecimal() or number[i] == '_' for i in range(1, len(number))) and \\\n",
- " number[-1].isdecimal():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3e8738ac",
- "metadata": {},
- "source": [
- "И это ещё не предель... \n",
- "Очевидно, задача оказалась сложнее, чем казалось на первый взгляд."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0a61b083",
- "metadata": {},
- "source": [
- "## Две основные стратегии обработки ошибок:\n",
- "\n",
- "### 1. LBYL (Look Before You Leap) — «Семь раз отмерь, один раз отрежь».\n",
- "Данный подход предполагает, что мы должны избегать ошибок. То есть мы должны сначала проверить, правильные ли у нас данные и можем ли мы с ними работать, а потом уже переходить к их обработке. \n",
- "\n",
- "С бытовой точки зрения этот подход кажется естественным и логичным. Однако, как показал предыдущий пример - не всегда... \n",
- "\n",
- "### 2. EAFP (Easier to Ask for Forgiveness than Permission) — «Легче попросить прощения, чем разрешения». \n",
- "Подход сводится к тому, чтобы попробовать выполнить какую-либо операцию. Если\n",
- "выполнить действие не получится, тогда нужно уже предпринимать какие-либо меры."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "71e850e5",
- "metadata": {},
- "source": [
- "## Основные типы исключений\n",
- "\n",
- "Вы уже не раз сталкивались с исключениями. Но давайте встретимся ещё раз:\n",
- "\n",
- "1. поделите на ноль\n",
- "2. во время команды input() остановите выполенение\n",
- "3. обратитесь к неизвестной переменной\n",
- "4. обратитесь к неизвестному атрибуту\n",
- "5. импортируйте библиотеку nuMMMpy\n",
- "6. приведите строку `qwe` к целочисленному типу\n",
- "7. вызовите функцию print() только с одной ковычкой"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "b03e6c25",
- "metadata": {},
- "outputs": [],
- "source": [
- "#1\n",
- "3 / 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "id": "ce15633c",
- "metadata": {},
- "outputs": [],
- "source": [
- "#2\n",
- "s = input()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "c59d0cd6",
- "metadata": {},
- "outputs": [],
- "source": [
- "#3\n",
- "print(some_var)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "id": "1f3ea27b",
- "metadata": {},
- "outputs": [],
- "source": [
- "#4\n",
- "some_var = 3\n",
- "some_var.some_attr"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "id": "e50ef4c5",
- "metadata": {},
- "outputs": [],
- "source": [
- "#5\n",
- "import nuMMMpy"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "id": "cb4cca37",
- "metadata": {},
- "outputs": [],
- "source": [
- "#6\n",
- "int('qwe')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "id": "b6c88c85",
- "metadata": {},
- "outputs": [],
- "source": [
- "#7\n",
- "print(\"Hello)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76ac10e1",
- "metadata": {},
- "source": [
- "[В документации к языку](https://docs.python.org/3/library/exceptions.html) Достаточно подробно описана иерархия классов исключений. \n",
- "Все исключения наследуются от базового класса `BaseException`, а все «обычные» исключения наследуется от класса `Exception`:\n",
- "\n",
- "* BaseException:\n",
- " * BaseExceptionGroup\n",
- " * GeneratorExit\n",
- " * KeyboardInterrupt\n",
- " * SystemExit\n",
- " * Exception"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83bdd436",
- "metadata": {},
- "source": [
- "Класс `Exception` содержит также несколько уровней иерархии (см. картинку): \n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83ae32d7",
- "metadata": {},
- "source": [
- "# 2. Обработка исключений в Pyhton\n",
- "\n",
- "## Пример использования \n",
- "\n",
- "Вернёмся к рассмотренной ранее задаче: ввод целого числа. Решение задачи в стиле EAFP может выглядеть следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "id": "e49da539",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: \n",
- "Это не целое число, попробуйте ещё раз\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except:\n",
- " print('Это не целое число, попробуйте ещё раз')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d9247b32",
- "metadata": {},
- "source": [
- "Однако писать просто `except` - плохо (не всегда, но плохо)..."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "59ccc0d4",
- "metadata": {},
- "source": [
- "## Краткий шаблон синтаксиса оператора `try-except`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "cec54c5a",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "except Exception as e: # указываем тип ошибки\n",
- " # обработка ошибки\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cbb278a6",
- "metadata": {},
- "source": [
- "Перепишем решение по этому шаблону:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "1e385b3e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: q\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: 'q'\n",
- "['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']\n",
- "\n",
- "\n",
- "1\n",
- "invalid literal for int() with base 10: 'q'\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except ValueError as e:\n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- " print(dir(e), sep='\\n')\n",
- " print()\n",
- " print(type(e.args))\n",
- " print(len(e.args))\n",
- " print(e.args[0])\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2121bba2",
- "metadata": {},
- "source": [
- "А что будет если прервать работу программы с клавиатуры? Исключение сработает? Что нужно сделать чтоб сработало?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "id": "a7d3e597",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 123q\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: '123q'\n",
- "Inappropriate argument value (of correct type).\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except (ValueError, KeyboardInterrupt) as e:\n",
- " if isinstance(e, ValueError):\n",
- " print(\"Это не целое число, попробуйте ещё раз\")\n",
- " print(e)\n",
- " print(e.__doc__)\n",
- " \n",
- " if isinstance(e, KeyboardInterrupt):\n",
- " print(\"Остановка работы через клавиатуру\")\n",
- " print(e)\n",
- " print(e.__doc__)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c680edf6",
- "metadata": {},
- "source": [
- "А можно ли не перечислять `ValueError, KeyboardInterrupt`? Вспомните иерархию классов."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "id": "4c5b77d9",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 2*2\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: '2*2'\n",
- "Inappropriate argument value (of correct type).\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except BaseException as e:\n",
- " if isinstance(e, ValueError):\n",
- " print(\"Это не целое число, попробуйте ещё раз\")\n",
- " print(e)\n",
- " print(e.__doc__)\n",
- " \n",
- " if isinstance(e, KeyboardInterrupt):\n",
- " print(\"Остановка работы через клавиатуру\")\n",
- " print(e)\n",
- " print(e.__doc__)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3a3831fa",
- "metadata": {},
- "source": [
- "***Но с такими обобщениями нужно быть аакуратнее!!!***"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "414ae602",
- "metadata": {},
- "source": [
- "## Стандарный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "75a09c5c",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "except Exception as e: # указываем тип ошибки\n",
- " # обработка ошибки\n",
- " pass\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5a963bbc",
- "metadata": {},
- "source": [
- "Решим задачу используя этот шаблон:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "6b7d7c82",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: й\n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: 'й'\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- "except ValueError as e: \n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "fba4055a",
- "metadata": {},
- "source": [
- "Однако, можно обеспечить ту же функциональность без блоков `else` и `finally`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "fd1622c0",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: \n",
- "Это не целое число, попробуйте ещё раз\n",
- "invalid literal for int() with base 10: ''\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- " print('число введено успешно')\n",
- "except ValueError as e:\n",
- " print('Это не целое число, попробуйте ещё раз')\n",
- " print(e)\n",
- " \n",
- "print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "601056d7",
- "metadata": {},
- "source": [
- "Однако первый вариант синтаксиса предпочтительнее: там явным образом делятся \"положительный\" и \"негативный\" сценарии."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d6a8a559",
- "metadata": {},
- "source": [
- "## Самый полный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "8fa16a26",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "\n",
- "except (Exception1, Exception2) as e: # указываем тип ошибки\n",
- " # обработка ошибок 1 и 2\n",
- " pass\n",
- "except Exception3:\n",
- " # обработка ошибки 3\n",
- " pass\n",
- "except Exception: # вспомните иерархию классов\n",
- " # обработка всех остальных ошибок\n",
- " pass\n",
- "\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2ada632b",
- "metadata": {},
- "source": [
- "В таком случае получим следующее решение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "id": "c636c27d",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: 7\n",
- "дошли до строчки с nummmber\n",
- "name 'nummmber' is not defined\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "try:\n",
- " number = int(input('Введите целое число: '))\n",
- " if number % 7 == 0:\n",
- " print(nummmber)\n",
- " else:\n",
- " print(f\"10 / {number-1} = {10 / (number-1)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(e)\n",
- " \n",
- "except NameError as e:\n",
- " print(\"дошли до строчки с nummmber\")\n",
- " print(e)\n",
- " \n",
- " \n",
- "except Exception as e:\n",
- " print(\"вероятнее всего это деление на ноль\")\n",
- " print(e)\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "e663c067",
- "metadata": {},
- "source": [
- "Отлично! \n",
- "\n",
- "Мы научились обрабатывать основные типы исключений!"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "88cce6e8",
- "metadata": {},
- "source": [
- "## Генерация исключений. Оператор raise"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f1e0fba3",
- "metadata": {},
- "source": [
- "Создавать исключения можно с помощью оператора `raise`.\n",
- "\n",
- "Попробуйте созадать исключение на ровном месте:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "id": "00100cc6",
- "metadata": {},
- "outputs": [
- {
- "ename": "IndexError",
- "evalue": "('some massege', 1, 2)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6003/2602018470.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"some massege\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m: ('some massege', 1, 2)"
- ]
- }
- ],
- "source": [
- "raise IndexError(\"some massege\", 1, 2)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2263e90c",
- "metadata": {},
- "source": [
- "Попытаемся обработать сгенерированное исключение. \n",
- "Добавьте функцию, которая бросит исключение, если введённое число меньше нуля. \n",
- "Добавьте функцию, которая просто бросает исключение..."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "id": "05156301",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите неотрицательное целое число: 100\n",
- "вероятнее всего просто Exception от my_raiser\n",
- "\n",
- "('just exception', 1)\n",
- "1\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "def check(num):\n",
- " if num < 0:\n",
- " raise ValueError(f\"Число {num} меньше нуля!!!\", num)\n",
- " \n",
- "def my_raiser(a):\n",
- " raise Exception(\"just exception\", a)\n",
- " \n",
- "\n",
- "try:\n",
- " number = int(input('Введите неотрицательное целое число: '))\n",
- " check(number)\n",
- " \n",
- " if number > 10:\n",
- " my_raiser(1)\n",
- " else:\n",
- " print(f\"10 / {number} = {10 / (number)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "except ZeroDivisionError as e:\n",
- " print(\"поделили на ноль\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- " \n",
- "except Exception as e:\n",
- " print(\"вероятнее всего просто Exception от my_raiser\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0b708880",
- "metadata": {},
- "source": [
- "Не рекомендуется писать `raise Exception(...)`, потому что эта запись никак не отображает суть исключения."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ad0caa6a",
- "metadata": {},
- "source": [
- "## Исключния создаваемые пользователем"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9b23fc80",
- "metadata": {},
- "source": [
- "Если вы хотите создать свой тип исключений (что рекоммендуется), то нужно, как вы могли догаться создать __наследника класса Exception__ и при вызове оператора `raise` бросать соответсвующее исключение.\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "4880be1b",
- "metadata": {},
- "outputs": [],
- "source": [
- "class MyCustomError(Exception):\n",
- " def my_work(self):\n",
- " print(\"ErrorErrorError\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "20cc7d3d",
- "metadata": {},
- "source": [
- "Скопируйте предыдущий код, только теперь в функции `my_raiser` генерируйте особое исключение."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "36dd6dc1",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите неотрицательное целое число: 100\n",
- "Поймали пользовательскую ошибку\n",
- "\n",
- "('just exception', 1)\n",
- "ErrorErrorError\n",
- "конец блока\n"
- ]
- }
- ],
- "source": [
- "def check(num):\n",
- " if num < 0:\n",
- " raise ValueError(f\"Число {num} меньше нуля!!!\", num)\n",
- " \n",
- "def my_raiser(a):\n",
- " raise MyCustomError(\"just exception\", a)\n",
- " \n",
- "\n",
- "try:\n",
- " number = int(input('Введите неотрицательное целое число: '))\n",
- " check(number)\n",
- " \n",
- " if number > 10:\n",
- " my_raiser(1)\n",
- " else:\n",
- " print(f\"10 / {number} = {10 / (number)}\")\n",
- "\n",
- "except ValueError as e:\n",
- " print(\"некорректный ввод\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " print(e.args[1])\n",
- " \n",
- "except ZeroDivisionError as e:\n",
- " print(\"поделили на ноль\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- "except MyCustomError as e:\n",
- " print(\"Поймали пользовательскую ошибку\")\n",
- " print(type(e))\n",
- " print(e)\n",
- " e.my_work()\n",
- " \n",
- "except Exception as e:\n",
- " print(type(e))\n",
- " print(e)\n",
- " \n",
- "else:\n",
- " print('число введено успешно')\n",
- "finally:\n",
- " print('конец блока')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "89c538a4",
- "metadata": {},
- "source": [
- "## Задача на обработку исключений:\n",
- "\n",
- "Дан список целых чисел. Пользователю предлагается ввести:\n",
- "1) номер числа из данного списка; \n",
- "2) число, на которое следует поделить число, выбранное из списка. \n",
- "\n",
- "Предусмотреть ситуации: \n",
- "1) числа с таким номером нет; \n",
- "2) в качестве делителя введен ноль; \n",
- "3) другие возможные ошибки "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5ce4bf9b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "dfdd9113",
- "metadata": {},
- "source": [
- "## Замечание: проблема метлы\n",
- "\n",
- "Главный вопрос, который встает перед нами в этой ситуации: где именно обрабатывать исключение? Общий принцип — чем ближе к месту, тем лучше. Но не всегда можно обработать исключение непосредственно там, где оно возникает.\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "В конце иерархии есть сотрудник без подчиненных — например, дворник. Если вдруг\n",
- "его метла будет сломана, то он должен понять, может ли он сам справиться с этой\n",
- "проблемой. \n",
- "1) ДА: он может починить метлу самостоятельно и продолжить работу. Тогда никаких дополнительных действий совершать не нужно. \n",
- "\n",
- "\n",
- "2) НЕТ: он обращается к своему начальнику. Если тот не может решить проблему, то он обращается к своему начальнику. И так выше, не доводя до сведения генерального директора. \n",
- "\n",
- "\n",
- "__Важно!__ \n",
- "Важно найти правильный уровень, который будет достаточно высоким, чтобы\n",
- "решить проблему и справиться с исключениями (имеет достаточно полномочий). Но не будет слишком высоким, чтобы внутренние детали реализации мелких функций не\n",
- "влияли на код, который находится на высоком уровне."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": true
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git "a/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb" "b/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb"
deleted file mode 100644
index af9d6a43..00000000
--- "a/lessons/lesson6/sem6_312/\320\227\320\260\320\275\321\217\321\202\320\270\320\2653[unsolved]_\320\230\321\201\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\230\320\244\320\260\320\271\320\273\321\213.ipynb"
+++ /dev/null
@@ -1,652 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "66b27650",
- "metadata": {},
- "source": [
- "# 1. Введение"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "01784ecd",
- "metadata": {},
- "source": [
- "## Простая задача:\n",
- "Напишите программу ввода целого числа пользователем."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e3a7bf03",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "4f54d8a6",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка, что каждый символ - цифра"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "1f4d4486",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка лишних пробелов"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "e14e61d2",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка первого знака -\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "4f9c63a6",
- "metadata": {},
- "outputs": [],
- "source": [
- "# проверка первого знака +\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a4662f8f",
- "metadata": {},
- "source": [
- "Этот процесс можно продолжать долго. В оперделённый момент мы можем получить такой страшный код:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "id": "da2a6d2e",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Введите целое число: +123_345\n"
- ]
- }
- ],
- "source": [
- "number = input('Введите целое число: ').strip()\n",
- "if number and (number[0] in '+-' or number[0].isdecimal()) and \\\n",
- " all(number[i].isdecimal() or number[i] == '_' for i in range(1, len(number))) and \\\n",
- " number[-1].isdecimal():\n",
- " number = int(number)\n",
- "else:\n",
- " print('Это не целое число, попробуйте еще раз...')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3e8738ac",
- "metadata": {},
- "source": [
- "И это ещё не предель... \n",
- "Очевидно, задача оказалась сложнее, чем казалось на первый взгляд."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0a61b083",
- "metadata": {},
- "source": [
- "## Две основные стратегии обработки ошибок:\n",
- "\n",
- "### 1. LBYL (Look Before You Leap) — «Семь раз отмерь, один раз отрежь».\n",
- "Данный подход предполагает, что мы должны избегать ошибок. То есть мы должны сначала проверить, правильные ли у нас данные и можем ли мы с ними работать, а потом уже переходить к их обработке. \n",
- "\n",
- "С бытовой точки зрения этот подход кажется естественным и логичным. Однако, как показал предыдущий пример - не всегда... \n",
- "\n",
- "### 2. EAFP (Easier to Ask for Forgiveness than Permission) — «Легче попросить прощения, чем разрешения». \n",
- "Подход сводится к тому, чтобы попробовать выполнить какую-либо операцию. Если\n",
- "выполнить действие не получится, тогда нужно уже предпринимать какие-либо меры."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "71e850e5",
- "metadata": {},
- "source": [
- "## Основные типы исключений\n",
- "\n",
- "Вы уже не раз сталкивались с исключениями. Но давайте встретимся ещё раз:\n",
- "\n",
- "1. поделите на ноль\n",
- "2. во время команды input() остановите выполенение\n",
- "3. обратитесь к неизвестной переменной\n",
- "4. обратитесь к неизвестному атрибуту\n",
- "5. импортируйте библиотеку nuMMMpy\n",
- "6. приведите строку `qwe` к целочисленному типу\n",
- "7. вызовите функцию print() только с одной ковычкой"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "id": "b03e6c25",
- "metadata": {},
- "outputs": [],
- "source": [
- "#1\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "id": "ce15633c",
- "metadata": {},
- "outputs": [],
- "source": [
- "#2\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "id": "c59d0cd6",
- "metadata": {},
- "outputs": [],
- "source": [
- "#3\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "id": "1f3ea27b",
- "metadata": {},
- "outputs": [],
- "source": [
- "#4\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "id": "e50ef4c5",
- "metadata": {},
- "outputs": [],
- "source": [
- "#5\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "id": "cb4cca37",
- "metadata": {},
- "outputs": [],
- "source": [
- "#6\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "id": "b6c88c85",
- "metadata": {},
- "outputs": [],
- "source": [
- "#7\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76ac10e1",
- "metadata": {},
- "source": [
- "[В документации к языку](https://docs.python.org/3/library/exceptions.html) Достаточно подробно описана иерархия классов исключений. \n",
- "Все исключения наследуются от базового класса `BaseException`, а все «обычные» исключения наследуется от класса `Exception`:\n",
- "\n",
- "* BaseException:\n",
- " * BaseExceptionGroup\n",
- " * GeneratorExit\n",
- " * KeyboardInterrupt\n",
- " * SystemExit\n",
- " * Exception"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83bdd436",
- "metadata": {},
- "source": [
- "Класс `Exception` содержит также несколько уровней иерархии (см. картинку): \n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "83ae32d7",
- "metadata": {},
- "source": [
- "# 2. Обработка исключений в Pyhton\n",
- "\n",
- "## Пример использования \n",
- "\n",
- "Вернёмся к рассмотренной ранее задаче: ввод целого числа. Решение задачи в стиле EAFP может выглядеть следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e49da539",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "d9247b32",
- "metadata": {},
- "source": [
- "Однако писать просто `except` - плохо (не всегда, но плохо)..."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "59ccc0d4",
- "metadata": {},
- "source": [
- "## Краткий шаблон синтаксиса оператора `try-except`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "cec54c5a",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "cbb278a6",
- "metadata": {},
- "source": [
- "Перепишем решение по этому шаблону:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1e385b3e",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "2121bba2",
- "metadata": {},
- "source": [
- "А что будет если прервать работу программы с клавиатуры? Исключение сработает? Что нужно сделать чтоб сработало?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a7d3e597",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "c680edf6",
- "metadata": {},
- "source": [
- "А можно ли не перечислять `ValueError, KeyboardInterrupt`? Вспомните иерархию классов."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4c5b77d9",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "3a3831fa",
- "metadata": {},
- "source": [
- "***Но с такими обобщениями нужно быть аакуратнее!!!***"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "414ae602",
- "metadata": {},
- "source": [
- "## Стандарный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "75a09c5c",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "5a963bbc",
- "metadata": {},
- "source": [
- "Решим задачу используя этот шаблон:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6b7d7c82",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "fba4055a",
- "metadata": {},
- "source": [
- "Однако, можно обеспечить ту же функциональность без блоков `else` и `finally`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "fd1622c0",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "601056d7",
- "metadata": {},
- "source": [
- "Однако первый вариант синтаксиса предпочтительнее: там явным образом делятся \"положительный\" и \"негативный\" сценарии."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d6a8a559",
- "metadata": {},
- "source": [
- "## Самый полный шаблон"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "8fa16a26",
- "metadata": {},
- "outputs": [],
- "source": [
- "try:\n",
- " # код, который может вызвать ошибку\n",
- " pass\n",
- "\n",
- "except (Exception1, Exception2) as e: # указываем тип ошибки\n",
- " # обработка ошибок 1 и 2\n",
- " pass\n",
- "except Exception3:\n",
- " # обработка ошибки 3\n",
- " pass\n",
- "except Exception: # вспомните иерархию классов\n",
- " # обработка всех остальных ошибок\n",
- " pass\n",
- "\n",
- "else:\n",
- " # что делать, если ошибок не было\n",
- " pass\n",
- "finally:\n",
- " # что сделать в любом случае\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2ada632b",
- "metadata": {},
- "source": [
- "В таком случае получим следующее решение:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c636c27d",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "e663c067",
- "metadata": {},
- "source": [
- "Отлично! \n",
- "\n",
- "Мы научились обрабатывать основные типы исключений!"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "88cce6e8",
- "metadata": {},
- "source": [
- "## Генерация исключений. Оператор raise"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f1e0fba3",
- "metadata": {},
- "source": [
- "Создавать исключения можно с помощью оператора `raise`.\n",
- "\n",
- "Попробуйте созадать исключение на ровном месте:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "id": "00100cc6",
- "metadata": {},
- "outputs": [
- {
- "ename": "IndexError",
- "evalue": "('some massege', 1, 2)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6003/2602018470.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"some massege\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mIndexError\u001b[0m: ('some massege', 1, 2)"
- ]
- }
- ],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "2263e90c",
- "metadata": {},
- "source": [
- "Попытаемся обработать сгенерированное исключение. \n",
- "Добавьте функцию, которая бросит исключение, если введённое число меньше нуля. \n",
- "Добавьте функцию, которая просто бросает исключение..."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "05156301",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "0b708880",
- "metadata": {},
- "source": [
- "Не рекомендуется писать `raise Exception(...)`, потому что эта запись никак не отображает суть исключения."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ad0caa6a",
- "metadata": {},
- "source": [
- "## Исключния создаваемые пользователем"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9b23fc80",
- "metadata": {},
- "source": [
- "Если вы хотите создать свой тип исключений (что рекоммендуется), то нужно, как вы могли догаться создать __наследника класса Exception__ и при вызове оператора `raise` бросать соответсвующее исключение.\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "id": "4880be1b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "20cc7d3d",
- "metadata": {},
- "source": [
- "Скопируйте предыдущий код, только теперь в функции `my_raiser` генерируйте особое исключение."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "36dd6dc1",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "89c538a4",
- "metadata": {},
- "source": [
- "## Задача на обработку исключений:\n",
- "\n",
- "Дан список целых чисел. Пользователю предлагается ввести:\n",
- "1) номер числа из данного списка; \n",
- "2) число, на которое следует поделить число, выбранное из списка. \n",
- "\n",
- "Предусмотреть ситуации: \n",
- "1) числа с таким номером нет; \n",
- "2) в качестве делителя введен ноль; \n",
- "3) другие возможные ошибки "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5ce4bf9b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "dfdd9113",
- "metadata": {},
- "source": [
- "## Замечание: проблема метлы\n",
- "\n",
- "Главный вопрос, который встает перед нами в этой ситуации: где именно обрабатывать исключение? Общий принцип — чем ближе к месту, тем лучше. Но не всегда можно обработать исключение непосредственно там, где оно возникает.\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "В конце иерархии есть сотрудник без подчиненных — например, дворник. Если вдруг\n",
- "его метла будет сломана, то он должен понять, может ли он сам справиться с этой\n",
- "проблемой. \n",
- "1) ДА: он может починить метлу самостоятельно и продолжить работу. Тогда никаких дополнительных действий совершать не нужно. \n",
- "\n",
- "\n",
- "2) НЕТ: он обращается к своему начальнику. Если тот не может решить проблему, то он обращается к своему начальнику. И так выше, не доводя до сведения генерального директора. \n",
- "\n",
- "\n",
- "__Важно!__ \n",
- "Важно найти правильный уровень, который будет достаточно высоким, чтобы\n",
- "решить проблему и справиться с исключениями (имеет достаточно полномочий). Но не будет слишком высоким, чтобы внутренние детали реализации мелких функций не\n",
- "влияли на код, который находится на высоком уровне."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson6/sem6_313/task.ipynb b/lessons/lesson6/sem6_313/task.ipynb
deleted file mode 100644
index 31de16da..00000000
--- a/lessons/lesson6/sem6_313/task.ipynb
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# База данных\n",
- "\n",
- "### Преамбула\n",
- "\n",
- "Представим, что мы занимаемся разработкой высоконагруженного приложения. Большинство высоконагруженных приложений сегодня не обходятся без баз данных. С развитием микрисервисной архитектуры прямой доступ к базе данных стал считаться мовитоном, для этих целей обычно используются обертки вокруг баз данных, чтобы обеспечить безопасный доступ к данным нескольких сервисов. Нашей целью будет реализовать примитивную обертку.\n",
- "\n",
- "### Детальное описание\n",
- "\n",
- "Наше приложение, без привязки к какому-либо домену, предоставляет пользователю некоторые услуги. Чтобы получить доступ к этим услугам, пользователь должен зарегистрироваться. Во время регистрации пользователь должен придумать уникальный никнейм, придумать надежный пароль, привязать адрес своей электронной почты, указать дату рождения. Причем к предоставляемой информации существует ряд требований:\n",
- "\n",
- "- никнейм должен быть уникальный в контексте нечувствительности к кейсу; \n",
- "- никнейм должен состоять не менее чем из 2 и не более чем из 10 допустимых символов; допустимыми символами считаются буквы английского алфавита в верхнем и нижнем регистре, а также цифры от 0 до 9; никнейм не может начинаться с цифры; \n",
- "- пароль может состоять только из английских буквы в верхнем и нижнем регистре, цифр от 0 до 9 и из знаков пунктуации; \n",
- "- пароль считается надежным, если его длина не меньше 8 символов; также пароль должен содержать цифры, английские буквы в верхнем и нижнем регистре, знаки препинания; \n",
- "- почта должна быть уникальна - не разрешается привязывать более одного аккаунта к одному и тому же адресу; \n",
- "- пользователь должен быть совершеннолетним, т.е. с его даты рождения должно пройти не менее 18 лет; \n",
- "\n",
- "Если никнейм, пароль, почта или возраст не соответствуют установленным требованиям, регистрация пользователя заканчивается возбуждением исключения `ValueError`, в котором сообщается, что именно не соответсвует требованиям. Иначе, записи о пользователе присваивается уникальный идентефикатор, который используется микросервисами приложения для обмена данными. В базу данных заносится следующая информация:\n",
- "\n",
- "- уникальный идентефикатор; \n",
- "- никнейм; \n",
- "- пароль; \n",
- "- электронная почта; \n",
- "- дата рождения; \n",
- "- время последней активности в приложении - сначала это дата регистрации; \n",
- "\n",
- "Визуализация данных:\n",
- "\n",
- "|id|nickname|password|email|birthday|last action timestamp|\n",
- "|--|--|--|--|--|--|\n",
- "|1 |Alex| 1a2Bc!ef| alex@gmail.com| 2001-09-10 | 2023-10-13\n",
- "\n",
- "Существуют следующие сценарии взаимодействия микросервисов нашего приложения с данной базой данных:\n",
- "\n",
- "- добавление новой записи; \n",
- "- запрос информации о записях по указанным индентификаторам; \n",
- "- изменение никнейма, пароля или электронной почты по указанному идентефикатору; причем новые значения должны удовлетворять всем ограничениям, накладываемым на соответствующие данные; \n",
- "- удаление записи из БД по указанному id; \n",
- "- обновление поля last action timestamp по id; \n",
- "\n",
- "Все действия, кроме удаления записи, сопровождаются обновлениям поля `last action timestamp` - запись текущего таймстемпа.\n",
- "\n",
- "Также, поскольку наше приложение - это стартап и мы не можем позволить себе вечное хранение огромного количества данных, наша обертка должна обладать функционалом для удаления данных пользователей, непроявляющих активность в течении полугода. Эта функция будет использоваться некторым внешним скедулером, передающим нам таймстемп относительно которого и вычисляются эти 6 месяцев. \n",
- "\n",
- "**Опционально:**\n",
- "\n",
- "- предусмотреть возможность бекапа - сохранение данных нашей питонячьей базы в текстовый файл; \n",
- "- предусмотреть возможность восстановления нашей питонячьей базы данных из текстового файла; "
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson6/sem6_314/task.ipynb b/lessons/lesson6/sem6_314/task.ipynb
deleted file mode 100644
index 31de16da..00000000
--- a/lessons/lesson6/sem6_314/task.ipynb
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# База данных\n",
- "\n",
- "### Преамбула\n",
- "\n",
- "Представим, что мы занимаемся разработкой высоконагруженного приложения. Большинство высоконагруженных приложений сегодня не обходятся без баз данных. С развитием микрисервисной архитектуры прямой доступ к базе данных стал считаться мовитоном, для этих целей обычно используются обертки вокруг баз данных, чтобы обеспечить безопасный доступ к данным нескольких сервисов. Нашей целью будет реализовать примитивную обертку.\n",
- "\n",
- "### Детальное описание\n",
- "\n",
- "Наше приложение, без привязки к какому-либо домену, предоставляет пользователю некоторые услуги. Чтобы получить доступ к этим услугам, пользователь должен зарегистрироваться. Во время регистрации пользователь должен придумать уникальный никнейм, придумать надежный пароль, привязать адрес своей электронной почты, указать дату рождения. Причем к предоставляемой информации существует ряд требований:\n",
- "\n",
- "- никнейм должен быть уникальный в контексте нечувствительности к кейсу; \n",
- "- никнейм должен состоять не менее чем из 2 и не более чем из 10 допустимых символов; допустимыми символами считаются буквы английского алфавита в верхнем и нижнем регистре, а также цифры от 0 до 9; никнейм не может начинаться с цифры; \n",
- "- пароль может состоять только из английских буквы в верхнем и нижнем регистре, цифр от 0 до 9 и из знаков пунктуации; \n",
- "- пароль считается надежным, если его длина не меньше 8 символов; также пароль должен содержать цифры, английские буквы в верхнем и нижнем регистре, знаки препинания; \n",
- "- почта должна быть уникальна - не разрешается привязывать более одного аккаунта к одному и тому же адресу; \n",
- "- пользователь должен быть совершеннолетним, т.е. с его даты рождения должно пройти не менее 18 лет; \n",
- "\n",
- "Если никнейм, пароль, почта или возраст не соответствуют установленным требованиям, регистрация пользователя заканчивается возбуждением исключения `ValueError`, в котором сообщается, что именно не соответсвует требованиям. Иначе, записи о пользователе присваивается уникальный идентефикатор, который используется микросервисами приложения для обмена данными. В базу данных заносится следующая информация:\n",
- "\n",
- "- уникальный идентефикатор; \n",
- "- никнейм; \n",
- "- пароль; \n",
- "- электронная почта; \n",
- "- дата рождения; \n",
- "- время последней активности в приложении - сначала это дата регистрации; \n",
- "\n",
- "Визуализация данных:\n",
- "\n",
- "|id|nickname|password|email|birthday|last action timestamp|\n",
- "|--|--|--|--|--|--|\n",
- "|1 |Alex| 1a2Bc!ef| alex@gmail.com| 2001-09-10 | 2023-10-13\n",
- "\n",
- "Существуют следующие сценарии взаимодействия микросервисов нашего приложения с данной базой данных:\n",
- "\n",
- "- добавление новой записи; \n",
- "- запрос информации о записях по указанным индентификаторам; \n",
- "- изменение никнейма, пароля или электронной почты по указанному идентефикатору; причем новые значения должны удовлетворять всем ограничениям, накладываемым на соответствующие данные; \n",
- "- удаление записи из БД по указанному id; \n",
- "- обновление поля last action timestamp по id; \n",
- "\n",
- "Все действия, кроме удаления записи, сопровождаются обновлениям поля `last action timestamp` - запись текущего таймстемпа.\n",
- "\n",
- "Также, поскольку наше приложение - это стартап и мы не можем позволить себе вечное хранение огромного количества данных, наша обертка должна обладать функционалом для удаления данных пользователей, непроявляющих активность в течении полугода. Эта функция будет использоваться некторым внешним скедулером, передающим нам таймстемп относительно которого и вычисляются эти 6 месяцев. \n",
- "\n",
- "**Опционально:**\n",
- "\n",
- "- предусмотреть возможность бекапа - сохранение данных нашей питонячьей базы в текстовый файл; \n",
- "- предусмотреть возможность восстановления нашей питонячьей базы данных из текстового файла; "
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson7/interactive_conspect.ipynb b/lessons/lesson7/interactive_conspect.ipynb
deleted file mode 100644
index b31c8c67..00000000
--- a/lessons/lesson7/interactive_conspect.ipynb
+++ /dev/null
@@ -1,1441 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Функции\n",
- "\n",
- "## Введение\n",
- "\n",
- "Большая часть утверждений в типичной программе на Python представляют собой часть функций. Это не удивительно, ведь подобный подход обладает целым рядом приемуществ. Одно из самых очевидных преимуществ - структурирование. Организациия программы как набора функций позволяет вам увеличить понятность структуры и читаемость кода. Также в пользу подобной организации кода говорит и факт того, что код, помещенный в тело функции, может выполняться быстрее кода, находящегося на уровне модуля.\n",
- "\n",
- "Функция - это всего лишь набор утверждений, которые выполняются по определнному запросу - функциональному вызову. В Python существует большое количество встроенных функций.\n",
- "\n",
- "В Python функции являются такими же объектами, как целые числа или списки, а потому интерпретатор и работает с ними, как с обычными объектами. Так, например, вы можете передать одну функцию в качестве аргумента другой функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Callable\n",
- "\n",
- "def do_something() -> None:\n",
- " print('do something')\n",
- "\n",
- "\n",
- "def do_something_with_func(func: Callable) -> None:\n",
- " print(f'do something with func: {func.__name__}')\n",
- " do_something()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "do something with func: do_something\n",
- "do something\n"
- ]
- }
- ],
- "source": [
- "do_something_with_func(do_something)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Вы можете использовать одну функции, в качестве результата выполнения другой функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "def produce_func() -> Callable:\n",
- " def do_something() -> None:\n",
- " print('produced function')\n",
- "\n",
- " return do_something"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "type: function;\n",
- "produced function\n"
- ]
- }
- ],
- "source": [
- "produced_func = produce_func()\n",
- "\n",
- "print(f'type: {type(produced_func).__name__};')\n",
- "produced_func()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Функция может быть привязана к переменной:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "do something\n",
- "do something\n"
- ]
- }
- ],
- "source": [
- "my_var = do_something\n",
- "\n",
- "do_something()\n",
- "my_var()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Функция может быть элементом в некоторой коллекции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "value: 1; type: int\n",
- "value: 3.14; type: float\n",
- "value: string; type: str\n",
- "value: (1, 2); type: tuple\n",
- "value: ; type: function\n"
- ]
- }
- ],
- "source": [
- "collection = [1, 3.14, 'string', (1, 2,), do_something]\n",
- "\n",
- "for elem in collection:\n",
- " print(f'value: {str(elem)}; type: {type(elem).__name__}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также, функции являются хэшируемыми (подумайте, почему), следовательно вы можете использовать функции в качестве ключей словаря. В примере ниже это свойство использовано для построение следующего словаря: ключ - это определенная математическая функция, значение - обратная к ней функция.\n",
- "\n",
- "*Пример*:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "func: sin; inverse func: asin\n",
- "func: sinh; inverse func: asinh\n",
- "func: cos; inverse func: acos\n",
- "func: cosh; inverse func: acosh\n",
- "func: asin; inverse func: sin\n",
- "func: asinh; inverse func: sinh\n",
- "func: acos; inverse func: cos\n",
- "func: acosh; inverse func: cosh\n"
- ]
- }
- ],
- "source": [
- "import math\n",
- "\n",
- "functions = {\n",
- " math.sin: math.asin, math.sinh: math.asinh,\n",
- " math.cos: math.acos, math.cosh: math.acosh\n",
- "}\n",
- "\n",
- "for key, value in list(functions.items()):\n",
- " functions[value] = key\n",
- "\n",
- "for key, value in functions.items():\n",
- " print(f'func: {key.__name__}; inverse func: {value.__name__}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Исходя из вышеперечисленных свойств можно сказать, что функции - это полноправные объекты. Т.е. объекты - удовлетворяющие следующим условиям:\n",
- "\n",
- "- могут быть созданы во время выполнения программы; \n",
- "- могут быть присвоены переменной или полюструктуры данных; \n",
- "- могут быть переданы функциям в качестве аргумента; \n",
- "- могут быть выозврщены как результат выполнения функций; "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Определение пользовательских функций\n",
- "\n",
- "Как бы очевидно это ни было, но в Python существует возможность создания пользовательских функций.\n",
- "\n",
- "### Утверждение def\n",
- "\n",
- "Утверждение `def` - самый распространенный способ создания пользовательских функций. def - составное утверждение с единственным положением, которое в общем случае выглядит следующим образом:\n",
- "\n",
- "```python\n",
- "def function_identifier(parameters):\n",
- " statements\n",
- "```\n",
- "\n",
- "Разберем по частями, что здесь происходит:\n",
- "\n",
- "- `function_identifier` - идентефикатор функции; это имя переменной, которая связывается с объектом-функции во время выполнения def; \n",
- "- `parameters` - параметры функции, необязательный набор идентификаторов, разделенных запятыми, которые будут привязаны к объектам, переданным в момент вызова функции - аргументы функции; в простейшем случае, когда функция не обладает параметрами, т.е. не принимает ни одного аргумента при вызове, за идентефикатором следуют пустые круглые скобки:\n",
- " ```python\n",
- " def simple_func():\n",
- " ...\n",
- " ```\n",
- "\n",
- " Параметры - локальные переменные функции, т.е. вы не можете обратиться к параметрам функции вне функции; при каждом вызове функции аргументы, переданные в функцию вызываемой стороной, связываются с данными локальными переменными и делают возможным использование тех или иных объектах в теле функции;\n",
- "\n",
- "- `statements` - непустотой набор утверждений, также известный, как тело функции; тело функции не выполяется в момент определения функции, но определяет набор команд, которые будут выполняться при каждом вызове функции, а также их последовательность; \n",
- "\n",
- "### Подробнее про параметры\n",
- "\n",
- "Параметры функции именуют, а в случаю параметров со значениями по умолчанию еще и определяют, объекты, передаваемые в тело функции при каждом вызове. Эти переменные существуют и связываются с объектами в пространстве имен функции, которое создается заново при каждом вызове функции, и уничтожается при выходе из функции любым доступным способом (return, raise и т.д.). \n",
- "\n",
- "Параметры, которые являются простыми идентефикаторами, определяют **позиционные параметры**. Каждый вызов функции, содержащей позиционные параметры, должен сопровождаться передачей в эту функцию аргументов, соответствующих позиционным параметрам. За множеством (в том числе и пустым) позиционных параметров следует множество (в том числе и пустое) **необязательных параметров**. От позиционных параметров они отличаются лишь наличием значения по умолчанию:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Any\n",
- "\n",
- "def print_args(arg1: Any, arg2: Any = \"hello\") -> None:\n",
- " print(\n",
- " f'positional argument value: {arg1}',\n",
- " f'optional argiment value: {arg2}',\n",
- " sep='\\n',\n",
- " end='\\n\\n'\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "positional argument value: 1\n",
- "optional argiment value: hello\n",
- "\n",
- "positional argument value: 1\n",
- "optional argiment value: 2\n",
- "\n",
- "positional argument value: 1\n",
- "optional argiment value: 1\n",
- "\n",
- "print_args() missing 1 required positional argument: 'arg1'\n"
- ]
- }
- ],
- "source": [
- "print_args(1)\n",
- "print_args(1, arg2=2)\n",
- "print_args(arg2=1, arg1=1)\n",
- "\n",
- "try:\n",
- " print_args(arg2='word')\n",
- "\n",
- "except TypeError as execinfo:\n",
- " print(str(execinfo))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Во время выполнения утверждения def выполняется каждое выражение для параметров по умолчанию, ссылка на результат этого выражения сохраняется в кортеж `__defaults__`, который является атрибутом функции-объекта. В этом кортеже хранится информация о значениях по умолчанию. Когда вызов функции не содержит аргумента, связываемого с параметром по умолчанию, переменная, соответствующая данному параметру, связывается с нужным значением из кортежа `__defaults__` в момент вызова. \n",
- "\n",
- "Отдельное внимание стоит уделить тому, что Python вычисляет значения по умолчанию всего один раз в момент выполнения def, **не** во время каждого вызова, как это присходит с привязкой параметров. Это значит, что в момент отсутствия необязательных аргументов при вызове, необязательные параметры привязываются к одним и тем же объектов. \n",
- "\n",
- "*Совет*:\n",
- "\n",
- " Будьте осторожны при использовании изменяемых типов данных в качестве значений по умолчанию.\n",
- "\n",
- "*Пример*:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Any\n",
- "\n",
- "# неправильный вариант\n",
- "def append_into_list(elem: Any, list_: list[Any] = []) -> list[Any]:\n",
- " print(f'list id: {id(list_)};')\n",
- " \n",
- " list_.append(elem)\n",
- " return list_"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "list id: 2295021836992;\n",
- "[1, 2, 1337]\n",
- "list id: 2295022181376;\n",
- "[42]\n",
- "list id: 2295022181376;\n",
- "[42, 3.14]\n"
- ]
- }
- ],
- "source": [
- "my_list = [1, 2]\n",
- "\n",
- "print(append_into_list(1337, my_list))\n",
- "print(append_into_list(42))\n",
- "print(append_into_list(3.14))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Optional, Any\n",
- "\n",
- "# правильный вариант\n",
- "def append_elem(\n",
- " elem: Any, list_: Optional[list[Any]] = None\n",
- ") -> list[Any]:\n",
- " if not list_:\n",
- " list_ = []\n",
- "\n",
- " print(f'list id: {id(list_)}')\n",
- "\n",
- " list_.append(elem)\n",
- " return list_"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "list id: 2295021265216\n",
- "[1, 2, 1337]\n",
- "list id: 2295021836992\n",
- "[42]\n",
- "list id: 2295022240512\n",
- "[3.14]\n"
- ]
- }
- ],
- "source": [
- "my_list = [1, 2]\n",
- "\n",
- "print(append_elem(1337, my_list))\n",
- "print(append_elem(42))\n",
- "print(append_elem(3.14))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако использование изменяемых типов данных в качестве значений по умолчанию может быть обосновано. Например, данный подход может быть использован для решения задачи кэширования."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [],
- "source": [
- "def do_expensive_coputitions(arg: float, _cache: dict = {}) -> float:\n",
- " if arg in _cache:\n",
- " return _cache[arg]\n",
- " \n",
- " if len(_cache) == 3:\n",
- " _cache.popitem()\n",
- "\n",
- " _cache[arg] = arg * 123\n",
- " print(_cache)\n",
- "\n",
- " return arg * 123"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{1: 123}\n",
- "{1: 123, 3.14: 386.22}\n",
- "{1: 123, 3.14: 386.22, 2.72: 334.56}\n",
- "{1: 123, 3.14: 386.22, 9.8: 1205.4}\n"
- ]
- }
- ],
- "source": [
- "do_expensive_coputitions(1)\n",
- "do_expensive_coputitions(3.14)\n",
- "do_expensive_coputitions(2.72)\n",
- "do_expensive_coputitions(9.8);"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "После позиционных и необязательных параметров вы можете испоьзовать специальные формы параметров: **\\*args** и **\\*\\*kwargs**. Нет ничего особенного в идентефикаторах args и kwargs, это просто стоявшиеся в сообществе идентефикаторы, вы можете использовать любые имена на свое усмотрение. Если обе эти формы присутствуют, форма с двумя звездочками обязана следовать со формой с одинарной звездой. \n",
- "\n",
- "Аргумент \\*agrs означает, что вызов функции поддерживает наличие любого числа позиционных аргументов. В случае их наличия в вызове, эти аргменты сохраняются в кортежи, связываемый с именем args, и создаваемый при каждом вызове функции. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [],
- "source": [
- "def test_args(*args) -> None:\n",
- " print(f'type: {type(args).__name__}')\n",
- " print(f'value: {args}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "type: tuple\n",
- "value: ()\n",
- "type: tuple\n",
- "value: (1, 2, 3)\n",
- "type: tuple\n",
- "value: ('h', 'e', 'l', 'l', 'o')\n"
- ]
- }
- ],
- "source": [
- "test_args()\n",
- "test_args(1, 2, 3)\n",
- "test_args(*list('hello'))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Аргумент \\*\\*kwargs означает, что вызов функции поддерживает наличие любого числа именованных аргументов. В случае их наличия, эти аргументы будут сохранены в словарь kwargs, который также создается при каждом вызове функции."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "def test_kwargs(**kwargs) -> None:\n",
- " print(f'type: {type(kwargs).__name__}')\n",
- " print(f'value: {kwargs}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "type: dict\n",
- "value: {}\n",
- "type: dict\n",
- "value: {'a': 1, 'b': 2, 'c': 3}\n",
- "type: dict\n",
- "value: {'a': 'A', 'b': 'B'}\n"
- ]
- }
- ],
- "source": [
- "test_kwargs()\n",
- "test_kwargs(a=1, b=2, c=3)\n",
- "test_kwargs(**{'a': 'A', 'b': 'B'})"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Помимо вышеперечисленных параметров, в Python существует возможность создавать строго именованные параметры. По своиму виду они похожи на необязательные параметры, но в отличие от них помещаются за выражением *args:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "def test_nameonly(*, arg1='a', arg2='b'):\n",
- " print(arg1, arg2)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1 2\n",
- "test_nameonly() takes 0 positional arguments but 2 were given\n"
- ]
- }
- ],
- "source": [
- "test_nameonly(arg1=1, arg2=2)\n",
- "\n",
- "try:\n",
- " test_nameonly(1, 2)\n",
- "\n",
- "except TypeError as execinfo:\n",
- " print(str(execinfo))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## return\n",
- "\n",
- "Утверждение return используется для возврата значений из функции и может существовать только в теле функции. За return может как следовать любое выражение, так и не следовать ничего. В момент выполнения return выполнение тела функции завершается и контроль над потоком выполнения программы возвращается к вызывающей строне.\n",
- "\n",
- "В Python функция всегда что-то возвращает. Даже если тело функции не содержит return`а, она вернет None по достижению конца тела. Также функции возвращают None и в том случае, если за утверждением return ничего не следует."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 52,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Union\n",
- "\n",
- "def get_none() -> None:\n",
- " pass\n",
- "\n",
- "def get_answer_to_main_question() -> int:\n",
- " return 42\n",
- "\n",
- "def get_return_example(arg: int) -> Union[int, None]:\n",
- " if arg % 2:\n",
- " return 42"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "None\n",
- "42\n",
- "42\n",
- "None\n"
- ]
- }
- ],
- "source": [
- "print(\n",
- " get_none(),\n",
- " get_answer_to_main_question(),\n",
- " get_return_example(1),\n",
- " get_return_example(2),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Передача аргументов\n",
- "\n",
- "В таких языках программирования, как C++, по умолчанию передача аргументов в функции осуществляется по значению. Т.е. во время вызова создается копия объекта, время жизни которого ограничено телом функции. В Python напротив передача аргументов в функции осуществляется по ссылке. Т.е. в момент вызова функции создается новая ссылка, которая связывает локальную для данной фукнции переменную с уже существующим объектом в памяти, переданным в момент вызова. Если объект имеет изменяемый тип данных, то модифицирующие операции над этим объектом в теле функции отразяется на его состоянии вне тела функции. Также вы можете осуществлять операции перепривзяки в теле функции, которые не отражаются на внешних по отношению к функции объектах."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 89,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Any\n",
- "\n",
- "\n",
- "def test_modification(target: Any, addition: Any) -> None:\n",
- " target_type = type(target)\n",
- "\n",
- " if not isinstance(addition, target_type):\n",
- " try:\n",
- " addition = target_type(addition)\n",
- "\n",
- " except ValueError:\n",
- " print(\n",
- " f'impossible to cast {type(addition).__name__}'\n",
- " f' to target type {target_type.__name__}'\n",
- " )\n",
- " return\n",
- " \n",
- " target += addition\n",
- " print(f'target after addition: {target}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 91,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "target after addition: 10\n",
- "target after addition: (1, 2, 3, 42, 56)\n",
- "target after addition: [1, 2, 123]\n",
- "\n",
- "num after modification call: 5\n",
- "tuple after modification call: (1, 2, 3)\n",
- "list after modification call: [1, 2, 123]\n"
- ]
- }
- ],
- "source": [
- "num = 5\n",
- "my_tuple = (1, 2, 3)\n",
- "my_list = [1, 2]\n",
- "\n",
- "test_modification(num, 5)\n",
- "test_modification(my_tuple, (42, 56))\n",
- "test_modification(my_list, [123])\n",
- "\n",
- "print(\n",
- " f'\\nnum after modification call: {num}',\n",
- " f'tuple after modification call: {my_tuple}',\n",
- " f'list after modification call: {my_list}',\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "При вызове функций аргументы связываются с параметрами следующим образом. Сначала происходит привязка обязательных параметров к позиционным аргументам. Порядок привязки определяется порядком следования аргуметов. Количество обязательных аргументов должно соответствовать количеству обязательных параметров. Затем происходит привязка необязательных параметров к необязательным аргументам, если таковые переданы. Если число опциональных аргуметов меньше числа опциональных параметров, происходит привязка только первых n параметров к арументам, остальные параметры связываются со своими значениями по умолчанию. Это поведение можно изменить, указав при вызове функции какому конкретно параметру вы хотите присвоить переданное значение. Затем произвольное количество неименованных аргументов помещается в *args. За *args может следовать любое число строго именованных параметров. Заключает сигнатуру функции форма **kwargs."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Некоторые атрибуты функций\n",
- "\n",
- "Как было сказано выше, функции являются объектами, и как и у любых объектов, у функций есть ряд атрибутов. В данной лекции будут затронуты не все атрибуты, но при необходимости вы можете получить список всех атрибутов и методов с помощью интроспекции функций, используя встроенную утилиту `dir()`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [],
- "source": [
- "def func():\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "['__annotations__',\n",
- " '__builtins__',\n",
- " '__call__',\n",
- " '__class__',\n",
- " '__closure__',\n",
- " '__code__',\n",
- " '__defaults__',\n",
- " '__delattr__',\n",
- " '__dict__',\n",
- " '__dir__',\n",
- " '__doc__',\n",
- " '__eq__',\n",
- " '__format__',\n",
- " '__ge__',\n",
- " '__get__',\n",
- " '__getattribute__',\n",
- " '__getstate__',\n",
- " '__globals__',\n",
- " '__gt__',\n",
- " '__hash__',\n",
- " '__init__',\n",
- " '__init_subclass__',\n",
- " '__kwdefaults__',\n",
- " '__le__',\n",
- " '__lt__',\n",
- " '__module__',\n",
- " '__name__',\n",
- " '__ne__',\n",
- " '__new__',\n",
- " '__qualname__',\n",
- " '__reduce__',\n",
- " '__reduce_ex__',\n",
- " '__repr__',\n",
- " '__setattr__',\n",
- " '__sizeof__',\n",
- " '__str__',\n",
- " '__subclasshook__']"
- ]
- },
- "execution_count": 65,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "dir(func)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### \\_\\_defaults\\_\\_\n",
- "\n",
- "\n",
- "Выше был затронут атрибут `__defaults__`, в котором хранятся значения для необязательных аргументов. При желании вы можете осуществить перепривязку этого атрибута, но делать это крайне не рекомендуется:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "before rebinding: (1, 2, 3)\n",
- "after rebinding: (1, 2, 3)\n"
- ]
- }
- ],
- "source": [
- "print(f'before rebinding: {func.__defaults__}')\n",
- "\n",
- "func.__defaults__ = (1, 2, 3)\n",
- "\n",
- "print(f'after rebinding: {func.__defaults__}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### \\_\\_name\\_\\_\n",
- "\n",
- "Также в ячейках выше был использовн атрибут `__name__`, который хранит строку - имя функции. Вы также можете осуществлять его перепривязку."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "name before: func\n",
- "name after: new name\n"
- ]
- }
- ],
- "source": [
- "print(f'name before: {func.__name__}')\n",
- "\n",
- "func.__name__ = 'new name'\n",
- "\n",
- "print(f'name after: {func.__name__}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### \\_\\_doc\\_\\_\n",
- "\n",
- "Данные атрибу хранит в себе докстринг. По умолчанию, докстринг отсутствует, и значение атрибута равно None. Однако вы можете задать значение \\_\\_doc\\_\\_, используя операцию привязки, или в момент создания функции, используя строковый литерал в начале ее тела:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [],
- "source": [
- "def func() -> None:\n",
- " \"this function do nothing\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 72,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "this function do nothing\n"
- ]
- }
- ],
- "source": [
- "print(func.__doc__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Несложно понять, что обычно данная строка используется для документирования функций. Поскольку редко можно встретить функции, документация которых умещалась бы в одной физической строке, поэтому чазе всего докстринг задается в форме многострочного литерала. Причем за года существования языка сложилась определенная культура написания подобных документирующих строк:\n",
- "\n",
- "- в первой строке докстринга в одном коротком предложении должна описываться суть функции; \n",
- "- далее идет блок описания параметров, возвращаемых значений и сторонних эффектов, отделенный от описания сути функции пустой строкой; \n",
- "- каждое описание начинается на новой строке; "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [],
- "source": [
- "def sum_two_numbers(num1: float, num2: float) -> float:\n",
- " \"\"\"\n",
- " This function sums two floats\n",
- "\n",
- " :param: num1 float - first agrument\n",
- " :param: num2 float - second argument\n",
- "\n",
- " :return: float - sum of arguments\n",
- " \"\"\"\n",
- "\n",
- " return num1 + num2"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Докстринг помогает другим программистам понять, что конкретно делает ваша функция. Также докстринг используется в качестве метаинформации интегрированными средами разработки для формирования всплывающих подсказок. Сам интерпретатор обращается к этому аргументу при вызове строенной функции `help()`. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 74,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on function sum_two_numbers in module __main__:\n",
- "\n",
- "sum_two_numbers(num1: float, num2: float) -> float\n",
- " This function sums two floats\n",
- " \n",
- " :param: num1 float - first agrument\n",
- " :param: num2 float - second argument\n",
- " \n",
- " :return: float - sum of arguments\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(sum_two_numbers)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### \\_\\_annotations\\_\\_\n",
- "\n",
- "Данный атрибут содержит словарь с аннотациями параметров и возвращаемого значения. Аннотации - часть документации функции, которая позволяет вам упростить читабельность кода, а также его написание. В качестве аннотаций может быть использовано любое выражение, но чаше всего для аннотаций используют типы данных. Сам интерпретатор не использует аннотации ни для каких целей. При наличии аннотаций типов интерпретатор не будет автоматически проверять тип переданного в функцию значений и возбуждать какое-либо исключение. Аннотации - метаинформация, которая используется интегрированными средами разработки для высвечивания контекстных подсказок. Как минимум по этой причине не стоит принебрегать этим механизмом."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "def print_string(string: str) -> None:\n",
- " print(f'received string: {string}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 77,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "received string: hello world!\n",
- "received string: 1\n",
- "received string: [2, 3, 4]\n"
- ]
- }
- ],
- "source": [
- "print_string('hello world!')\n",
- "print_string(1)\n",
- "print_string([2, 3, 4])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 78,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'string': , 'return': None}\n"
- ]
- }
- ],
- "source": [
- "print(print_string.__annotations__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Динамическое создание атрибутов\n",
- "\n",
- "Помимо предопределенных атрибутов, вы можете создавать новые атрибуты для ваших функций. В примере ниже создается специальный атрибут, для хранения информации о количестве вызовов функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 85,
- "metadata": {},
- "outputs": [],
- "source": [
- "def func_with_counter() -> int:\n",
- " if 'counter' not in func_with_counter.__dict__:\n",
- " func_with_counter.counter = 0\n",
- "\n",
- " func_with_counter.counter += 1\n",
- " return func_with_counter.counter"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 86,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "1"
- ]
- },
- "execution_count": 86,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "func_with_counter()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Пространства имен\n",
- "\n",
- "Все параметры функции, а также переменные, которые определяются в теле функции, формируют пространство имено данной функции, которое также называют локальной областью видимости функции. Каждая из переменных, входящих в пространство имен функции, называется локальной переменной по отношению к этой функции. \n",
- "\n",
- "Переменные, которые не являются локальными по отношению к данной функции, называются глобальными по отношению к ней. Глобальные переменные обычно являются отрибутами модулей. Стоит понимать, что даже когда переменная внутри тела функции имеет то же имя, что и глобальная переменная, она все равно ссылается на внутреннюю переменную, а не на переменную из глобальной области видимости. В этой ситуации говорят, что локальная переменная скрывает глобальную. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 94,
- "metadata": {},
- "outputs": [],
- "source": [
- "variable = 5\n",
- "\n",
- "def func() -> None:\n",
- " variable = 42\n",
- " print(f'inside func {variable = }')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 95,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "inside func variable = 42\n",
- "outside func variable = 5\n"
- ]
- }
- ],
- "source": [
- "func()\n",
- "\n",
- "print(f'outside func {variable = }')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Ключевое слово global\n",
- "\n",
- "По умолчанию, все переменные определяемые внутри тела функции являются локальными. Однако в Python есть специально механизм, благодаря которому это поведение может быть переопределено, и глобальные переменные могут быть изменены в ходе вызова функции. Для этого достаточно использовать следующую команду в начале тела функции:\n",
- "\n",
- "```python\n",
- "global identifiers\n",
- "```\n",
- "\n",
- "identifiers - имена глобальных переменных, необходимых для использования, перечисленные через запятую."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 105,
- "metadata": {},
- "outputs": [],
- "source": [
- "call_counter = 0\n",
- "\n",
- "\n",
- "def do_something() -> None:\n",
- " global call_counter\n",
- "\n",
- " call_counter += 1\n",
- " print('do some work')\n",
- "\n",
- "\n",
- "def do_another_things() -> None:\n",
- " global call_counter\n",
- "\n",
- " call_counter += 1\n",
- " print('do another things')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 106,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "call_counter = 0\n",
- "do some work\n",
- "do another things\n",
- "do another things\n",
- "call_counter = 3\n"
- ]
- }
- ],
- "source": [
- "print(f'{call_counter = }')\n",
- "\n",
- "do_something()\n",
- "do_another_things()\n",
- "do_another_things()\n",
- "\n",
- "print(f'{call_counter = }')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В случае отсутствия global, вызов данных функций приводил бы к ошибке:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 107,
- "metadata": {},
- "outputs": [],
- "source": [
- "def do_wrong_staff() -> None:\n",
- " call_counter += 1\n",
- " print('do wring staff')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 108,
- "metadata": {},
- "outputs": [
- {
- "ename": "UnboundLocalError",
- "evalue": "cannot access local variable 'call_counter' where it is not associated with a value",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mUnboundLocalError\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson7\\interactive_conspect.ipynb Cell 70\u001b[0m line \u001b[0;36m1\n\u001b[1;32m----> 1\u001b[0m do_wrong_staff()\n",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\general_course\\lessons\\lesson7\\interactive_conspect.ipynb Cell 70\u001b[0m line \u001b[0;36m2\n\u001b[0;32m 1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mdo_wrong_staff\u001b[39m() \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m----> 2\u001b[0m call_counter \u001b[39m+\u001b[39m\u001b[39m=\u001b[39m \u001b[39m1\u001b[39m\n\u001b[0;32m 3\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mdo wring staff\u001b[39m\u001b[39m'\u001b[39m)\n",
- "\u001b[1;31mUnboundLocalError\u001b[0m: cannot access local variable 'call_counter' where it is not associated with a value"
- ]
- }
- ],
- "source": [
- "do_wrong_staff()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Не рекомендуется использовать global для связывания некоторого состояния с некоторыми действиями, даже насмотря на наличие данной возможности. Для обозначенных целей рекомендуется использовать Объектно-Ориентированные подход и классы, которые мы рассмотрим позже."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Вложенные функции и области видимости\n",
- "\n",
- "Вы можете определять одну функцию в теле другой. Функция, определяемая в теле другой функции, называется вложенной функцией, функция, в чьем теле происходит определение другой функции - внешней функцией по отношению к вложенной. В теле вложенной функции может осуществляться доступ к локальным переменном внешней функции, однако вы не можете перепривязывать их. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 118,
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_percents(*parts) -> list[float]:\n",
- " total = sum(parts)\n",
- "\n",
- " if not total:\n",
- " print(\"can't compute percents\")\n",
- " return\n",
- " \n",
- " def compute_percents(part: float) -> str:\n",
- " return f'{part / total:.2%}'\n",
- " \n",
- " return ', '.join(compute_percents(part) for part in parts)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 119,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'16.67%, 33.33%, 50.00%'"
- ]
- },
- "execution_count": 119,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "get_percents(1, 2, 3)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Вложенные функции, которые используют в своем теле локальные переменные внешних функций называются замыканиями. На замыканиях построены принципы создания декораторов, о чем мы поговорим в одной из следующих лекций.\n",
- "\n",
- "Если внешняя функция, содержащая замыкания, возвращает его в качестве результата выполнения, то эта внешняя функция является фабрикой. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 120,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Callable\n",
- "\n",
- "\n",
- "def make_scaler(scale_factor: float = 1) -> Callable:\n",
- " def scale(num: float):\n",
- " return num * scale_factor\n",
- " \n",
- " return scale"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 121,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "10\n",
- "25\n"
- ]
- }
- ],
- "source": [
- "scaler_x2 = make_scaler(scale_factor=2)\n",
- "scaler_x5 = make_scaler(scale_factor=5)\n",
- "\n",
- "print(scaler_x2(5))\n",
- "print(scaler_x5(5))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Ключевое слово nonlocal\n",
- "\n",
- "Как было сказано выше, вы можете использовать, но не можете перепривязывать переменные внешней функции в теле вложенной функции. Однако в Python есть механизм, который позволяет вам это делать - ключевое слово nonlocal. nonlocal ведет себя похожим образом с global, только в отличие от gloabl, nonlocal осуществляет поиск походящих имен во внешних по отношению к данной функции функциях. Когда nonlocal встречатеся в функции, вложенной на несколько уровней, интерпретатор начинает поиск нужного имени, восходя по уровням вложенности до тех пор, пока нужно имя не будет найдено, или стек вложенности не будет исчерпан. В последнем случае возбуждается исключение."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 122,
- "metadata": {},
- "outputs": [],
- "source": [
- "def f():\n",
- " var_f = 1\n",
- " def g():\n",
- " var_g = 2\n",
- " def h():\n",
- " nonlocal var_f\n",
- " var_f += var_g\n",
- "\n",
- " h()\n",
- " g()\n",
- " \n",
- " print(f'{var_f = }')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 123,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "var_f = 3\n"
- ]
- }
- ],
- "source": [
- "f()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "С помощью nonlocal вы можете создавать более гибкие и изощренные фабрики:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 124,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Callable\n",
- "\n",
- "\n",
- "def make_counter(start: int = 0) -> Callable:\n",
- " counter = start\n",
- "\n",
- " def count() -> int:\n",
- " nonlocal counter\n",
- " counter += 1\n",
- " return counter\n",
- " \n",
- " return count"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 125,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "counter0 : 1\n",
- "counter0 : 2\n",
- "counter0 : 3\n",
- "\n",
- "counter1 : 2\n",
- "counter1 : 3\n",
- "counter1 : 4\n",
- "counter1 : 5\n",
- "counter1 : 6\n"
- ]
- }
- ],
- "source": [
- "counter_0 = make_counter()\n",
- "counter_1 = make_counter(start=1)\n",
- "\n",
- "for i in range(3):\n",
- " print(f'counter0 : {counter_0()}')\n",
- "\n",
- "print('')\n",
- "\n",
- "for i in range(5):\n",
- " print(f'counter1 : {counter_1()}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Данные фабрики являются шагами в сторону ООП, поскольку позволяют связать с каждой версией счетчика определенные данные и изменять состояния функции после каждого вызова. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson7/sem7_312/strategy.png b/lessons/lesson7/sem7_312/strategy.png
deleted file mode 100644
index 627879d8..00000000
Binary files a/lessons/lesson7/sem7_312/strategy.png and /dev/null differ
diff --git a/lessons/lesson7/sem7_312/task.ipynb b/lessons/lesson7/sem7_312/task.ipynb
deleted file mode 100644
index fd0c6623..00000000
--- a/lessons/lesson7/sem7_312/task.ipynb
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Паттерн стратегия через полноправные функции\n",
- "\n",
- "## Описание паттерна\n",
- "\n",
- "В книге «Паттерны проектирования» паттерн Стратегия описывается следующим образом:\n",
- "\n",
- " Определить семейство алгоритмов, инкапсулировать каждый из\n",
- " них и сделать их взаимозаменяемыми. Стратегия позволяет заменять\n",
- " алгоритм независимо от использующих его клиентов.\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как следует из приведенной UML-даграммы, для реализации патерна авторы предлагают использовать все блага ООП, в частности, наследование, абстрактные классы, полиморфизм и пр. Однако из-за наличия в Python полноправных функций, этот паттерн может быть реализован и без использования столь сложных концепций. В этом на и предстоит убедиться."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача\n",
- "\n",
- "Рассмотрим Интернет-магазин со следующими правилами формирования скидок:\n",
- "- заказчику, имеющему не менее 1000 баллов лояльности, предоставляется глобальная скидка 5 % на весь заказ;\n",
- "- на позиции, заказанные в количестве не менее 20 единиц в одном заказе, предоставляется скидка 10 %;\n",
- "- на заказы, содержащие не менее 10 различных позиций, предоставляется глобальная скидка 7 %.\n",
- "- к каждому заказу может быть применена только одна из данных скидок - скидка, дающая наибольший бонус.\n",
- "\n",
- "Также отдел аналитики нашего Интернет-магазина хочет собирать статистику по популярности использования каждой стратегии начисления скидки.\n",
- "\n",
- "Наша задача, используя структы ниже, реализовать механизм расчета оптимальной скидки для каждого заказа и сбора статистика для отдела аналитики."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Структуры для выполнения**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Optional, Callable\n",
- "from dataclasses import dataclass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "@dataclass\n",
- "class Item:\n",
- " label: str\n",
- " price: float\n",
- " amount: int"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "@dataclass\n",
- "class Customer:\n",
- " customer_id: int\n",
- " username: str\n",
- " loyalty_points: int"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Order:\n",
- " customer: Customer\n",
- " order: list[Item]\n",
- " price: float\n",
- " promotion: Optional[Callable] = None\n",
- "\n",
- " def __init__(self, \n",
- " customer: Customer,\n",
- " order: list[Item],\n",
- " promotion: Optional[Callable] = None\n",
- " ) -> None:\n",
- " self.customer = customer\n",
- " self.order = list(order)\n",
- " self.promotion = promotion\n",
- " self.price = self._compute_price()\n",
- "\n",
- " def _compute_price(self) -> float:\n",
- " price = sum(item.price * item.amount for item in self.order)\n",
- "\n",
- " if self.promotion:\n",
- " price -= self.promotion(self.order)\n",
- "\n",
- " return price"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson7/sem7_312/task1.py b/lessons/lesson7/sem7_312/task1.py
deleted file mode 100644
index 58075da4..00000000
--- a/lessons/lesson7/sem7_312/task1.py
+++ /dev/null
@@ -1,37 +0,0 @@
-""" Дана функция и список целых чисел.
-
- Написать функцию, которая:
- 1) Считает значение функции для каждого элемента из списка
- 2) Возвращает результат работы одной из следующих оперций над полученными значениями функции:
- - Сумму всех элементов ("sum")
- - Максимальное значение ("max")
- - Минимальное значение ("min")
- - Среднее значение ("mean")
- - Медианное значение ("med")
-
- Если при вызове функции не указана операция, то она должна возвращать произведение всех элементов.
-"""
-
-from math import prod
-from statistics import median
-
-def super_reductor(*args):
- """ Примение частной функции к каждому элементу списка
- и применение групповой операции к результатам.
-
- Вход:
- func : function
- Функция, которую нужно применить ко всем элементам списка
- arr : List[int]
- Обрабатываемый список целых чисел
- op : str, otional
- Название групповой оперции, которую надо применить.
- Может принимать значения из списка: ['multi', 'sum', 'max', 'min', 'mean', 'med']
- Значение по умолчанию: 'multi'
-
- Выход:
- result : float
- Результат групповой операции над списком результатов работы
- частной функции от каждого элемента исходного списка
- """
- pass
diff --git a/lessons/lesson7/sem7_312/task2.py b/lessons/lesson7/sem7_312/task2.py
deleted file mode 100644
index 1bddd655..00000000
--- a/lessons/lesson7/sem7_312/task2.py
+++ /dev/null
@@ -1,24 +0,0 @@
-""" На вход подается массив целых чисел arr.
-
- Создайте массив arr2 на основе arr, возведя все чётные числа в квадрат,
- а нечётные - в куб и сохранив порядокю
-
- Затем, на основе arr2 создайте два массива arr3 и arr4 из чисел,
- которые нацело делятся на 3 и 4 соответственно, и верните их
-
-"""
-
-def task2(arr):
- """ Обработка массива.
-
- Вход:
- arr : List[int]
- Обрабатываемый массив целых чисел
-
- Выход:
- arr3 : List[int]
- Массив чисел из arr2, делящихся на 3
- arr4 : List[int]
- Массив чисел из arr2, делящихся на 4
- """
- pass
diff --git a/lessons/lesson7/sem7_312/task3.py b/lessons/lesson7/sem7_312/task3.py
deleted file mode 100644
index 17557cc1..00000000
--- a/lessons/lesson7/sem7_312/task3.py
+++ /dev/null
@@ -1,24 +0,0 @@
-""" Даны две функции:
-
- 1) F(x, y, z) = {
- G(x - 2, y // 2) + G(y - 1, z - 2), if (x >= 2, y >= 1, z >= 2)
- x + y + z , else
- }
-
- 2) G(x, y) = {
- F(x // 3, y - 1, |x - y|), if (x >= 0, y >= 1)
- 1 , else
- }
-
- По трём входным числам x, y, z посчитать знаение F(x, y, z)
-"""
-
-
-
-# assert F(1, 1, 1) == 3
-# assert F(2, 1, 2) == 2
-# assert F(4, 4, 4) == 4
-# assert F(15, 60, 23) == 122
-# assert F(652, 681, 314) == 9053
-
-
diff --git a/lessons/lesson7/sem7_312/task4.py b/lessons/lesson7/sem7_312/task4.py
deleted file mode 100644
index 545982ca..00000000
--- a/lessons/lesson7/sem7_312/task4.py
+++ /dev/null
@@ -1,52 +0,0 @@
-
-""" Женя работает в фирме, которая разробатывает приложение для создание заметок.
- Ему дали задание написать 2 функции:
- 1) Функция, которая добавляет запись в заметку по ее названию,
- если заметка уже существует, или создает новую заметку.
-
- 2) Функция, которая выводит все записи из конкретной заметки,
- если название заметки не указывается, то выводятся записи
- из главной заметки под названием "home" (она всегда существует).
-
- Но есть один ньюнас:
- записи в заметках разделены на предложение (и хранятся в базе данных как список предложеий),
- поэтому на вход в функцию Жени подается имя заметки и n предложений из потока ввода.
- Значение n всегда разное, Женя не знает сколько предложений ему прийдет.
-
- Помогите Жене реализовать эти функции!
-
- Сценарий использоавния функций:
-
- add_note("name", "sentence1", "sentence2", "sentence3", ...)
-
- get_note("name")
- get_note()
-
-"""
-
-notes = {'home' : []}
-
-
-
-
-
-
-
-# add_note('home', "wash dishes", "cook dinner")
-# add_note('store', 'bread', 'butter', 'milk')
-# add_note('movies', 'Barbie', 'Oppenheimer', 'Dark Knight')
-
-# get_note() # wash dishes cook dinner
-# get_note('home') # wash dishes cook dinner
-# get_note('store') # bread butter milk
-# get_note('movies') # Barbie Oppenheimer Dark Knight
-
-
-# add_note('home', "clean window")
-# add_note('store', 'icecream')
-# add_note('movies', 'Back To The Future')
-
-# get_note() # wash dishes cook dinner clean window
-# get_note('home') # wash dishes cook dinner clean window
-# get_note('store') # bread butter milk icecream
-# get_note('movies') # Barbie Oppenheimer Dark Knight Back To The Future
diff --git a/lessons/lesson7/sem7_312/task5.py b/lessons/lesson7/sem7_312/task5.py
deleted file mode 100644
index 5fcdc06f..00000000
--- a/lessons/lesson7/sem7_312/task5.py
+++ /dev/null
@@ -1,28 +0,0 @@
-""" Напишите алгоритм возведения числа в степень,
- работающий за время O(sqrt(n)), где n - степень
-"""
-
-def pow_n(x, n):
- """ Быстрое возведение числа в степень.
-
- Вход:
- x : float
- Число, возводимое в степень
- n : int
- Степень, в которую надо возвести число x
-
- Выход:
- result : float
- Результат вычисления x^n
- """
- pass
-
-
-# assert pow_n(1, 2) == 1
-# assert pow_n(0, 2) == 0
-# assert pow_n(0, 0) == 1
-# assert pow_n(2, 10) == 1024
-# assert pow_n(12, 5) == 248832
-# assert pow_n(3, 12) == 531441
-# assert pow_n(7, 0) == 1
-# assert pow_n(7, 1) == 7
\ No newline at end of file
diff --git a/lessons/lesson7/sem7_313/strategy.png b/lessons/lesson7/sem7_313/strategy.png
deleted file mode 100644
index 627879d8..00000000
Binary files a/lessons/lesson7/sem7_313/strategy.png and /dev/null differ
diff --git a/lessons/lesson7/sem7_313/task.ipynb b/lessons/lesson7/sem7_313/task.ipynb
deleted file mode 100644
index fd0c6623..00000000
--- a/lessons/lesson7/sem7_313/task.ipynb
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Паттерн стратегия через полноправные функции\n",
- "\n",
- "## Описание паттерна\n",
- "\n",
- "В книге «Паттерны проектирования» паттерн Стратегия описывается следующим образом:\n",
- "\n",
- " Определить семейство алгоритмов, инкапсулировать каждый из\n",
- " них и сделать их взаимозаменяемыми. Стратегия позволяет заменять\n",
- " алгоритм независимо от использующих его клиентов.\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как следует из приведенной UML-даграммы, для реализации патерна авторы предлагают использовать все блага ООП, в частности, наследование, абстрактные классы, полиморфизм и пр. Однако из-за наличия в Python полноправных функций, этот паттерн может быть реализован и без использования столь сложных концепций. В этом на и предстоит убедиться."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Задача\n",
- "\n",
- "Рассмотрим Интернет-магазин со следующими правилами формирования скидок:\n",
- "- заказчику, имеющему не менее 1000 баллов лояльности, предоставляется глобальная скидка 5 % на весь заказ;\n",
- "- на позиции, заказанные в количестве не менее 20 единиц в одном заказе, предоставляется скидка 10 %;\n",
- "- на заказы, содержащие не менее 10 различных позиций, предоставляется глобальная скидка 7 %.\n",
- "- к каждому заказу может быть применена только одна из данных скидок - скидка, дающая наибольший бонус.\n",
- "\n",
- "Также отдел аналитики нашего Интернет-магазина хочет собирать статистику по популярности использования каждой стратегии начисления скидки.\n",
- "\n",
- "Наша задача, используя структы ниже, реализовать механизм расчета оптимальной скидки для каждого заказа и сбора статистика для отдела аналитики."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**Структуры для выполнения**:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Optional, Callable\n",
- "from dataclasses import dataclass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "@dataclass\n",
- "class Item:\n",
- " label: str\n",
- " price: float\n",
- " amount: int"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "@dataclass\n",
- "class Customer:\n",
- " customer_id: int\n",
- " username: str\n",
- " loyalty_points: int"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Order:\n",
- " customer: Customer\n",
- " order: list[Item]\n",
- " price: float\n",
- " promotion: Optional[Callable] = None\n",
- "\n",
- " def __init__(self, \n",
- " customer: Customer,\n",
- " order: list[Item],\n",
- " promotion: Optional[Callable] = None\n",
- " ) -> None:\n",
- " self.customer = customer\n",
- " self.order = list(order)\n",
- " self.promotion = promotion\n",
- " self.price = self._compute_price()\n",
- "\n",
- " def _compute_price(self) -> float:\n",
- " price = sum(item.price * item.amount for item in self.order)\n",
- "\n",
- " if self.promotion:\n",
- " price -= self.promotion(self.order)\n",
- "\n",
- " return price"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson7/sem7_314/tasks.ipynb b/lessons/lesson7/sem7_314/tasks.ipynb
deleted file mode 100644
index 13318b40..00000000
--- a/lessons/lesson7/sem7_314/tasks.ipynb
+++ /dev/null
@@ -1,198 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "72f00248-6435-4379-80f8-33ad70d7c22b",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 1"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "70d69579-127f-4cd3-81e6-9d133ac486d5",
- "metadata": {},
- "source": [
- "Дана функция и список целочисленных переменных.\n",
- "\n",
- "Написать функцию, которая считает значение функции для каждого элемента из списка. И возвращает результат работы одной из следующих оперций над полученными значениями функции:\n",
- "- Сумму всех элементов (\"sum\")\n",
- "- Максимальное значение (\"max\")\n",
- "- Минимальное значение (\"min\")\n",
- "- Среднее значение (\"mean\")\n",
- "- Медианное значение (\"med\")\n",
- "\n",
- "Если при вызове функции не указана операция, то она должна возвращать произведение всех элементов."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "id": "f4cff83c-95a3-410e-bd38-851c2a7fd579",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "## Your code"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "430cb0bd-59c6-42fa-abc2-f9e81bf35e4e",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "90822b0b-75e5-4fe2-a948-9716ddb8898e",
- "metadata": {},
- "source": [
- "На вход подается массив, при помощи функции map все четный числа возведите в квадрат, а нечетные в куб.\n",
- "\n",
- "После при помощи функции filter выделите из предыдущего массива 2 подмассива, которые делятся на 3 и на 4 без остатка соответсвенно."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "a81a5804-5d32-4524-bcfa-02c03182015f",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "## Your code"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ad0c57c1-30e5-4c32-9eb6-27f9e20bd646",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 3"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bd43e64b-a825-4f81-aaec-4926fc79fc1c",
- "metadata": {},
- "source": [
- "Даны функции:\n",
- "\n",
- "$F(x, y, z) = G(x - 2, y // 2) + G(y - 1, z - 2)$ при $x \\geq 2, y \\geq 1, z \\geq 2$, иначе $F(x, y, z) = x + y + z$ \n",
- "\n",
- "$G(x, y) = F(x // 3, y - 1, |x - y|)$ при $x \\geq 0, y \\geq 1$, иначе $G(x, y) = 1$\n",
- "\n",
- "По трём входным числам $x, y, z$ посчитать знаение $F(x, y, z)$"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "id": "30d7198c-62e8-4485-b929-0af650088dfc",
- "metadata": {},
- "outputs": [],
- "source": [
- "## Your code"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9116c66d-85ea-4aaa-b114-5b7b000bd5dc",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 4"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "edfb0dd0-c0d9-4060-aaf8-8e95ac63d384",
- "metadata": {},
- "source": [
- "Женя работает в фирме, которая разробатывает приложение для создание заметок. Ему дали задание написать 2 функции:\n",
- "- Функция которая добавляет запись в заметку по ее названию, если заметка уже существует, или создает новую заметку.\n",
- "- Функция которая выводить все записи из конкретной заметки, если название заметки не указывается, то выводятся записи из главной заметки под названием \"home\" (она всегда существует).\n",
- "\n",
- "Но есть один ньюнас, записи в заметках разделены на предложение (и хранятся в базе данных как список предложеий), поэтому на вход в функцию Жени подается имя заметки и n предложений из потока ввода. Значение n всегда разное, Женя не знает сколько предложений ему прийдет. Помогите Жене реализовать эти функции.\n",
- "\n",
- "Формат ввода:\n",
- "\n",
- "add_note(\"name\", \"sentence1\", \"sentence2\", \"sentence3\", ...)\n",
- "\n",
- "get_note(\"name\") / get_note()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "1558d3ff-0dc5-4e9a-a7aa-8cb58902859a",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "## Your code"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f08e284d-c2c6-4144-984c-d7168d64681b",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Задача 5"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3da51bba-c4df-4c93-bc22-7a175e7dca20",
- "metadata": {},
- "source": [
- "Возвести число x в степень n за $O(\\sqrt n)$"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "150b4779-02be-4012-9bd3-c12d0bd171cf",
- "metadata": {},
- "outputs": [],
- "source": [
- "## Your code"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson8/interactive_conspect.ipynb b/lessons/lesson8/interactive_conspect.ipynb
deleted file mode 100644
index 42082c7d..00000000
--- a/lessons/lesson8/interactive_conspect.ipynb
+++ /dev/null
@@ -1,1200 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Декораторы\n",
- "\n",
- "В данной лекции речь пойдет о декораторах, о цели их использования и способах создания. Однако, прежде, чем начать этот разговор, нам необходимо вспомнить материал [прошлой лекции](../lesson7/interactive_conspect.ipynb), посвященный облостям видимости вложенным функциям.\n",
- "\n",
- "## Области видимости: повторение\n",
- "\n",
- "Начнем детальное знакомство с замыканиям с небольшого уточнения информации об областях видимости. На прошлой лекции мы разобрались, что у любой функции есть свое пространство имен, которое можно представить, как перечень переменных, используемых в теле функции - локальных переменных. Пространство имен создается при каждом вызове функции и уничтожается в момент завершения выполнения функции. Переменные, которые определены на уровне модуля, являются глобальными переменнами по отношению к данной функции. При этом в теле функции можно получить доступ к глобальным переменным:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "var_global = 5\n",
- "\n",
- "def print_vars(var_local: int) -> None:\n",
- " print(f'{var_local = };')\n",
- " print(f'{var_global = };')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "var_local = 10;\n",
- "var_global = 5;\n"
- ]
- }
- ],
- "source": [
- "print_vars(10)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Если глобальная переменная является объектом изменяемого типа данных, действия в теле функции могут привести к изменению значения данной глобальной переменной. Однако стоит понимать, что нового объекта создано не будет. Вы не можете осуществить перепревязку глобальной переменной в теле функции. Подобные попытки приведут лишь к созданию локальной переменной, с тем же идентефикатором, что и глобальная переменная, в теле функции. Локальная переменная будет связана с новым объектом в памяти, время жизни которого ограничено телом функции. Данная локальная переменная в теле функции будет перекрывать своим именем имя глобальной переменной. При этом глобальная переменная никак не изменится. Эти положения иллюстрируются следующим примером:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "list_global = [1, 2]\n",
- "\n",
- "\n",
- "def print_list_info(list_value: list, list_name: str) -> None:\n",
- " print(\n",
- " f'{list_name} value: {list_value};',\n",
- " f'{list_name} id: {id(list_value)};',\n",
- " sep='\\n',\n",
- " end='\\n\\n',\n",
- " )\n",
- "\n",
- "\n",
- "def change_list() -> None:\n",
- " list_global = list(range(10))\n",
- " print_list_info(list_global, 'list_global')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "list_global value: [1, 2];\n",
- "list_global id: 140667361610688;\n",
- "\n",
- "list_global value: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\n",
- "list_global id: 140667119931264;\n",
- "\n",
- "list_global value: [1, 2];\n",
- "list_global id: 140667361610688;\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print_list_info(list_global, 'list_global')\n",
- "change_list()\n",
- "print_list_info(list_global, 'list_global')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Более того, Python не определяет динамически, является ли переменная локальной или нет. Данная информация определяется в момент определения функции и остается неизменной на протяжении всей жизни программы. Отсюда следует, что любая переменная в теле функции является или глобальной, или локальной, она не может быть локальной в одних случая, а глобальной - в других, или быть до определенного момента глобальной, а потом локальной. Переменная считается локальной, если является операндом оператора присваивания в любой форме (как простой, так и составной). Данное положение можно проиллюстрировать следующим примером:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "some_number = 9\n",
- "\n",
- "\n",
- "def func1(num: int) -> None:\n",
- " print(f'{num = };')\n",
- " print(f'{some_number = };')\n",
- " print('')\n",
- "\n",
- "\n",
- "def func2(num: int) -> None:\n",
- " some_number = 6\n",
- " print(f'{num = };')\n",
- " print(f'{some_number = };')\n",
- " print('')\n",
- "\n",
- "\n",
- "def func3(num: int) -> None:\n",
- " print(f'{num = };')\n",
- " print(f'{some_number = };')\n",
- " some_number = 6\n",
- " print('')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "num = 3;\n",
- "some_number = 9;\n",
- "\n",
- "num = 3;\n",
- "some_number = 6;\n",
- "\n",
- "num = 3;\n"
- ]
- },
- {
- "ename": "UnboundLocalError",
- "evalue": "local variable 'some_number' referenced before assignment",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6252/2469376657.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mfunc3\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m/tmp/ipykernel_6252/3164014442.py\u001b[0m in \u001b[0;36mfunc3\u001b[0;34m(num)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfunc3\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'{num = };'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'{some_number = };'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0msome_number\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'some_number' referenced before assignment"
- ]
- }
- ],
- "source": [
- "func1(3)\n",
- "func2(3)\n",
- "func3(3)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как мы видим любая попытка перепривязки или составного присваивания в теле функции делает переменную локальной. Чтобы избежать подобного поведения и получить возможность изменять глобальные переменные в теле функции, существует специальное слово **global**, явно сообщающее интерпретатору, что переменная является глобальной и поиска данного имени должен осуществляться на уровне модуля. Исправим функцию из примера выше:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "def func3(num: int) -> None:\n",
- " global some_number\n",
- "\n",
- " print(f'{num = };')\n",
- " print(f'{some_number = };')\n",
- " some_number = 6\n",
- " print('')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "num = 3;\n",
- "some_number = 9;\n",
- "\n",
- "some_number = 6;\n"
- ]
- }
- ],
- "source": [
- "func3(3)\n",
- "\n",
- "print(f'{some_number = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Аналогичная ситуация складывается и с вложенными функциями - функциями, определенными в телах других функций. Вложенная функция может иметь доступ к переменным, находящимся в области видимости внешней функции, т.е. функции, в которую данная функция вложена. Однако любые попытки перепривязки приводят к тому, что внешние переменная воспринимаются интерпретатором как локальные. Данное положение можно проиллюстрировать следующим примером:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Callable\n",
- "\n",
- "\n",
- "def outer_func1(num: int) -> Callable:\n",
- " outer_num = num\n",
- "\n",
- " def inner_func(num: int) -> None:\n",
- " print(f'inner {num = };')\n",
- " print(f'{outer_num = };')\n",
- " print('')\n",
- "\n",
- " print(f'{outer_num = };', end='\\n\\n')\n",
- "\n",
- " return inner_func\n",
- "\n",
- "\n",
- "def outer_func2(num: int) -> Callable:\n",
- " outer_num = num\n",
- "\n",
- " def inner_func(num: int) -> None:\n",
- " print(f'inner {num = };')\n",
- " print(f'{outer_num = };')\n",
- " outer_num = 5\n",
- " print('')\n",
- "\n",
- " print(f'{outer_num = };', end='\\n\\n')\n",
- "\n",
- " return inner_func"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "outer_num = 10;\n",
- "\n",
- "outer_num = 10;\n",
- "\n",
- "inner num = 3;\n",
- "outer_num = 10;\n",
- "\n",
- "inner num = 3;\n"
- ]
- },
- {
- "ename": "UnboundLocalError",
- "evalue": "local variable 'outer_num' referenced before assignment",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m/tmp/ipykernel_6252/1330043198.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0minner_func1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0minner_func2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m/tmp/ipykernel_6252/2971057007.py\u001b[0m in \u001b[0;36minner_func\u001b[0;34m(num)\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minner_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'inner {num = };'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'{outer_num = };'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 23\u001b[0m \u001b[0mouter_num\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'outer_num' referenced before assignment"
- ]
- }
- ],
- "source": [
- "inner_func1 = outer_func1(10)\n",
- "inner_func2 = outer_func2(10)\n",
- "\n",
- "inner_func1(3)\n",
- "inner_func2(3)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Для решения данной проблемы, по аналогии с global, было введено специальное слово **nonlocal**, которое явно сообщает интепретатору, что используемое имя содержится в одной из внешних функций. В отличие от global, поиск нужного имени осуществляется последовательно во всех функциях, в которые была вложена данная функция, но не на уровне модуля. Имя это ввиду исправим функции из предыдущего примера:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "def outer_func2(num: int) -> Callable:\n",
- " outer_num = num\n",
- "\n",
- " def inner_func(num: int) -> None:\n",
- " nonlocal outer_num\n",
- "\n",
- " print(f'inner {num = };')\n",
- " print(f'{outer_num = };')\n",
- " outer_num = 5\n",
- " print('')\n",
- "\n",
- " print(f'{outer_num = };', end='\\n\\n')\n",
- "\n",
- " return inner_func"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "outer_num = 10;\n",
- "\n",
- "inner num = 3;\n",
- "outer_num = 10;\n",
- "\n"
- ]
- }
- ],
- "source": [
- "inner_func2 = outer_func2(10)\n",
- "inner_func2(3)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Замыкания\n",
- "\n",
- "Все вложенные функции из примеров выше, использовавшие нелокальные переменная, являются замыканиями. Формально замыкание можно определить следующим образом: замыкание - это функция с расширенной областью видимости, которая охватывает все неглобальные переменные, на которые есть ссылки в теле функции, хотя они в нем не определены. Т.е. замыкания - это такие функции, которые могут обращаться к неглобальными переменным, определенным вне их тела. \n",
- "\n",
- "Давайте рассмотрим более содержательный пример использования замыкания. Допустим мы хотим создать функцию счетчик, которая будет хранить в себе актуальную информацию о количестве вызовов, обновлять и возвращать ее при каждом новом вызове. Сама по себе эта функция несет мало пользы, поэтому допустим что она является частью большой и важной системы, а вызывающая сторона использует эту информацию для учета каких-то статистик. Как бы могла выглядеть эта функция-счетчик?\n",
- "\n",
- "Мы не можем поместить переменную, хранящую информацию о количестве вызовов в тело нашей функции-счетчика, поскольку пространство имен каждый раз создается заново, а значит не сохраняет никакой информации о предыдущих вызовах. Первое, что приходит в голову - сделать переменную-счетчик глобальной:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "counter = 0\n",
- "\n",
- "\n",
- "def count() -> int:\n",
- " global counter\n",
- " \n",
- " counter += 1\n",
- " return counter"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "7"
- ]
- },
- "execution_count": 34,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "count()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Отлично! Это работает! Но что если мы захотим создать два независимых счетчика, которые бы могли подсчитывать вызовы и хранить информацию независимо друг от друга?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "counter1 = count\n",
- "counter2 = count"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "8\n",
- "9\n"
- ]
- }
- ],
- "source": [
- "print(counter1())\n",
- "print(counter2())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В этом случае мы помним предыдущие состояния функции. Более того, каждый вызов \"независимого счетчика\" влияет на состояние остальных счетчиков. Т.е. использование глобальных переменных нашу проблему не решает. В такие моменты замыкания и становятся достаточно полезными интсрументами:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Callable\n",
- "\n",
- "\n",
- "def make_counter() -> Callable[[], int]:\n",
- " counter = 0\n",
- "\n",
- " def count() -> int:\n",
- " nonlocal counter\n",
- " counter += 1\n",
- "\n",
- " return counter\n",
- " \n",
- " return count"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "counter1: 4;\n",
- "counter1: 6;\n"
- ]
- }
- ],
- "source": [
- "counter1 = make_counter()\n",
- "counter2 = make_counter()\n",
- "\n",
- "for i in range(3):\n",
- " counter1()\n",
- "\n",
- "for i in range(5):\n",
- " counter2()\n",
- "\n",
- "print(f'counter1: {counter1()};')\n",
- "print(f'counter1: {counter2()};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Теперь создание функции счетчика происходит в момент выполнения программы посредством вызова функции `make_counter()`, в теле которой объявляется переменная `counter` и функция `count()`. Функция `count()` является замыканием, поскольку она вложена в функцию make_counter() и использует в своем теле внешние неглобальные переменные - counter, - для хранения и обновления информации о количестве вызовов. Функции подобные `make_counter()` называются \"фабриками\", посколько позволяют \"производить\" некоторые объекты по ходу выполнения программы - в нашем случае, функции-счетчики. \n",
- "\n",
- "Данный подход полностью решает поставленную проблему: мы можем создавать функции-счетчики, которые позволяют получать информацию о текущем количестве вызовов, при необходимости мы можем создать сколько угодно независимых счетчиков. Однако данный подход также должен вызвать у вас ряд вопросов в числе которых: а где же хранится информация о количестве вызовов? Ведь переменная `counter` определена в теле функции make_counter, а значит является локальной переменной, т.к. должна быть уничтожена после выполнения функции. Но почему-то этого не происходит, и мы по-прежнему имеем к ней доступ через замыкание.\n",
- "\n",
- "Это происходит потому что внутри функции `count` переменная `counter` является свободной переменной, т.е. несвязанной локальной областью видимости. Мы можем наглядно в этом убедиться, произведя инспекцию нашей функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "local variables: ()\n",
- "free variables: ('counter',)\n"
- ]
- }
- ],
- "source": [
- "print(f'local variables: {counter1.__code__.co_varnames}')\n",
- "print(f'free variables: {counter1.__code__.co_freevars}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Python хранит откомпилировнаное тело функции в атрибуте `__code__`. Также в этом атрибуте хранится информация о локальных и свободных переменных. Как мы видим в примере выше переменная `counter` действительно является свободной переменной. Однако таким образом python сохраняет именно имена переменных, но где же хранится само значение?\n",
- "\n",
- "Привязка переменной counter сохраняется в специальном атрибуте `__closure__`(буквально - замыкание). Между элементами `__closure__` и именами, хранящимися в `__code__.co_freevars` существует взаимооднозначное соответствие. Элементами атрибута `__closure__` являются специальные ячейки (cells). У каждой ячейки есть атрибут `cell_content` - содержимое ячейки, именно в этом атрибуте и хранится значение свободной переменной. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "counter1 closure: (,)\n",
- "counter1 cell0 content: 4;\n"
- ]
- }
- ],
- "source": [
- "print(f'counter1 closure: {counter1.__closure__}')\n",
- "\n",
- "for i, cell in enumerate(counter1.__closure__):\n",
- " print(f'counter1 cell{i} content: {cell.cell_contents};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Таким образом замыкания хранят значения свободных переменных в специальном кортеже и специальным образом. Именно поэтому мы можем продолжать использовать переменные внешних функций за пределами тела этих внешних по отношению к замыканию функций.\n",
- "\n",
- "Резюмируем: замыкание – это функция, которая запоминает привязки свободных переменных, существовавших на момент определения функции, так что их можно использовать впоследствии при вызове функции, когда область видимости, в которой она была определена, уже не существует."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Декораторы\n",
- "\n",
- "Разобравшись с замыканием мы готовы к изучению декораторов. Декораторы - это вызываемый объект, который принимает другую функцию - декорируемую функцию, в качестве аргумента. Обычно декораторы используются, чтобы дополнить фнкционал уже существующих функций некоторым образом, в этом случае в результате применения декоратора исходная функция заменяется на результат вычисления декоратора. Также декораторы могу использоваться, чтобы осуществить некоторые дополнительные манипуляции с функцией по определенному общему сценарию.\n",
- "\n",
- "Рассмотрим следующий учебный пример:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "def my_decorator(func):\n",
- " def wrapper(*args, **kwargs):\n",
- " print('start function')\n",
- " result = func(*args, **kwargs)\n",
- " print('finished function')\n",
- "\n",
- " return result\n",
- " \n",
- " return wrapper\n",
- "\n",
- "\n",
- "@my_decorator\n",
- "def do_something():\n",
- " print('do_something')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "start function\n",
- "do_something\n",
- "finished function\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В данном примере мы реализовали просто декоратор - my_decorator. Данный декоратор принимает на вход функцию. В своем теле он определяет замыкание, которое использует данную функцию и выводит в стандартный поток вывода сообщения о начале вычислений и об их завершении. Также в замыкании происходит вызов декорируемой функции и возвращение результатов ее вычисления, что является распространненой практикой, поскольку декораторы именно дополняеют поведение функции, а не переопределяют его. Сам декоратор возвращает замыкание в качестве результата вычисления.\n",
- "\n",
- "Вызов декоратора, позволяющий применить его к нашей функции, является всего лишь синтаксической глазурью:\n",
- "\n",
- "```python\n",
- "@my_decorator\n",
- "def do_something():\n",
- " ...\n",
- "```\n",
- "\n",
- "Формально, ничего не мешает нам применить декоратор следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "def do_something():\n",
- " print('do_something')\n",
- "\n",
- "do_something = my_decorator(do_something)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "start function\n",
- "do_something\n",
- "finished function\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Поскольку код применения декораторов из примеров выше эквивалентен, можно сделать следующий вывод: объект, возвращаемый декоратором заменяет собой декорируемую функцию. Это можно проверить, обратившись к имени функции после декорировнаия:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'wrapper'"
- ]
- },
- "execution_count": 54,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "do_something.__name__"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Т.е. функция `wrapper`, определенная в теле декоратора, действительно заменила собой исходный объект. Именно поэтому мы и определили замыкание с настолько общей сигнатурой. Ведь функция wrapper будет заменять собой любую декорируемую функцию, а значит должна обеспечить корректную работу функций с любыми сигнатурами. \n",
- "\n",
- "Из факта подобной замены следует, что затирается и сигнатура первоначально функции, а значит IDE не сможет предоставлять нам интеллектуальные подсказки о декорируемой функции, типах ее аргументов и возвращаемом значении. Чтобы это предотвратить, в стандартной библиотеки был реализован декоратор `wraps`, который позволяет сохранить информацию о декорированной функции.\n",
- "\n",
- "Чтобы посмотреть на использования описанного декоратора, давайте реализуем декоратор, распечатывающий информации о вызове функции: имя функции, с какими аргументами был совершен вызов, за какое время была выполнена фукнция."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [],
- "source": [
- "import time\n",
- "\n",
- "from typing import Callable\n",
- "from functools import wraps\n",
- "\n",
- "\n",
- "def call_info(func) -> Callable:\n",
- " @wraps(func)\n",
- " def get_call_info(*args, **kwargs) -> Callable:\n",
- " args_list = []\n",
- "\n",
- " if args:\n",
- " args_list.append(', '.join(str(arg) for arg in args))\n",
- "\n",
- " if kwargs:\n",
- " args_list.append(\n",
- " ', '.join(f'{key}={val}' for key, val in kwargs.items())\n",
- " )\n",
- "\n",
- " args_str = ', '.join(args_list)\n",
- "\n",
- " time_start = time.time()\n",
- " result = func(*args, **kwargs)\n",
- "\n",
- " print(\n",
- " f'[CALL INFO]: {func.__name__}({args_str}) -> {result} '\n",
- " f'|| {time.time() - time_start}'\n",
- " )\n",
- "\n",
- " return result\n",
- "\n",
- " return get_call_info"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [],
- "source": [
- "from time import sleep\n",
- "\n",
- "\n",
- "@call_info\n",
- "def do_something() -> None:\n",
- " sleep(1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[CALL INFO]: do_something() -> None || 1.0010740756988525\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'do_something'"
- ]
- },
- "execution_count": 51,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "do_something.__name__"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Момент выполнения декораторов\n",
- "\n",
- "Декораторы выполняются в момент определения функций. Обычно, когда программа состоит из нескольких модулей (разбита на несколько файлов с расширением .py), вы импортируете декорированные функции из других модулей. В данной ситуации декоратор выполняется в момент импорта. Важно осознавать, что декоратор будет выполняться отдельно для каждой декорируемой функции. В качестве демонстрации этого принципа приведен следующий пример:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 52,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "run decorate\n",
- "run decorate\n"
- ]
- }
- ],
- "source": [
- "def decorate(func):\n",
- " print('run decorate')\n",
- " return func\n",
- "\n",
- "\n",
- "@decorate\n",
- "def do_something() -> None:\n",
- " print('do_something')\n",
- "\n",
- "\n",
- "@decorate\n",
- "def do_another_thing() -> None:\n",
- " print('do_another_thing')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "do_something\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "do_another_thing\n"
- ]
- }
- ],
- "source": [
- "do_another_thing()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как вы видите сообщение о запуске декоратора было распечатано дважды в момент определения функции. Сами же функции выполняются только посредством явного вызова в коде. Этот пример интересен тем, что возвращаемым объектом декоратора служит исходная функция, а не замыкание, определяемое в теле. В контексте пример выше это кажется упрощением, но подобная техника может оказаться очень полезной, например для регистрации функций в общем реестре функций. Более практически полезный пример будет разберан на семинаре."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Композиции декораторов\n",
- "\n",
- "При необходимости вы можете применять несколько декораторов к функции одновременно. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [],
- "source": [
- "def outer(func):\n",
- " def wrapper(*args, **kwargs):\n",
- " print('outer')\n",
- " \n",
- " result = func(*args, **kwargs)\n",
- " return result\n",
- " \n",
- " return wrapper\n",
- "\n",
- "\n",
- "def inner(func):\n",
- " def wrapper(*args, **kwargs):\n",
- " print('inner')\n",
- "\n",
- " result = func(*args, **kwargs)\n",
- " return result\n",
- " \n",
- " return wrapper\n",
- "\n",
- "\n",
- "@inner\n",
- "@outer\n",
- "def do_something() -> None:\n",
- " print('do_something')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "inner\n",
- "outer\n",
- "do_something\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Порядок применения декораторов в данном примере хорошо описывается следующим эквивалентным кодом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [],
- "source": [
- "def do_something() -> None:\n",
- " print('do_something')\n",
- "\n",
- "do_something = outer(inner(do_something))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "outer\n",
- "inner\n",
- "do_something\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Параметризованные декораторы\n",
- "\n",
- "В некоторых ситуациях бывает полезно иметь возможность настраивать декоратор и передавать в него какие-либо значения помимо декорируемой функции. На этот случай все, что нам нужно сделать - реализовать параметризованный декоратор. Формально, параметризованный декоратор - не декоратор, а фабрика декораторов. Определение данного объекта выглядит следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {},
- "outputs": [],
- "source": [
- "import time\n",
- "\n",
- "from typing import Callable\n",
- "from functools import wraps\n",
- "\n",
- "\n",
- "def call_info(\n",
- " use_arguments: bool = True,\n",
- " fix_time: bool = True,\n",
- ") -> Callable:\n",
- " def call_deco(func) -> Callable:\n",
- " @wraps(func)\n",
- " def get_call_info(*args, **kwargs) -> Callable:\n",
- " args_list = []\n",
- "\n",
- " if args and use_arguments:\n",
- " args_list.append(', '.join(str(arg) for arg in args))\n",
- "\n",
- " if kwargs and use_arguments:\n",
- " args_list.append(\n",
- " ', '.join(f'{key}={val}' for key, val in kwargs.items())\n",
- " )\n",
- "\n",
- " args_str = ', '.join(args_list)\n",
- "\n",
- " time_start = time.time()\n",
- " result = func(*args, **kwargs)\n",
- "\n",
- " dots = '...'\n",
- "\n",
- " print(\n",
- " f'[CALL INFO]: {func.__name__}'\n",
- " f'({args_str if use_arguments else dots}) -> {result}'\n",
- " f' || {time.time() - time_start if fix_time else dots}'\n",
- " )\n",
- "\n",
- " return result\n",
- "\n",
- " return get_call_info\n",
- " \n",
- " return call_deco"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {},
- "outputs": [],
- "source": [
- "from random import randint\n",
- "from time import sleep\n",
- "\n",
- "\n",
- "@call_info()\n",
- "def sum_numbers(*args) -> float:\n",
- " return sum(args)\n",
- "\n",
- "\n",
- "@call_info(fix_time=False)\n",
- "def invert_numbers(*args) -> list[float]:\n",
- " return [\n",
- " 1 / i if i != 0 else i for i in args\n",
- " ]\n",
- "\n",
- "\n",
- "@call_info(use_arguments=False)\n",
- "def do_something() -> None:\n",
- " time_to_sleep = randint(1, 5)\n",
- " sleep(time_to_sleep)\n",
- "\n",
- " print('did something')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[CALL INFO]: sum_numbers(1, 2, 3, 4, 5) -> 15 || 6.4373016357421875e-06\n",
- "[CALL INFO]: invert_numbers(1, 2, 4, 0) -> [1.0, 0.5, 0.25, 0] || ...\n",
- "did something\n",
- "[CALL INFO]: do_something(...) -> None || 3.015892267227173\n"
- ]
- }
- ],
- "source": [
- "sum_numbers(1, 2, 3, 4, 5)\n",
- "invert_numbers(1, 2, 4, 0)\n",
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В данных примерах использование декоратора выглядит как функциональный вызов. Это происходит потому что call_info не является декоратором сама по себе. Как было сказано ранее call_info - это фабрика декораторов, т.е. это функция, позволяющая создать декораторы в процессе выполнения программы. Созданные декоратор применяется к декорируемой функции.\n",
- "\n",
- "Менее употребимый, но эквивалентный код, помогающий улучшить понимание работы параметрического декоратора приведен ниже:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {},
- "outputs": [],
- "source": [
- "from random import randint\n",
- "from time import sleep\n",
- "\n",
- "def do_something() -> None:\n",
- " time_to_sleep = randint(1, 5)\n",
- " sleep(time_to_sleep)\n",
- "\n",
- " print('did something')\n",
- "\n",
- "do_something = call_info()(do_something)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "did something\n",
- "[CALL INFO]: do_something() -> None || 3.002840280532837\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "И еще более подробно:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [],
- "source": [
- "from random import randint\n",
- "from time import sleep\n",
- "\n",
- "def do_something() -> None:\n",
- " time_to_sleep = randint(1, 5)\n",
- " sleep(time_to_sleep)\n",
- "\n",
- " print('did something')\n",
- "\n",
- "wrapper = call_info()\n",
- "do_something = wrapper(do_something)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "did something\n",
- "[CALL INFO]: do_something() -> None || 4.003225564956665\n"
- ]
- }
- ],
- "source": [
- "do_something()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson8/sem8_312/task.ipynb b/lessons/lesson8/sem8_312/task.ipynb
deleted file mode 100644
index 0eadfb0d..00000000
--- a/lessons/lesson8/sem8_312/task.ipynb
+++ /dev/null
@@ -1,466 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Декораторы"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 1: Суммы прогрессий\n",
- "\n",
- "Необходимо реализовать функционал для подсчета суммы первых n + 1 - членов арифметической и геометрической прогрессии с возможностями настройки шага и значения первого члена. n соответствует числу вызовов функции по подсчету суммы. \n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "sum_arithmetic = make_arithmetic_progression_sum(first_member=2, step=0.5)\n",
- "sum_geometric = make_geometric_progression_sum(first_member=1, step=0.5)\n",
- "\n",
- "print(sum_arithmetic())\n",
- "print(sum_arithmetic())\n",
- "print('')\n",
- "print(sum_geometric())\n",
- "print(sum_geometric())\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "4.5\n",
- "7.5\n",
- "\n",
- "1.5\n",
- "1.75\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "def make_arithmetic_progression_sum(first_member: float, step: float):\n",
- " last_elem = first_member\n",
- " progr_summ = first_member\n",
- "\n",
- " def update_sum():\n",
- " nonlocal last_elem, progr_summ\n",
- " new_elem = last_elem + step\n",
- " progr_summ += new_elem\n",
- " last_elem = new_elem\n",
- " return progr_summ\n",
- "\n",
- " return update_sum\n",
- "\n",
- "\n",
- "def make_geometric_progression_sum(first_member: float, step: float):\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "4.5\n",
- "7.5\n",
- "11.0\n"
- ]
- }
- ],
- "source": [
- "sum_arithmetic = make_arithmetic_progression_sum(first_member=2, step=0.5)\n",
- "\n",
- "print(sum_arithmetic())\n",
- "print(sum_arithmetic())\n",
- "print(sum_arithmetic())\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 2: Среднее\n",
- "\n",
- "Предположим, что мы занимаемся инвистициями и у нас есть некоторый портфель акций. Каждый день наш портфель приносит нам некоторый доход или убыток. Мы задались целью: каждый день фиксированного периода определять средний доход (или убыток), который мы получаем. С этой целью мы реализовали функцию get_avg(), принимающую на вход значение заработка на сегодняшний день. Наша функция вычисляет среднее в течении определнного фиксированного периода, скажем, 30 дней, после чего обнуляется и начинает вычислять среднее заново, но уже по новым значениям. \n",
- "\n",
- "Также у нас есть друзья инвесторы, которые оценили разработанный нами функционал и хотели бы заполучить свой экземпляр функции get_avg, для подсчета своего дохода в течении интересующего их промежутка времени.\n",
- "\n",
- "Ваша задача: реализовать функционал, для получения произвольного числа независимых функций get_avg(). В момент создания функции сообщается длительность периода расчета среднего, по достижении которого среднее начинает расчитываться заново, а также наш начальный доход. При каждом вызове функции передается число - заработок в текущий день.\n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "get_avg1 = make_averager(accumulation_period=2)\n",
- "print(get_avg1(78))\n",
- "print(get_avg1(-17))\n",
- "print(get_avg1(52))\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "78.0\n",
- "30.5\n",
- "52.0\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код\n",
- "def make_averager(accumulation_period=30):\n",
- " sum_general = amount = 0\n",
- "\n",
- " def get_avg(new_value):\n",
- " nonlocal sum_general, amount\n",
- " sum_general += new_value\n",
- " amount += 1\n",
- " avg = sum_general / amount\n",
- "\n",
- " if amount == accumulation_period:\n",
- " sum_general = amount = 0\n",
- " return avg\n",
- "\n",
- " return get_avg"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "78.0\n",
- "30.5\n",
- "52.0\n"
- ]
- }
- ],
- "source": [
- "get_avg1 = make_averager(accumulation_period=2)\n",
- "print(get_avg1(78))\n",
- "print(get_avg1(-17))\n",
- "print(get_avg1(52))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 3: Сбор статистик\n",
- "\n",
- "Предположим, что мы работаем в отделе аналитики некоторой компании. В компании также существуют другие отделы, которые разрабатывают некоторые функции для осуществления сложных вычислений. Также в нашей компании существует отдел планирования, который следит за исполнением сроков реализации той или иной функции, и в случае, если разработка затягивается, начинает торопить разработчиков. В таком случае разработчики пишут медленный код на скорую руку, что расстраивает заказчиков.\n",
- "\n",
- "Наша задача, как аналитиков, собрать статистику по проблемным функциям. Нас интересует количество вызовов функции, а также среднее время выполнения функции. Все статистики собираются в отдельную базу данных - специальный единый словарь. Более того, статистика должна собираться не для всех функций, а только для функций, зарегестрированных в базе данных. Затем эта информация будет передана начальству, чтобы в скорейшее время заняться переписанием долгих и популярных функций.\n",
- "\n",
- "Ваша задача реализовать функционал для регистрации функций в БД и сбора статистик. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from time import time, sleep\n",
- "import random\n",
- "from functools import wraps\n",
- "\n",
- "functions_register = {}\n",
- "\n",
- "# ваш код\n",
- "def register(func):\n",
- " if func not in functions_register:\n",
- " functions_register[func.__name__] = {\n",
- " \"calls_amount\" : 0,\n",
- " \"time_avg\" : None,\n",
- " }\n",
- "\n",
- "\n",
- "def get_statistic_deco(func):\n",
- " register(func)\n",
- "\n",
- " @wraps(func)\n",
- " def func_with_stats(*args, **kwargs):\n",
- " time_start = time()\n",
- " result = func(*args, **kwargs)\n",
- " time_finish = time()\n",
- " work_time = time_finish - time_start\n",
- "\n",
- " # global functions_register\n",
- " func_name = func.__name__\n",
- " if functions_register[func_name][\"calls_amount\"] == 0:\n",
- " functions_register[func_name][\"calls_amount\"] = 1\n",
- " functions_register[func_name][\"time_avg\"] = work_time\n",
- " else:\n",
- " total_time = functions_register[func_name][\"time_avg\"] * functions_register[func_name][\"calls_amount\"]\n",
- " functions_register[func_name][\"calls_amount\"] += 1\n",
- " functions_register[func_name][\"time_avg\"] = (total_time + work_time) / functions_register[func_name][\"calls_amount\"]\n",
- " return result\n",
- "\n",
- " return func_with_stats\n",
- "\n",
- "\n",
- "@get_statistic_deco\n",
- "def my_super_func():\n",
- " sleeptime = random.randint(1, 3)\n",
- " print(f\"my_super_func will sleep {sleeptime} seconds...\", end=\"\")\n",
- " sleep(sleeptime)\n",
- " print(\"Done!\")\n",
- "\n",
- "@get_statistic_deco\n",
- "def my_super_func2():\n",
- " sleeptime = random.randint(2, 4)\n",
- " print(f\"my_super_func2 will sleep {sleeptime} seconds...\", end=\"\")\n",
- " sleep(sleeptime)\n",
- " print(\"Done!\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "my_super_func will sleep 3 seconds...Done!\n",
- "my_super_func will sleep 2 seconds...Done!\n",
- "my_super_func will sleep 1 seconds...Done!\n",
- "my_super_func will sleep 3 seconds...Done!\n",
- "my_super_func will sleep 1 seconds...Done!\n",
- "my_super_func will sleep 2 seconds...Done!\n",
- "my_super_func2 will sleep 2 seconds...Done!\n",
- "my_super_func2 will sleep 4 seconds...Done!\n",
- "my_super_func2 will sleep 4 seconds...Done!\n",
- "my_super_func2 will sleep 2 seconds...Done!\n",
- "my_super_func2 will sleep 4 seconds...Done!\n",
- "my_super_func2 will sleep 3 seconds...Done!\n"
- ]
- }
- ],
- "source": [
- "my_super_func()\n",
- "my_super_func()\n",
- "my_super_func()\n",
- "my_super_func()\n",
- "my_super_func()\n",
- "my_super_func()\n",
- "\n",
- "my_super_func2()\n",
- "my_super_func2()\n",
- "my_super_func2()\n",
- "my_super_func2()\n",
- "my_super_func2()\n",
- "my_super_func2()\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "{'my_super_func': {'calls_amount': 6, 'time_avg': 2.0024502277374268},\n",
- " 'my_super_func2': {'calls_amount': 6, 'time_avg': 3.1690328121185303}}"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "functions_register"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 4: наивный LRU-кэш\n",
- "\n",
- "Представим, что вы являетесь сотрудником департамента оптимизаций некоторой компании, занимающейся разработкой ПО для научных вычислений. Раздел аналитиков предоставил вам исследование, согласно которому значительная часть функций в вашей компании работает очень медленно и должна быть оптимизирована. Более того, согласно исследованию, вызовы этих функций в основном осуществляются с ограниченным множеством аргументов. Для оптимизации этих функций вы решили использовать LRU-кэш:\n",
- "\n",
- "- вы заранее фиксируете размер кэша - памяти, выделенной для хранения результатов вычислений функции; \n",
- "- в кэше хранится следующая информация: аргументы вызова - результат; \n",
- "- помимо этого для каждой пары хранится время их последнего вызова; \n",
- "- в случае достижения объема кэша установленной границы удаляются значения, чьи времена последних вызовов являются самыми старыми; \n",
- "\n",
- "Для применения данного подхода ко всем проблемным функциям, не переписывая сами функции, вы решили реализовать параметрический декоратор, т.к. для разных функций требуется разный размер кеша.\n",
- "\n",
- "Ваша задача: реализовать описанный декоратор."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 52,
- "metadata": {},
- "outputs": [],
- "source": [
- "from time import time, sleep\n",
- "from functools import wraps\n",
- "# ваш код\n",
- "\n",
- "\n",
- "def naiv_lru_cache(cache_size=5):\n",
- " def apply_cache(func):\n",
- " call_info = {}\n",
- " time_info = {}\n",
- "\n",
- " @wraps(func)\n",
- " def call(*args, **kwargs):\n",
- " print(call_info, time_info, sep=\"\\n\")\n",
- "\n",
- " key = tuple(args + tuple(kwargs.values()))\n",
- " cur_time = time()\n",
- " if key in call_info:\n",
- " time_info[key] = cur_time\n",
- " return call_info[key]\n",
- " \n",
- " result = func(*args, **kwargs)\n",
- "\n",
- " if len(call_info) == cache_size:\n",
- " least_used_key = min(time_info.items(), key=lambda x: x[-1])[0]\n",
- " del call_info[least_used_key]\n",
- " del time_info[least_used_key]\n",
- "\n",
- " call_info[key] = result\n",
- " time_info[key] = cur_time\n",
- " return result\n",
- " \n",
- " return call\n",
- " return apply_cache\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "@naiv_lru_cache(5)\n",
- "def super_func(x):\n",
- " print(f\"super_func({x = }) started!\")\n",
- " sleep(x)\n",
- " return x"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{(2,): 2, (3,): 3, (4,): 4, (5,): 5, (6,): 6}\n",
- "{(2,): 1698397561.1352177, (3,): 1698397561.1352637, (4,): 1698397561.1353033, (5,): 1698397561.135338, (6,): 1698397587.1443498}\n",
- "2||time = 0.00014925003051757812\n",
- " - \n",
- "{(2,): 2, (3,): 3, (4,): 4, (5,): 5, (6,): 6}\n",
- "{(2,): 1698397599.098065, (3,): 1698397561.1352637, (4,): 1698397561.1353033, (5,): 1698397561.135338, (6,): 1698397587.1443498}\n",
- "3||time = 5.316734313964844e-05\n",
- " - \n",
- "{(2,): 2, (3,): 3, (4,): 4, (5,): 5, (6,): 6}\n",
- "{(2,): 1698397599.098065, (3,): 1698397599.0981543, (4,): 1698397561.1353033, (5,): 1698397561.135338, (6,): 1698397587.1443498}\n",
- "4||time = 4.9591064453125e-05\n",
- " - \n",
- "{(2,): 2, (3,): 3, (4,): 4, (5,): 5, (6,): 6}\n",
- "{(2,): 1698397599.098065, (3,): 1698397599.0981543, (4,): 1698397599.0982351, (5,): 1698397561.135338, (6,): 1698397587.1443498}\n",
- "5||time = 4.9114227294921875e-05\n",
- " - \n",
- "{(2,): 2, (3,): 3, (4,): 4, (5,): 5, (6,): 6}\n",
- "{(2,): 1698397599.098065, (3,): 1698397599.0981543, (4,): 1698397599.0982351, (5,): 1698397599.0983088, (6,): 1698397587.1443498}\n",
- "6||time = 4.887580871582031e-05\n",
- " - \n"
- ]
- }
- ],
- "source": [
- "for i in range(2, 7):\n",
- " start = time()\n",
- " print(super_func(i), end=\"||\")\n",
- " finish = time()\n",
- " print(f\"time = {finish - start}\")\n",
- " print(\"-\".center(20))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{(1,): 1, (2,): 2, (3,): 3, (4,): 4, (5,): 5}\n",
- "{(1,): 1698397561.1351657, (2,): 1698397561.1352177, (3,): 1698397561.1352637, (4,): 1698397561.1353033, (5,): 1698397561.135338}\n",
- "super_func(x = 6) started!\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "6"
- ]
- },
- "execution_count": 61,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "super_func(6)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson8/sem8_312/task.pdf b/lessons/lesson8/sem8_312/task.pdf
deleted file mode 100644
index 0011e473..00000000
Binary files a/lessons/lesson8/sem8_312/task.pdf and /dev/null differ
diff --git a/lessons/lesson8/sem8_313/task.ipynb b/lessons/lesson8/sem8_313/task.ipynb
deleted file mode 100644
index 22111de9..00000000
--- a/lessons/lesson8/sem8_313/task.ipynb
+++ /dev/null
@@ -1,170 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Декораторы"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 1: Суммы прогрессий\n",
- "\n",
- "Необходимо реализовать функционал для подсчета суммы первых n + 1 - членов арифметической и геометрической прогрессии с возможностями настройки шага и значения первого члена. n соответствует числу вызовов функции по подсчету суммы. \n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "sum_arithmetic = make_arithmetic_progression_sum(first_member=2, step=0.5)\n",
- "sum_geometric = make_geometric_progression_sum(first_member=1, step=0.5)\n",
- "\n",
- "print(sum_arithmetic())\n",
- "print(sum_arithmetic())\n",
- "print('')\n",
- "print(sum_geometric())\n",
- "print(sum_geometric())\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "4.5\n",
- "7.5\n",
- "\n",
- "1.5\n",
- "1.75\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "def make_arithmetic_progression_sum(first_member: float, step: float):\n",
- " # ваш код\n",
- " pass\n",
- "\n",
- "\n",
- "def make_geometric_progression_sum(first_member: float, step: float):\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 2: Среднее\n",
- "\n",
- "Предположим, что мы занимаемся инвистициями и у нас есть некоторый портфель акций. Каждый день наш портфель приносит нам некоторый доход или убыток. Мы задались целью: каждый день фиксированного периода определять средний доход (или убыток), который мы получаем. С этой целью мы реализовали функцию get_avg(), принимающую на вход значение заработка на сегодняшний день. Наша функция вычисляет среднее в течении определнного фиксированного периода, скажем, 30 дней, после чего обнуляется и начинает вычислять среднее заново, но уже по новым значениям. \n",
- "\n",
- "Также у нас есть друзья инвесторы, которые оценили разработанный нами функционал и хотели бы заполучить свой экземпляр функции get_avg, для подсчета своего дохода в течении интересующего их промежутка времени.\n",
- "\n",
- "Ваша задача: реализовать функционал, для получения произвольного числа независимых функций get_avg(). В момент создания функции сообщается длительность периода расчета среднего, по достижении которого среднее начинает расчитываться заново, а также наш начальный доход. При каждом вызове функции передается число - заработок в текущий день.\n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "get_avg1 = make_averager(accumulation_period=2)\n",
- "print(get_avg1(78))\n",
- "print(get_avg1(-17))\n",
- "print(get_avg1(52))\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "78.0\n",
- "30.5\n",
- "52.0\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 3: Сбор статистик\n",
- "\n",
- "Предположим, что мы работаем в отделе аналитики некоторой компании. В компании также существуют другие отделы, которые разрабатывают некоторые функции для осуществления сложных вычислений. Также в нашей компании существует отдел планирования, который следит за исполнением сроков реализации той или иной функции, и в случае, если разработка затягивается, начинает торопить разработчиков. В таком случае разработчики пишут медленный код на скорую руку, что расстраивает заказчиков.\n",
- "\n",
- "Наша задача, как аналитиков, собрать статистику по проблемным функциям. Нас интересует количество вызовов функции, а также среднее время выполнения функции. Все статистики собираются в отдельную базу данных - специальный единый словарь. Более того, статистика должна собираться не для всех функций, а только для функций, зарегестрированных в базе данных. Затем эта информация будет передана начальству, чтобы в скорейшее время заняться переписанием долгих и популярных функций.\n",
- "\n",
- "Ваша задача реализовать функционал для регистрации функций в БД и сбора статистик. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "functions_register = {}\n",
- "\n",
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 4: наивный LRU-кэш\n",
- "\n",
- "Представим, что вы являетесь сотрудником департамента оптимизаций некоторой компании, занимающейся разработкой ПО для научных вычислений. Раздел аналитиков предоставил вам исследование, согласно которому значительная часть функций в вашей компании работает очень медленно и должна быть оптимизирована. Более того, согласно исследованию, вызовы этих функций в основном осуществляются с ограниченным множеством аргументов. Для оптимизации этих функций вы решили использовать LRU-кэш:\n",
- "\n",
- "- вы заранее фиксируете размер кэша - памяти, выделенной для хранения результатов вычислений функции; \n",
- "- в кэше хранится следующая информация: аргументы вызова - результат; \n",
- "- помимо этого для каждой пары хранится время их последнего вызова; \n",
- "- в случае достижения объема кэша установленной границы удаляются значения, чьи времена последних вызовов являются самыми старыми; \n",
- "\n",
- "Для применения данного подхода ко всем проблемным функциям, не переписывая сами функции, вы решили реализовать параметрический декоратор, т.к. для разных функций требуется разный размер кеша.\n",
- "\n",
- "Ваша задача: реализовать описанный декоратор."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson8/sem8_313/task.pdf b/lessons/lesson8/sem8_313/task.pdf
deleted file mode 100644
index 0011e473..00000000
Binary files a/lessons/lesson8/sem8_313/task.pdf and /dev/null differ
diff --git a/lessons/lesson8/sem8_314/task.ipynb b/lessons/lesson8/sem8_314/task.ipynb
deleted file mode 100644
index 22111de9..00000000
--- a/lessons/lesson8/sem8_314/task.ipynb
+++ /dev/null
@@ -1,170 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Декораторы"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 1: Суммы прогрессий\n",
- "\n",
- "Необходимо реализовать функционал для подсчета суммы первых n + 1 - членов арифметической и геометрической прогрессии с возможностями настройки шага и значения первого члена. n соответствует числу вызовов функции по подсчету суммы. \n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "sum_arithmetic = make_arithmetic_progression_sum(first_member=2, step=0.5)\n",
- "sum_geometric = make_geometric_progression_sum(first_member=1, step=0.5)\n",
- "\n",
- "print(sum_arithmetic())\n",
- "print(sum_arithmetic())\n",
- "print('')\n",
- "print(sum_geometric())\n",
- "print(sum_geometric())\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "4.5\n",
- "7.5\n",
- "\n",
- "1.5\n",
- "1.75\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "def make_arithmetic_progression_sum(first_member: float, step: float):\n",
- " # ваш код\n",
- " pass\n",
- "\n",
- "\n",
- "def make_geometric_progression_sum(first_member: float, step: float):\n",
- " # ваш код\n",
- " pass"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 2: Среднее\n",
- "\n",
- "Предположим, что мы занимаемся инвистициями и у нас есть некоторый портфель акций. Каждый день наш портфель приносит нам некоторый доход или убыток. Мы задались целью: каждый день фиксированного периода определять средний доход (или убыток), который мы получаем. С этой целью мы реализовали функцию get_avg(), принимающую на вход значение заработка на сегодняшний день. Наша функция вычисляет среднее в течении определнного фиксированного периода, скажем, 30 дней, после чего обнуляется и начинает вычислять среднее заново, но уже по новым значениям. \n",
- "\n",
- "Также у нас есть друзья инвесторы, которые оценили разработанный нами функционал и хотели бы заполучить свой экземпляр функции get_avg, для подсчета своего дохода в течении интересующего их промежутка времени.\n",
- "\n",
- "Ваша задача: реализовать функционал, для получения произвольного числа независимых функций get_avg(). В момент создания функции сообщается длительность периода расчета среднего, по достижении которого среднее начинает расчитываться заново, а также наш начальный доход. При каждом вызове функции передается число - заработок в текущий день.\n",
- "\n",
- "Предполагаемые сценарии использования:\n",
- "\n",
- "```python\n",
- "\n",
- "get_avg1 = make_averager(accumulation_period=2)\n",
- "print(get_avg1(78))\n",
- "print(get_avg1(-17))\n",
- "print(get_avg1(52))\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "78.0\n",
- "30.5\n",
- "52.0\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 3: Сбор статистик\n",
- "\n",
- "Предположим, что мы работаем в отделе аналитики некоторой компании. В компании также существуют другие отделы, которые разрабатывают некоторые функции для осуществления сложных вычислений. Также в нашей компании существует отдел планирования, который следит за исполнением сроков реализации той или иной функции, и в случае, если разработка затягивается, начинает торопить разработчиков. В таком случае разработчики пишут медленный код на скорую руку, что расстраивает заказчиков.\n",
- "\n",
- "Наша задача, как аналитиков, собрать статистику по проблемным функциям. Нас интересует количество вызовов функции, а также среднее время выполнения функции. Все статистики собираются в отдельную базу данных - специальный единый словарь. Более того, статистика должна собираться не для всех функций, а только для функций, зарегестрированных в базе данных. Затем эта информация будет передана начальству, чтобы в скорейшее время заняться переписанием долгих и популярных функций.\n",
- "\n",
- "Ваша задача реализовать функционал для регистрации функций в БД и сбора статистик. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "functions_register = {}\n",
- "\n",
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Задача 4: наивный LRU-кэш\n",
- "\n",
- "Представим, что вы являетесь сотрудником департамента оптимизаций некоторой компании, занимающейся разработкой ПО для научных вычислений. Раздел аналитиков предоставил вам исследование, согласно которому значительная часть функций в вашей компании работает очень медленно и должна быть оптимизирована. Более того, согласно исследованию, вызовы этих функций в основном осуществляются с ограниченным множеством аргументов. Для оптимизации этих функций вы решили использовать LRU-кэш:\n",
- "\n",
- "- вы заранее фиксируете размер кэша - памяти, выделенной для хранения результатов вычислений функции; \n",
- "- в кэше хранится следующая информация: аргументы вызова - результат; \n",
- "- помимо этого для каждой пары хранится время их последнего вызова; \n",
- "- в случае достижения объема кэша установленной границы удаляются значения, чьи времена последних вызовов являются самыми старыми; \n",
- "\n",
- "Для применения данного подхода ко всем проблемным функциям, не переписывая сами функции, вы решили реализовать параметрический декоратор, т.к. для разных функций требуется разный размер кеша.\n",
- "\n",
- "Ваша задача: реализовать описанный декоратор."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson8/sem8_314/task.pdf b/lessons/lesson8/sem8_314/task.pdf
deleted file mode 100644
index 0011e473..00000000
Binary files a/lessons/lesson8/sem8_314/task.pdf and /dev/null differ
diff --git a/lessons/lesson9/interactive_conspect.ipynb b/lessons/lesson9/interactive_conspect.ipynb
deleted file mode 100644
index f3b98421..00000000
--- a/lessons/lesson9/interactive_conspect.ipynb
+++ /dev/null
@@ -1,1286 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Функции: избранные темы\n",
- "\n",
- "В предыдущих лекциях мы рассмотрели с вами простейшие [пользовательские функции в языке Python](../lesson7/interactive_conspect.ipynb) и более сложные конструкции, которые могут быть построены с помощью функций, к числе которым относятся [замыкания и декораторы](../lesson8/interactive_conspect.ipynb). В это лекции мы закончим обзор функций в Python, познакомившись с анонимными функциями, генераторными функциями и некоторыми встроенными функциями языка Python. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Анонимные функции\n",
- "\n",
- "Иногда возникает необходимости создания простых функций, чье тело состоит из единственного утверждения **return**. Такие функции обычно используются для обработки некоторых последовательностей по определенным правилам. Например, мы хотим посчитать среднее значение квадратов элементов некоторой числовой последовательности. Мы могли бы сделать это следующим образом:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "mean of elements' squares: 28.5\n"
- ]
- }
- ],
- "source": [
- "def get_squared_number(number: float) -> float:\n",
- " return number ** 2\n",
- "\n",
- "\n",
- "sequence = list(range(10))\n",
- "\n",
- "square_mean = sum(map(get_squared_number, sequence))\n",
- "square_mean /= len(sequence)\n",
- "\n",
- "print(f\"mean of elements' squares: {square_mean}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Про функцию **map** мы поговорим чуть позже, сейчас сосредоточимся на другом. \n",
- "\n",
- "Данный подход может показаться избыточным, ведь нам пришлось создать целую функцию для банального возведения элементов в квадрат. Чтобы избежать подобного нагромождения однотипного кода, в Python поддерживаются анонимные функции, известные также как лямбда-выражения (название берет свое начало из формальной системы [лямбда-исчисления](https://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5)). Лямбда-выражения - однострочные функции, тело которых состоит из простых выражений - возвращаемых значений.\n",
- "\n",
- "В обзем виде анонимные функции определяются следующим образом:\n",
- "\n",
- "```python\n",
- "lambda parameters: expression\n",
- "```\n",
- "\n",
- "Рассмотрим его подробнее:\n",
- "\n",
- "- **lambda** - ключевое слово, которое сообщает интерпретатору, что вы намереваетесь создать анонимную функцию;\n",
- "- **parameters** - список параметров лямбда-функции, разделенных запятыми; параметры лямбда-функции ничем не отличаются от параметров обычной функции: вы можете создавать анонимные функции с позиционными параметрами, параметрами со значениями по умолчанию, чисто именованные параметры, параметрами в формах *args, **kwargs;\n",
- "\n",
- " Совет: несмотря на возможность использования различныъ форм параметров в лямбда-выражениях, стоит помнить, что анонимные функции - простые однострочные функции, выполняющие очень простые действия. Исходя из этого, стоит обходиться лишь позиционными параметрами и не переусложнять их.\n",
- "\n",
- "- **expression** - простое выражение, результат вычисления которого будет возвращен после вызова лямбда-функции;\n",
- "\n",
- "Теперь, вооружившись анонимными функциями, исправим приведенный выше пример:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "mean of elements' squares: 28.5\n"
- ]
- }
- ],
- "source": [
- "sequence = list(range(10))\n",
- "\n",
- "square_mean = sum(map(lambda x: x ** 2, sequence))\n",
- "square_mean /= len(sequence)\n",
- "\n",
- "print(f\"mean of elements' squares: {square_mean}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Теперь наш код выглядит более аккуратно и более читабельно.\n",
- "\n",
- "Стоит обратить внимание, что тело лямбда-функции - это именно выражение. Лямбда-функция не может содержать утвержедний, типа присваивания, или составных утверждений, типа циклов - только выражение. Однако вы по-прежнему можете использовать простейшее ветвление с помощью тернарного оператора: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "sequence: [0, 2, -1, 8, 5, -4, -4, -1, 7, -4]\n",
- "square_roots_sum = 9.124459975683667;\n"
- ]
- }
- ],
- "source": [
- "from random import randint\n",
- "from math import sqrt\n",
- "\n",
- "\n",
- "sequence = [randint(-9, 9) for _ in range(10)]\n",
- "\n",
- "print(f'sequence: {sequence}')\n",
- "\n",
- "square_roots_sum = sum(map(lambda x: sqrt(x) if x >= 0 else 0, sequence))\n",
- "print(f'{square_roots_sum = };')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также выражение в теле лямбда-функции может обуславливаться на локальный контекст, т.е. при вычислении выражения могут быть использованы внешние для данной анонимной функции переменные. Так, в примере ниже, прдставлен код для осуществления нормализации входных данных, что может быть полезным в ряде прикладных задач, в частности в задачах машинного обучения:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "sequence: [8, 4, 5, 3, 2, 4, -9, 7, -2, 8]\n",
- "sequence normalized: [1.0, 0.7647058823529411, 0.8235294117647058, 0.7058823529411765, 0.6470588235294118, 0.7647058823529411, 0.0, 0.9411764705882353, 0.4117647058823529, 1.0];\n"
- ]
- }
- ],
- "source": [
- "from random import randint\n",
- "\n",
- "\n",
- "sequence = [randint(-9, 9) for _ in range(10)]\n",
- "\n",
- "print(f'sequence: {sequence}')\n",
- "\n",
- "sequence_min, sequence_max = min(sequence), max(sequence)\n",
- "sequence_normalized = list(\n",
- " map(\n",
- " lambda x: (x - sequence_min) / (sequence_max - sequence_min),\n",
- " sequence \n",
- " )\n",
- ")\n",
- "\n",
- "print(f'sequence normalized: {sequence_normalized};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Обращаю ваше внимание, что все примеры выше являются чисто учебными, в реальных задах подобного плана целесообразней было бы обойтись списковыми включениями, которые были разобраны ранее, а также генераторными выражениями, которые будут разобраны в следующем разделе:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "sequence: [-6, -5, 3, 3, 0, -3, -1, 8, 6, -4]\n",
- "sequence normalized: [0.0, 0.07142857142857142, 0.6428571428571429, 0.6428571428571429, 0.42857142857142855, 0.21428571428571427, 0.35714285714285715, 1.0, 0.8571428571428571, 0.14285714285714285];\n"
- ]
- }
- ],
- "source": [
- "from random import randint\n",
- "\n",
- "\n",
- "sequence = [randint(-9, 9) for _ in range(10)]\n",
- "\n",
- "print(f'sequence: {sequence}')\n",
- "\n",
- "sequence_min, sequence_max = min(sequence), max(sequence)\n",
- "sequence_normalized = [\n",
- " (elem - sequence_min) / (sequence_max - sequence_min)\n",
- " for elem in sequence\n",
- "]\n",
- "\n",
- "print(f'sequence normalized: {sequence_normalized};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Однако, не стоит ставить крест на анонимных функциях, они по-прежнему могут быть полезными, например, для сортировки элементов по специальным правилам. Здесь позволю себе повторить пример, с сортировкой слов по их окончаниям, приведенный в одной из предыдущих лекций.\n",
- "\n",
- "Предположим, что у нас есть некоторый список доступных слов, и мы бы хотели сочинить стихи с их использованием. Не секрет, что для удачного стихосложения требуется, чтобы слова с определенной периодичностью имели одинаковые окончания (если вы не Даниил Хармс, разумеется). В этом случае, как программисты на языке Python, мы можем без труда сгруппировать слова по окончаниям, используя анонимные функции:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['storage', 'orange', 'apple', 'begin', 'sin', 'formation', 'vibration', 'station', 'glass', 'grass']\n"
- ]
- }
- ],
- "source": [
- "words = [\n",
- " 'apple', 'grass', 'station', 'begin', 'orange',\n",
- " 'sin', 'glass', 'storage', 'vibration', 'formation'\n",
- "]\n",
- "\n",
- "words_sorted = sorted(words, key=lambda x: x[::-1])\n",
- "\n",
- "print(words_sorted)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В завершении этого раздела лишний раз подчеркну, что использовании лямбда-функций обосновано только если необходимый функционал является очень простым. Не стоит оформлять в виде анонимной функции сложный и неинтуитивно понятный код. В этом случае стоит задуматься над реализацией отдельно функции."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Генераторные функции\n",
- "\n",
- "Изучая последовательности мы познакомились с итерируемыми объектами и итераторами. Также в предыдущих лекциях нами было установлено, что к итерируемым объектам относятся не только последовательности, но еще и списки, и словари. Сегодня мы рассмотрим еще один тип итерируемых объектов: генераторы. Создание которых можно осуществить посредством генераторных функций.\n",
- "\n",
- "Генераторная функция - это функция, тело которой содержит как минимуму одно выражение с ключевым словом **yeild**. При вызове генераторной функции, тело функции не выполняется сразу, а результат выполнения - специальный объект-генератор. Давайте проиллюстрируем сказанное простым примером:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Generator\n",
- "\n",
- "\n",
- "def generate_123() -> Generator:\n",
- " print('start generation')\n",
- "\n",
- " for i in range(1, 4):\n",
- " print(f'generate: {i}')\n",
- " yield i\n",
- "\n",
- " print('end generation')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "function\n",
- "generator\n"
- ]
- }
- ],
- "source": [
- "generator = generate_123()\n",
- "\n",
- "print(type(generate_123).__name__)\n",
- "print(type(generator).__name__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как мы видим, генераторная функция имеет тип данных **function**, как и прочие пользовательские функции, которые мы видели до этого. Но возвращенный объект имеет новый для нас типа данных - **generator**. Давайте разберемся, что это такое.\n",
- "\n",
- "Генератор - это специальный объект, который фактически оборачивает тело генераторной функции, ее локальные переменные и текущую точку выполнения. Генератор является итерируемым объектом, а потому поддерживает вызов встроенной функции **next()**, которая обсуждалась ранее:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "start generation\n",
- "generate: 1\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "1"
- ]
- },
- "execution_count": 28,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "next(generator)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Во время вызова функции next(), генератор выполняет тело функции до тех пор, пока не встретит выражение с ключевым словом yeild. Если после слова yeild идет какое-либо выражение, генератор произведет значение этого выражения и \"вернет его вызывающей стороне\", после чего остановится, запомнит свое текущее состояние, после чего вернет управление над потоком выполнения вызывающей стороне. Если после yeild ничего нет, то генератор произведет None с сделает все то же самое. \n",
- "\n",
- "После очередного вызова next() генератор продолжит выполнение с того места, на котором он остановился в последний раз:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "generate: 2\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "2"
- ]
- },
- "execution_count": 29,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "next(generator)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "generate: 3\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "3"
- ]
- },
- "execution_count": 30,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "next(generator)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В данном примере мы создали генератор, который способен произвести числа от 1 до 3, т.е. наш генератор поддерживает всего 3 вызова функции next(). При попытки вызвать функцию next() с генератором, исчерпавшим свои значения, мы получим исключение типа StopIteration. Т.е. столкнемся с классическим поведением ограниченных итераторов:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "end generation\n"
- ]
- },
- {
- "ename": "StopIteration",
- "evalue": "",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson9\\interactive_conspect.ipynb Cell 24\u001b[0m line \u001b[0;36m1\n\u001b[1;32m----> 1\u001b[0m \u001b[39mnext\u001b[39;49m(generator)\n",
- "\u001b[1;31mStopIteration\u001b[0m: "
- ]
- }
- ],
- "source": [
- "next(generator)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "После исчерпания генератор не может быть использован повторно. Однако мы можем создать новый генератор, используя нашу генераторную функцию. В таком контексте мы можем думать о генераторной функции, как о фабрике генераторов.\n",
- "\n",
- "Поскольку генераторы являются итерируемыми объектами, мы можем использовать их в циклах for, как показано ниже:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "start generation\n",
- "generate: 1\n",
- "get from generator: 1;\n",
- "generate: 2\n",
- "get from generator: 2;\n",
- "generate: 3\n",
- "get from generator: 3;\n",
- "end generation\n"
- ]
- }
- ],
- "source": [
- "generator = generate_123()\n",
- "\n",
- "for i in generator:\n",
- " print(f'get from generator: {i};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Также генераторная функция может содержать утверждение return. В этом случае, при достижении утверждения return генератором, будет возбуждаться исключение StopIteration, в качестве аргумента которого будет передано выражение, следующее за return. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Generator\n",
- "\n",
- "\n",
- "def generate_even_digits() -> Generator:\n",
- " num_curr = 0\n",
- "\n",
- " while True:\n",
- " if num_curr >= 10:\n",
- " return 'numbers are exhausted'\n",
- " \n",
- " if num_curr % 2 == 0:\n",
- " yield num_curr\n",
- "\n",
- " num_curr += 1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [
- {
- "ename": "StopIteration",
- "evalue": "numbers are exhausted",
- "output_type": "error",
- "traceback": [
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)",
- "\u001b[1;32mc:\\Users\\Michail\\Desktop\\mipt\\teaching\\python_mipt_dafe\\lessons\\lesson9\\interactive_conspect.ipynb Cell 29\u001b[0m line \u001b[0;36m8\n\u001b[0;32m 6\u001b[0m \u001b[39mnext\u001b[39m(generator)\n\u001b[0;32m 7\u001b[0m \u001b[39mnext\u001b[39m(generator)\n\u001b[1;32m----> 8\u001b[0m \u001b[39mnext\u001b[39;49m(generator)\n",
- "\u001b[1;31mStopIteration\u001b[0m: numbers are exhausted"
- ]
- }
- ],
- "source": [
- "generator = generate_even_digits()\n",
- "\n",
- "next(generator)\n",
- "next(generator)\n",
- "next(generator)\n",
- "next(generator)\n",
- "next(generator)\n",
- "next(generator)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Generator\n",
- "\n",
- "\n",
- "def generate_pyramid_sequence(top_number: int) -> Generator:\n",
- " if top_number <= 1:\n",
- " raise ValueError('top number should be greater than 1')\n",
- " \n",
- " top_number = int(top_number)\n",
- " \n",
- " for i in range(1, top_number):\n",
- " yield i\n",
- "\n",
- " for i in range(top_number, 0, -1):\n",
- " yield i"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Как настоящая фабрика, генераторная функция может иметь параметры, которые задаются теми же способами, что и параметры пользовательских функций."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1 2 3 4 5 4 3 2 1 "
- ]
- }
- ],
- "source": [
- "for i in generate_pyramid_sequence(5):\n",
- " print(i, end=' ')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "В данном примере мы создали генераторную функцию, которая строит \"пирамидальную\" последовательность. Генераторная функция принимает на вход один аргумент - целое положительное число, большее 1, и возвращает генератор, стоящий данную последовательность.\n",
- "\n",
- "Код из пример выше может быть упрощен с помощью использования специальной конструкции:\n",
- "\n",
- "```python\n",
- "yield from expression \n",
- "```\n",
- "\n",
- "В качестве expression должен выступать некоторый итерируемый объект. Данная конструкция порождает значения напрямую и итерируемого объекта и позволяет значительно упростить код."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Generator\n",
- "\n",
- "\n",
- "def generate_pyramid_sequence(top_number: int) -> Generator:\n",
- " if top_number <= 1:\n",
- " raise ValueError('top number should be greater than 1')\n",
- " \n",
- " top_number = int(top_number)\n",
- " \n",
- " yield from range(1, top_number)\n",
- " yield from range(top_number, 0, -1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1 2 3 4 5 4 3 2 1 "
- ]
- }
- ],
- "source": [
- "for i in generate_pyramid_sequence(5):\n",
- " print(i, end=' ')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Тепрь давайте обсудим, зачем нам вообще нужны генераторы, ведь все, что мы делали выше, может быть сделано и без использования генераторных функций. На самом деле генераторы могут оказать очень полезными.\n",
- "\n",
- "Во-первых, в отличие от функций, которые возвращают коллекции элементов, генераторы могут быть наограниченными. Т.е. мы буквально можем создать генератор, способный порождать бесконечное число элементов. В качестве примера такого генератора, давайте напишем генератор, способный порождать арифметическую прогрессию:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Generator\n",
- "\n",
- "\n",
- "def generate_arithmetic_progression(start=0, step=1) -> Generator:\n",
- " current_member = start\n",
- "\n",
- " while True:\n",
- " yield current_member\n",
- " current_member += step"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "member_1: 0;\n",
- "member_2: 2;\n",
- "member_3: 4;\n",
- "member_4: 6;\n",
- "member_5: 8;\n"
- ]
- }
- ],
- "source": [
- "progression_gen = generate_arithmetic_progression(step=2)\n",
- "\n",
- "for i in range(5):\n",
- " print(f'member_{i + 1}: {next(progression_gen)};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Подобное просто невозможно сделать с помощью функций, генерирующих последовательности, поскольку нам банально не зватит памяти для аллоцирования бесконечного числа элементов. Отсюда следует второе полезное качество генераторов. Генераторы вычисляются лениво. Т.е. очередное значение, порожденное генератором, будет рассчитано и аллоцировано только в момент непосредственного вызова. Генератора не предрассчитывает все пораждаемые элементы заранее, в отличие от функций, возвращающих коллекции.\n",
- "\n",
- "Это свойство может оказаться очень полезным при работе с большими объемами данных. Например, подобные генераторы могут быть использованы при порционной загрузке обучающих выборок при решении задач машинного обучения."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Генераторные выражения"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "По аналогии со списковыми и словарными включениями, вы можете создавать простые генераторы с помощью генераторных выражений. В качестве примера использования генераторных выражений, давайте вычислим сумму квадратов чисел от 0 до 9 с помощью списковых и генераторных включений:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "285"
- ]
- },
- "execution_count": 47,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# испоьзование спискового включения\n",
- "sum([i ** 2 for i in range(10)])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "285"
- ]
- },
- "execution_count": 48,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# использование генераторного выражения\n",
- "sum(i ** 2 for i in range(10))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Старайтесь использовать генераторные выражения для создания простых и обезличенных генераторов для мгновенного использования (например, для использования в функции sum, как в данном примере). При необходимости создания сложных генераторов и запутанной логикой, отдавайте предпочтение генераторным функциям."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Некоторые встроенные функции и итерируемые объекты"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### max() / min()\n",
- "\n",
- "Функции определения минимального и максимального значения среди переденных, или среди элементов некоторого итерируемого объекта. Также поддерживают аргумент key, для определения статистики по переданному ключу:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function max in module builtins:\n",
- "\n",
- "max(...)\n",
- " max(iterable, *[, default=obj, key=func]) -> value\n",
- " max(arg1, arg2, *args, *[, key=func]) -> value\n",
- " \n",
- " With a single iterable argument, return its biggest item. The\n",
- " default keyword-only argument specifies an object to return if\n",
- " the provided iterable is empty.\n",
- " With two or more arguments, return the largest argument.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(max)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function min in module builtins:\n",
- "\n",
- "min(...)\n",
- " min(iterable, *[, default=obj, key=func]) -> value\n",
- " min(arg1, arg2, *args, *[, key=func]) -> value\n",
- " \n",
- " With a single iterable argument, return its smallest item. The\n",
- " default keyword-only argument specifies an object to return if\n",
- " the provided iterable is empty.\n",
- " With two or more arguments, return the smallest argument.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(min)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "4\n",
- "9\n",
- "1\n"
- ]
- }
- ],
- "source": [
- "print(\n",
- " max(range(5)),\n",
- " max(1, 3, 5, 7, 9),\n",
- " max(range(1, 10), key=lambda x: 5 - x),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### any() / all()\n",
- "\n",
- "Функция all позволяет определить, принимают ли все элементы итерируемого объекта значение True в булевом контексте или нет. В случае если итерируемый объект пустой, функция вернет True. any возвращает True, если хотя бы один элемент итерируемого объект принимает значение True в булевом контексте. В случае если итерируемый объект пуст, функция вернет False."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function all in module builtins:\n",
- "\n",
- "all(iterable, /)\n",
- " Return True if bool(x) is True for all values x in the iterable.\n",
- " \n",
- " If the iterable is empty, return True.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(all)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function any in module builtins:\n",
- "\n",
- "any(iterable, /)\n",
- " Return True if bool(x) is True for any x in the iterable.\n",
- " \n",
- " If the iterable is empty, return False.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(any)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "False\n",
- "True\n"
- ]
- }
- ],
- "source": [
- "print(\n",
- " all(range(5)),\n",
- " all([]),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True\n",
- "False\n"
- ]
- }
- ],
- "source": [
- "print(\n",
- " any(range(5)),\n",
- " any([]),\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### range()\n",
- "\n",
- "Мы очень часто работали c range, но только узнав о генераторах и итераторах мы готовы всерьез ее обсудить. range является специальным встроенным объектом. При использовании его в цикле for range возвращает генератор, пораждающий числа из заданного диапазона с заданным шагом, который по умолчанию равен 1. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "range\n"
- ]
- }
- ],
- "source": [
- "range_obj = range(1, 10, 2)\n",
- "\n",
- "print(type(range_obj).__name__)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 72,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1\n",
- "3\n",
- "5\n",
- "7\n",
- "9\n"
- ]
- }
- ],
- "source": [
- "for i in range_obj:\n",
- " print(i)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### enumerate()\n",
- "\n",
- "Мы также неоднократно встречались с enumerate, но понять суть ее работы мы можем только сейчас. Аналогично range enumerate является встроенным типом данных, позволяющих конструировать специальные объекты. При использовании этих объектов в циклах for, они возвращают генераторы, пораждающие пары вида <индекс, значение>."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "enumerate\n"
- ]
- }
- ],
- "source": [
- "enum = enumerate(range(5), start=1)\n",
- "\n",
- "print(type(enum).__name__)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 74,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "elem_1: 0\n",
- "elem_2: 1\n",
- "elem_3: 2\n",
- "elem_4: 3\n",
- "elem_5: 4\n"
- ]
- }
- ],
- "source": [
- "for i, elem in enum:\n",
- " print(f'elem_{i}: {elem}')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### sum()\n",
- "\n",
- "Функция sum предназначена для суммирования всех элементов переданного итерируемого объекта. Причем аргумент start позволяет настроить тип и значения объекта, к которому будут прибавляться очередные значения."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on built-in function sum in module builtins:\n",
- "\n",
- "sum(iterable, /, start=0)\n",
- " Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n",
- " \n",
- " When the iterable is empty, return the start value.\n",
- " This function is intended specifically for use with numeric values and may\n",
- " reject non-numeric types.\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(sum)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 76,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "10"
- ]
- },
- "execution_count": 76,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "sum(range(5))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### map()\n",
- "\n",
- "Мы знакомы с объектом map из примеров выше. Объект map позволяет построить итерируемый объект, содержащий результат применения переданной функции, к переданным итерируемым объектам. Например, мы можем посчитать поэлементную разницу двух массивов:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 106,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "vector1: [4, -5, 4, -7, -3, -6, -10, -9, -10, -7];\n",
- "vector2: [-10, -3, 6, -9, 7, 4, 3, -1, 5, -8];\n",
- "map\n"
- ]
- }
- ],
- "source": [
- "from random import randint\n",
- "\n",
- "\n",
- "vector1 = [randint(-10, 10) for _ in range(10)]\n",
- "vector2 = [randint(-10, 10) for _ in range(10)]\n",
- "\n",
- "print(\n",
- " f'vector1: {vector1};',\n",
- " f'vector2: {vector2};',\n",
- " sep='\\n'\n",
- ")\n",
- "\n",
- "differences = map(lambda x, y: x - y, vector1, vector2)\n",
- "\n",
- "print(type(differences).__name__)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 107,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "difference_1: 14;\n",
- "difference_2: -2;\n",
- "difference_3: -2;\n",
- "difference_4: 2;\n",
- "difference_5: -10;\n",
- "difference_6: -10;\n",
- "difference_7: -13;\n",
- "difference_8: -8;\n",
- "difference_9: -15;\n",
- "difference_10: 1;\n"
- ]
- }
- ],
- "source": [
- "for i, difference in enumerate(differences, start=1):\n",
- " print(f'difference_{i}: {difference};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "При несоответствии размеров итерируемых объектов, map обрежет все до длины кратчайшей последовательности. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 108,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "difference_0: 14;\n",
- "difference_1: -2;\n",
- "difference_2: -2;\n",
- "difference_3: 2;\n",
- "difference_4: -10;\n"
- ]
- }
- ],
- "source": [
- "for i, diff in enumerate(map(lambda x, y: x - y, vector1, vector2[:5])):\n",
- " print(f'difference_{i}: {diff};')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### zip()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "zip позволяет получит итератор i-ый элементы которого - кортеж, содержащий i-е элементы переданных итерируемых объектов. При несоответствии длин итерируемых объектов, zip ведет себя подобно map, обрезая все по длине кратчайшей последовательности. \n",
- "\n",
- "Использование zip может быть проиллюстрировано вычислением манхеттенского расстояния между векторами:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 110,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "vector1: [7, 9, -4, -4, 4, -4, 0, -3, -4, -2];\n",
- "vector2: [-6, 7, 3, -4, -8, 3, -6, 2, 3, -3];\n"
- ]
- }
- ],
- "source": [
- "from random import randint\n",
- "\n",
- "\n",
- "vector1 = [randint(-10, 10) for _ in range(10)]\n",
- "vector2 = [randint(-10, 10) for _ in range(10)]\n",
- "\n",
- "print(\n",
- " f'vector1: {vector1};',\n",
- " f'vector2: {vector2};',\n",
- " sep='\\n'\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 111,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "distance = 60\n"
- ]
- }
- ],
- "source": [
- "distance = sum(abs(x_1 - x_2) for x_1, x_2 in zip(vector1, vector2))\n",
- "\n",
- "print(f'{distance = }')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/lessons/lesson9/sem9_312/1. context_mgr.py b/lessons/lesson9/sem9_312/1. context_mgr.py
deleted file mode 100644
index f703acd1..00000000
--- a/lessons/lesson9/sem9_312/1. context_mgr.py
+++ /dev/null
@@ -1,30 +0,0 @@
-""" Реализация своего контекстного менеджера.
-
- Пусть в начале работы файла задаётся точность вычислений с
- плавающей точкой (например, 3 знака после запятой)
-
- И пусть где-то по ходу выполнения программы нам необходимо
- повысить точность вычислений (например, до 6-ти знаков после запятой)
-
- Как это можно сделать?
-"""
-
-from contextlib import contextmanager # для создания собственного контекстного менеджера
- # можно использовать декоратор @contextmanager
-from decimal import Decimal, getcontext
-
-
-# пример регулировки точности вычислений
-getcontext().prec = 3
-
-result = Decimal('3') / Decimal('9')
-print(result)
-
-print("current precise:", getcontext().prec)
-
-getcontext().prec = 6
-
-result = Decimal('3') / Decimal('9')
-print(result)
-
-print("current precise:", getcontext().prec)
diff --git a/lessons/lesson9/sem9_312/2. geom_progression.py b/lessons/lesson9/sem9_312/2. geom_progression.py
deleted file mode 100644
index ab003907..00000000
--- a/lessons/lesson9/sem9_312/2. geom_progression.py
+++ /dev/null
@@ -1,7 +0,0 @@
-""" ГЕОМЕТРИЧЕСКАЯ ПРОГРЕССИЯ """
-
-# Напишите бесконечный генератор геометрической прогрессии.
-# В качестве параметров генератор должен принимать:
-# - первый член прогрессии
-# - шаг прогрессии
-
diff --git a/lessons/lesson9/sem9_312/3. float_range.py b/lessons/lesson9/sem9_312/3. float_range.py
deleted file mode 100644
index 2403db06..00000000
--- a/lessons/lesson9/sem9_312/3. float_range.py
+++ /dev/null
@@ -1,7 +0,0 @@
-""" генератор range для чисел с плавающей точкой """
-
-# Напишите генератор - аналог range, который генерирует арифметическую
-# прогрессию, принимая 1, 2 или аргумента:
-# - stop: последний (недостигаемый) член прогрессии
-# - start: первый член прогрессии (по умолчанию равен нулю)
-# - step: шаг арифметической прогрессии (по умолчанию равен 1)
\ No newline at end of file
diff --git a/lessons/lesson9/sem9_312/4.1 my_map.py b/lessons/lesson9/sem9_312/4.1 my_map.py
deleted file mode 100644
index c76d36b4..00000000
--- a/lessons/lesson9/sem9_312/4.1 my_map.py
+++ /dev/null
@@ -1,22 +0,0 @@
-""" Собственная реализация map() """
-
-# примеры работы map()
-vector1 = [3, 7, -2, 9, -3, 6]
-squares = map(lambda x: x**2, vector1)
-for num, sq in zip(vector1, squares):
- print(num, ":", sq, sep="\t")
-
-print("".center(20, "-"))
-
-vector2 = [3, -1, 0, 4]
-prods = map(lambda x, y: x * y, vector1, vector2)
-for num1, num2, pr in zip(vector1, vector2, prods):
- width = 2
- num1 = str(num1).center(width)
- num2 = str(num2).center(width)
- pr = str(pr).center(width)
- print(f"{num1} * {num2} = {pr}")
-
-
-# Напишите функцию my_map, которая будет возвращать генератор
-# результатов применения функции func к итерируемым объектам
diff --git a/lessons/lesson9/sem9_312/4.2 my_map_advanced.py b/lessons/lesson9/sem9_312/4.2 my_map_advanced.py
deleted file mode 100644
index fca347a3..00000000
--- a/lessons/lesson9/sem9_312/4.2 my_map_advanced.py
+++ /dev/null
@@ -1,15 +0,0 @@
-""" управляемый map """
-
-from enum import Enum
-from itertools import zip_longest
-
-
-class MapTypes(Enum):
- SHORTEST = 'short'
- LONGEST = 'long'
-
-# Напишите функцию my_map() с дополнительными именованными аргументами 'type' и 'fill_value'.
-# Если 'type' равен MapTypes.SHORTEST, то my_map должен работать как и встроенный map, обрезая
-# все пришедшие коллекции по длине самой короткой коллекции.
-# Если 'type' равен MapTypes.LONGEST, то все коллекции расширяются до длины самой длинной
-# коллекции, а недостающие значения заоплняются значением 'fill_value'.
diff --git a/lessons/lesson9/sem9_312/5.1 circle_generator.py b/lessons/lesson9/sem9_312/5.1 circle_generator.py
deleted file mode 100644
index 09b01bf5..00000000
--- a/lessons/lesson9/sem9_312/5.1 circle_generator.py
+++ /dev/null
@@ -1,17 +0,0 @@
-""" Генератор, выдающий всю коллекцию бесконечно по кругу """
-
-# Напишите функцию, которая на вход получает коллекцию и возвращает
-# генератор, последовательно возвращающий элементы коллекции,
-# а после возврата последнего элемента коллекции последующий
-# вызов генератора вернёт первый элемент коллекции, второй, и т.д.
-#
-# Пример:
-# chars = ['a', 'b', 'c']
-# generator_chars = circ_generator(chars)
-# print(next(generator_chars)) # 'a'
-# print(next(generator_chars)) # 'b'
-# print(next(generator_chars)) # 'c'
-# print(next(generator_chars)) # 'a'
-# print(next(generator_chars)) # 'b'
-# print(next(generator_chars)) # 'c'
-# print(next(generator_chars)) # 'a'
\ No newline at end of file
diff --git a/lessons/lesson9/sem9_312/5.2 wheel.py b/lessons/lesson9/sem9_312/5.2 wheel.py
deleted file mode 100644
index 481018bd..00000000
--- a/lessons/lesson9/sem9_312/5.2 wheel.py
+++ /dev/null
@@ -1,25 +0,0 @@
-""" Реализация спиннера.
-
- Интересная статья на тему индикаторов: https://dtf.ru/flood/174240-progress-bar-ili-spinner-chto-i-kogda-ispolzovat?ysclid=lorrg51syv550654720
- Возможно, вам поможет circle_generator...
-"""
-from typing import Iterable
-from time import sleep, time
-
-def wheel(time_limit: float, pause: float):
- """ Отрисовка спиннера.
-
- Печатает на экран надпись: 'Thinking: ',
- где вместо последовательно появляются знаки: \, |, /, -,
- что создаёт эффект вращения.
-
- Вход:
- time_limit: float
- время (в секундах), в течение которого должна производиться отрисовка спиннера
- pause: float
- время (в секундах) задержки между сменой символов спиннера
-
- Выход:
- None
- """
- pass
\ No newline at end of file
diff --git a/lessons/lesson9/sem9_312/tasks.ipynb b/lessons/lesson9/sem9_312/tasks.ipynb
deleted file mode 100644
index f0c00e10..00000000
--- a/lessons/lesson9/sem9_312/tasks.ipynb
+++ /dev/null
@@ -1,401 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "b0b60028",
- "metadata": {},
- "source": [
- "# Реализация своего контекстного менеджера\n",
- "\n",
- "Пусть в начале работы файла задаётся точность вычислений с \n",
- "плавающей точкой (например, 3 знака после запятой)\n",
- "\n",
- "И пусть где-то по ходу выполнения программы нам необходимо\n",
- "повысить точность вычислений (например, до 6-ти знаков после запятой)\n",
- "\n",
- "Как это можно сделать?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "2ee4bd58",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0.333\n",
- "current precise: 3\n",
- "0.333333\n",
- "current precise: 6\n"
- ]
- }
- ],
- "source": [
- "from contextlib import contextmanager # для создания собственного контекстного менеджера\n",
- " # можно использовать декоратор @contextmanager\n",
- "from decimal import Decimal, getcontext\n",
- "\n",
- "\n",
- "# пример регулировки точности вычислений\n",
- "getcontext().prec = 3\n",
- "\n",
- "result = Decimal('3') / Decimal('9') \n",
- "print(result)\n",
- "\n",
- "print(\"current precise:\", getcontext().prec)\n",
- "\n",
- "getcontext().prec = 6\n",
- "\n",
- "result = Decimal('3') / Decimal('9') \n",
- "print(result)\n",
- "\n",
- "print(\"current precise:\", getcontext().prec)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "33ce868a",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0e586459",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "47c5cd4e",
- "metadata": {},
- "source": [
- "# Геометрическая прогрессия\n",
- "\n",
- "Напишите бесконечный генератор геометрической прогрессии.\n",
- "В качестве параметров генератор должен принимать: \n",
- "- первый член прогрессии\n",
- "- шаг прогрессии"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a1dbd960",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1322283c",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "1b317e5a",
- "metadata": {},
- "source": [
- "# Генератор range для чисел с плавающей точкой\n",
- "\n",
- "Напишите генератор - аналог range, который генерирует арифметическую\n",
- "прогрессию, принимая 1, 2 или аргумента: \n",
- "- stop: последний (недостигаемый) член прогрессии\n",
- "- start: первый член прогрессии (по умолчанию равен нулю) \n",
- "- step: шаг арифметической прогрессии (по умолчанию равен 1) "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1eddc515",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c118dd7f",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "0e50efe0",
- "metadata": {},
- "source": [
- "# Собственная реализация map()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "f5851e10",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "3\t:\t9\n",
- "7\t:\t49\n",
- "-2\t:\t4\n",
- "9\t:\t81\n",
- "-3\t:\t9\n",
- "6\t:\t36\n",
- "--------------------\n",
- "3 * 3 = 9 \n",
- "7 * -1 = -7\n",
- "-2 * 0 = 0 \n",
- "9 * 4 = 36\n"
- ]
- }
- ],
- "source": [
- "# примеры работы map()\n",
- "vector1 = [3, 7, -2, 9, -3, 6]\n",
- "squares = map(lambda x: x**2, vector1)\n",
- "for num, sq in zip(vector1, squares):\n",
- " print(num, \":\", sq, sep=\"\\t\")\n",
- "\n",
- "print(\"\".center(20, \"-\"))\n",
- "\n",
- "vector2 = [3, -1, 0, 4]\n",
- "prods = map(lambda x, y: x * y, vector1, vector2)\n",
- "for num1, num2, pr in zip(vector1, vector2, prods):\n",
- " width = 2\n",
- " num1 = str(num1).center(width)\n",
- " num2 = str(num2).center(width)\n",
- " pr = str(pr).center(width)\n",
- " print(f\"{num1} * {num2} = {pr}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "90231b0d",
- "metadata": {},
- "source": [
- "Напишите функцию `my_map`, которая будет возвращать генератор\n",
- "результатов применения функции func к итерируемым объектам"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "121c0d82",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "577e35fc",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "de56aa9b",
- "metadata": {},
- "source": [
- "## Сделайте map управляемым\n",
- "\n",
- "Напишите функцию `my_map()` с дополнительными именованными аргументами 'type' и 'fill_value'. \n",
- "\n",
- "Если 'type' равен MapTypes.SHORTEST, то my_map должен работать как и встроенный map, обрезая все пришедшие коллекции по длине самой короткой коллекции. \n",
- "\n",
- "Если 'type' равен MapTypes.LONGEST, то все коллекции расширяются до длины самой длинной коллекции, а недостающие значения заоплняются значением 'fill_value'."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "0f930faa",
- "metadata": {},
- "outputs": [],
- "source": [
- "from enum import Enum\n",
- "from itertools import zip_longest\n",
- "\n",
- "\n",
- "class MapTypes(Enum):\n",
- " SHORTEST = 'short'\n",
- " LONGEST = 'long'\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "49b53ae5",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d9654d69",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "1ef76164",
- "metadata": {},
- "source": [
- "# Циклический генератор"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a79e62a0",
- "metadata": {},
- "source": [
- "Напишите функцию, которая на вход получает коллекцию и возвращает генератор, последовательно возвращающий элементы коллекции, а после возврата последнего элемента коллекции последующий вызов генератора вернёт первый элемент коллекции, второй, и т.д.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "5b1d7d33",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Пример:\n",
- "# chars = ['a', 'b', 'c']\n",
- "# generator_chars = circ_generator(chars)\n",
- "# print(next(generator_chars)) # 'a'\n",
- "# print(next(generator_chars)) # 'b'\n",
- "# print(next(generator_chars)) # 'c'\n",
- "# print(next(generator_chars)) # 'a'\n",
- "# print(next(generator_chars)) # 'b'\n",
- "# print(next(generator_chars)) # 'c'\n",
- "# print(next(generator_chars)) # 'a'"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "dbb6dcda",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "2552189f",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "id": "7eff66b3",
- "metadata": {},
- "source": [
- "## Реализуйте свой спиннер:\n",
- "\n",
- "Интересная статья на тему индикаторов: https://dtf.ru/flood/174240-progress-bar-ili-spinner-chto-i-kogda-ispolzovat?ysclid=lorrg51syv550654720"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "eb84de3b",
- "metadata": {},
- "outputs": [],
- "source": [
- "def wheel(time_limit: float, pause: float):\n",
- " \"\"\" Отрисовка спиннера.\n",
- "\n",
- " Печатает на экран надпись: 'Thinking: ',\n",
- " где вместо последовательно появляются знаки: \\, |, /, -, \n",
- " что создаёт эффект вращения.\n",
- "\n",
- " Вход:\n",
- " time_limit: float\n",
- " время (в секундах), в течение которого должна производиться отрисовка спиннера\n",
- " pause: float\n",
- " время (в секундах) задержки между сменой символов спиннера\n",
- " \n",
- " Выход:\n",
- " None\n",
- " \"\"\"\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "46962e8b",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "578fa172",
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9eab6768",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson9/sem9_313/tasks.ipynb b/lessons/lesson9/sem9_313/tasks.ipynb
deleted file mode 100644
index 1a264615..00000000
--- a/lessons/lesson9/sem9_313/tasks.ipynb
+++ /dev/null
@@ -1,307 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Функции: избранные темы"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b0b60028",
- "metadata": {},
- "source": [
- "## Вступление: контекстные менеджеры\n",
- "\n",
- "В Python по умолчанию задана некоторая точность вычисления чисел с плавающей точкой, которая, что логично, может отличаться от требуемой для решения конкретной задачи точности. Для пример, будет рассматривать точность вычислений в библиотеке **decimal**.\n",
- "\n",
- "Предположим, нас устраивает точность вычислений по умолчанию в этой библиотеке, но для некоторых расчетов мы бы хотели использовать повышенную или пониженную точность и только для них. Как нам быть? Тут-то нам на помощь и придут контекстные менеджеры.\n",
- "\n",
- "В этом примере мы поговорим о контекстных менеджерах и напишем свой менеджер для установки точности вычислений, которые будет работать примерно так:\n",
- "\n",
- "```python\n",
- "with set_precision(5):\n",
- " ...\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "2ee4bd58",
- "metadata": {},
- "outputs": [],
- "source": [
- "from decimal import Decimal, getcontext\n",
- "from contextlib import contextmanager\n",
- "\n",
- "\n",
- "# наш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "47c5cd4e",
- "metadata": {},
- "source": [
- "## Разминка: геометрическая прогрессия\n",
- "\n",
- "Напишите бесконечный генератор геометрической прогрессии. В качестве параметров генератор должен принимать: \n",
- "- первый член прогрессии\n",
- "- шаг прогрессии"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "1322283c",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1b317e5a",
- "metadata": {},
- "source": [
- "## Задание 1: float_range\n",
- "\n",
- "Необходимо создать аналог range(), который генерирует арифметическую прогрессию. Ниже приведены примеры использования:\n",
- "\n",
- "*Пример 1*:\n",
- "\n",
- "```python\n",
- "for i in float_range(stop=5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "0\n",
- "1\n",
- "2\n",
- "3\n",
- "4\n",
- "```\n",
- "___\n",
- "*Пример 2*:\n",
- "\n",
- "```python\n",
- "for i in float_range(stop=1, step=0.5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "0\n",
- "0.5\n",
- "```\n",
- "___\n",
- "*Пример 3*:\n",
- "\n",
- "```python\n",
- "for i in float_range(start=1, stop=-1, step=-0.5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "1\n",
- "0.5\n",
- "0\n",
- "-0.5\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "c118dd7f",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0e50efe0",
- "metadata": {},
- "source": [
- "## Задание 2: свой map"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "90231b0d",
- "metadata": {},
- "source": [
- "### Часть 1: копируем map\n",
- "\n",
- "Реализуйте аналог функции map, полностью копирующий ее поведение. Саму map использовать нельзя."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "121c0d82",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "577e35fc",
- "metadata": {},
- "source": [
- "### Часть 2: дополняем map\n",
- "\n",
- "Добавьте возможность управлять поведением вашего map'a: сделайте так, чтобы map имела возможность не только обрезать последовательности, но и дополнять короткие последовательности до динных. \n",
- "\n",
- "Совет: функция **zip_longest** из библиотеки **itertools** может оказаться полезной."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "0f930faa",
- "metadata": {},
- "outputs": [],
- "source": [
- "from enum import Enum\n",
- "from itertools import zip_longest\n",
- "\n",
- "\n",
- "class MapTypes(Enum):\n",
- " SHORTEST = 'short'\n",
- " LONGEST = 'long'\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "49b53ae5",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1ef76164",
- "metadata": {},
- "source": [
- "## Задание 3: Спиннер"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a79e62a0",
- "metadata": {},
- "source": [
- "### Часть 1: генератор\n",
- "\n",
- "Напишите функцию, которая на вход получает коллекцию и возвращает генератор, последовательно возвращающий элементы коллекции, а после возврата последнего элемента коллекции очередной вызов генератора приведет к зацикливанию.\n",
- "\n",
- "\n",
- "*Пример*:\n",
- "\n",
- "```python\n",
- "generator = generate_circle('abc')\n",
- "\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "```\n",
- "*Вывод*:\n",
- "```console\n",
- "a\n",
- "b\n",
- "c\n",
- "a\n",
- "b\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "5b1d7d33",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7eff66b3",
- "metadata": {},
- "source": [
- "### Часть 2: Колесо Сансары\n",
- "\n",
- "Используя генератор из предыдущего раздела, реализуйте функцию, которая отображает на экране спиннер для индикации загрузки.\n",
- "\n",
- "Отрисовка спиннера печатает на экран надпись: Thinking: \\, где вместо \\ последовательно появляются знаки: \\, |, /, -, что создаёт эффект вращения.\n",
- "\n",
- "Вход функции: \n",
- "- time_limit - время (в секундах), в течение которого должна производиться отрисовка спиннера;\n",
- "- pause - время (в секундах) задержки между сменой символов спиннера;\n",
- "\n",
- "Интересная статья на тему индикаторов: https://dtf.ru/flood/174240-progress-bar-ili-spinner-chto-i-kogda-ispolzovat?ysclid=lorrg51syv550654720"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "eb84de3b",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/lesson9/sem9_314/tasks.ipynb b/lessons/lesson9/sem9_314/tasks.ipynb
deleted file mode 100644
index 1a264615..00000000
--- a/lessons/lesson9/sem9_314/tasks.ipynb
+++ /dev/null
@@ -1,307 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Функции: избранные темы"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b0b60028",
- "metadata": {},
- "source": [
- "## Вступление: контекстные менеджеры\n",
- "\n",
- "В Python по умолчанию задана некоторая точность вычисления чисел с плавающей точкой, которая, что логично, может отличаться от требуемой для решения конкретной задачи точности. Для пример, будет рассматривать точность вычислений в библиотеке **decimal**.\n",
- "\n",
- "Предположим, нас устраивает точность вычислений по умолчанию в этой библиотеке, но для некоторых расчетов мы бы хотели использовать повышенную или пониженную точность и только для них. Как нам быть? Тут-то нам на помощь и придут контекстные менеджеры.\n",
- "\n",
- "В этом примере мы поговорим о контекстных менеджерах и напишем свой менеджер для установки точности вычислений, которые будет работать примерно так:\n",
- "\n",
- "```python\n",
- "with set_precision(5):\n",
- " ...\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "2ee4bd58",
- "metadata": {},
- "outputs": [],
- "source": [
- "from decimal import Decimal, getcontext\n",
- "from contextlib import contextmanager\n",
- "\n",
- "\n",
- "# наш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "47c5cd4e",
- "metadata": {},
- "source": [
- "## Разминка: геометрическая прогрессия\n",
- "\n",
- "Напишите бесконечный генератор геометрической прогрессии. В качестве параметров генератор должен принимать: \n",
- "- первый член прогрессии\n",
- "- шаг прогрессии"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "1322283c",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1b317e5a",
- "metadata": {},
- "source": [
- "## Задание 1: float_range\n",
- "\n",
- "Необходимо создать аналог range(), который генерирует арифметическую прогрессию. Ниже приведены примеры использования:\n",
- "\n",
- "*Пример 1*:\n",
- "\n",
- "```python\n",
- "for i in float_range(stop=5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "0\n",
- "1\n",
- "2\n",
- "3\n",
- "4\n",
- "```\n",
- "___\n",
- "*Пример 2*:\n",
- "\n",
- "```python\n",
- "for i in float_range(stop=1, step=0.5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "0\n",
- "0.5\n",
- "```\n",
- "___\n",
- "*Пример 3*:\n",
- "\n",
- "```python\n",
- "for i in float_range(start=1, stop=-1, step=-0.5):\n",
- " print(i)\n",
- "```\n",
- "\n",
- "*Вывод*:\n",
- "```console\n",
- "1\n",
- "0.5\n",
- "0\n",
- "-0.5\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "c118dd7f",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0e50efe0",
- "metadata": {},
- "source": [
- "## Задание 2: свой map"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "90231b0d",
- "metadata": {},
- "source": [
- "### Часть 1: копируем map\n",
- "\n",
- "Реализуйте аналог функции map, полностью копирующий ее поведение. Саму map использовать нельзя."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "121c0d82",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "577e35fc",
- "metadata": {},
- "source": [
- "### Часть 2: дополняем map\n",
- "\n",
- "Добавьте возможность управлять поведением вашего map'a: сделайте так, чтобы map имела возможность не только обрезать последовательности, но и дополнять короткие последовательности до динных. \n",
- "\n",
- "Совет: функция **zip_longest** из библиотеки **itertools** может оказаться полезной."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "0f930faa",
- "metadata": {},
- "outputs": [],
- "source": [
- "from enum import Enum\n",
- "from itertools import zip_longest\n",
- "\n",
- "\n",
- "class MapTypes(Enum):\n",
- " SHORTEST = 'short'\n",
- " LONGEST = 'long'\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "49b53ae5",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1ef76164",
- "metadata": {},
- "source": [
- "## Задание 3: Спиннер"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a79e62a0",
- "metadata": {},
- "source": [
- "### Часть 1: генератор\n",
- "\n",
- "Напишите функцию, которая на вход получает коллекцию и возвращает генератор, последовательно возвращающий элементы коллекции, а после возврата последнего элемента коллекции очередной вызов генератора приведет к зацикливанию.\n",
- "\n",
- "\n",
- "*Пример*:\n",
- "\n",
- "```python\n",
- "generator = generate_circle('abc')\n",
- "\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "print(next(generator))\n",
- "```\n",
- "*Вывод*:\n",
- "```console\n",
- "a\n",
- "b\n",
- "c\n",
- "a\n",
- "b\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "5b1d7d33",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7eff66b3",
- "metadata": {},
- "source": [
- "### Часть 2: Колесо Сансары\n",
- "\n",
- "Используя генератор из предыдущего раздела, реализуйте функцию, которая отображает на экране спиннер для индикации загрузки.\n",
- "\n",
- "Отрисовка спиннера печатает на экран надпись: Thinking: \\, где вместо \\ последовательно появляются знаки: \\, |, /, -, что создаёт эффект вращения.\n",
- "\n",
- "Вход функции: \n",
- "- time_limit - время (в секундах), в течение которого должна производиться отрисовка спиннера;\n",
- "- pause - время (в секундах) задержки между сменой символов спиннера;\n",
- "\n",
- "Интересная статья на тему индикаторов: https://dtf.ru/flood/174240-progress-bar-ili-spinner-chto-i-kogda-ispolzovat?ysclid=lorrg51syv550654720"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "eb84de3b",
- "metadata": {},
- "outputs": [],
- "source": [
- "# ваш код"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.1"
- },
- "toc": {
- "base_numbering": 1,
- "nav_menu": {},
- "number_sections": true,
- "sideBar": true,
- "skip_h1_title": false,
- "title_cell": "Table of Contents",
- "title_sidebar": "Contents",
- "toc_cell": false,
- "toc_position": {},
- "toc_section_display": true,
- "toc_window_display": false
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/lessons/sem01/lesson01/git_cheat_sheet.md b/lessons/sem01/lesson01/git_cheat_sheet.md
new file mode 100644
index 00000000..f0dba21e
--- /dev/null
+++ b/lessons/sem01/lesson01/git_cheat_sheet.md
@@ -0,0 +1,260 @@
+# Введение в Git
+
+**Содержание**:
+- [Что такое системы контроля версий?](#что-такое-системы-контроля-версий)
+- [Разновидности систем контроля версий](#разновидности)
+- [Что такое репозиторий?](#что-такое-репозиторий)
+- [Структура проекта Git](#структура-проекта-git)
+- [Основы работы с репозиторием](#основы-работы-с-репозиторием)
+- [Как Git хранит данные](#как-git-хранит-данные)
+- [Что такое ветка в git?](#что-такое-ветка-в-git)
+- [История коммитов](#история-коммитов)
+- [Назад в прошлое](#назад-в-прошлое)
+- [Работа с удаленными репозиториями](#работа-с-удаленными-репозиториями)
+
+## Что такое системы контроля версий?
+
+**Система контроля версий** (англ. *Version Control System*, сокращенно *VCS*) — это система, записывающая изменения в файл или набор файлов в течение времени и позволяющая вернуться позже к определённой версии. Важно отметить, что речь идет о любых файлах, а не только о файлах с программным кодом, поэтому, при необходимости, системы контроля версий можно использовать для работы над любыми проектами.
+
+[[К содержанию](#введение-в-git)]
+
+## Разновидности
+
+Существуют следующие виды VCS:
+- **Локальные VCS**. Представляют собой связку программы для отслеживания изменений файлов с простой базой данных для хранения изменений.
+
+ **Сильные стороны**:
+ - Решение проблем версионирования
+
+ **Слабые стороны**:
+ - Невозможность командной работы над проектом
+ - При критической поломке компьютера вы теряете ваш проект со всей историей изменений
+
+- **Централизованные VCS**. Следующий этап развития систем контроля версий, призванный устранить недостатки локальных VCS. Централизованные VCS представляют собой связку программы для отслеживания изменений файлов и базу данных, расположенную на удаленном сервере. После внесения изменений разработчики отправляют их на сервер, чтобы другие разработчики имели возможность работать с актуальной версией проекта. Актуальную версию проекта разработчики также получают от сервера.
+
+ **Сильные стороны**:
+ - Возможность командной работы над проектом
+ - Легкость администрирования проекта
+
+ **Слабые стороны**:
+ - Единая точка отказа. Если сервер с базой данных упадет на некоторое время, разработчики потеряют возможность вносить и получать изменения. Если сервер выйдет из строя, то вся история изменений будет потеряна.
+
+- **Распределенные VCS**. Распределенные VCS объединяют в себе сильные стороны локальных и централизованных VCS. Как и в случае с локальными VCS, распределенные VCS организуют на стороне клиента хранение полной истории изменений, а также позволяют обмениваться данными через удаленный сервер, как это было с централизованными VCS. Однако, в отличие от централизованных VCS, в случае с отказом сервера разработчики имеют возможность продолжать работу с проектом путем сохранения изменений в локальную историю проекта. После устранения проблем с удаленным сервером вся локальная история с актуальными изменениями может быть отправлена на сервер. В случае же выхода сервера из строя данные не будут потеряны, поскольку на стороне каждого разработчика хранится полная история изменений проекта.
+
+ **Сильные стороны:**
+ - Решение проблем версионирования
+ - Возможность командной работы над проектом
+ - Высокая отказоустойчивость системы
+
+Git является распределенной системой контроля версий.
+
+[[К содержанию](#введение-в-git)]
+
+## Что такое репозиторий?
+
+**Репозиторий Git** - это хранилище изменений вашего проекта и сопутствующих метаданных. Все эти данные хранятся в каталоге `.git`.
+
+Репозиторий Git может быть создан двумя способами:
+
+- Создан с нуля на основе текущего проекта. Для этого используется команда:
+ ```console
+ git init
+ ```
+
+- Скопирован с удаленного сервера с помощью команды:
+ ```console
+ git clone [folder_name]
+ ```
+
+[[К содержанию](#введение-в-git)]
+
+## Структура проекта Git
+
+Проект, находящийся под контролем git, состоит из трех основных секций:
+
+- **Рабочая копия (working tree)** - это одна из версий проекта, которая была извлечена из сжатой базы данных каталога `.git`, разжата и помещена на диск. Рабочая копия содержит файлы, в которые вы можете вносить изменения.
+
+- **Область индексирования (index)** - это специальный файл, хранящийся в каталоге `.git`, который содержит информацию об изменениях, попадущих в базу изменений во время следующего коммита.
+
+- **Каталог .git** - место хранения всех изменений и метаданных проекта. Этот каталог - самая важная часть проекта Git, которая будет передаваться при клонировании репозитория и работе с удаленным сервером.
+
+[[К содержанию](#введение-в-git)]
+
+## Основы работы с репозиторием
+
+Все файлы, которые хранятся в проекте под системой контроля версий git, могут находиться в одном из 4 состояний:
+
+- **Untracked** - неотслеживаемые файлы. Это новые файлы, о которых у git пока нет никакой информации.
+- **Modified** - измененные файлы. Это файлы, которые были изменены, и изменения которых не были зафиксированы.
+- **Added** - индексированные файлы. Это файлы, которые были добавлены в индекс, но не были закоммичены.
+- **Commited** - файлы, изменения которых были зафиксированы, т.е. сохранены в локальной базе изменений.
+
+Для того, чтобы просмотреть статус файлов в вашем репозитории, необходимо выполнить команду:
+```console
+git status
+```
+
+Вывод этой команды является очень подробным. Чтобы опустить подробности, воспользуйтесь командой:
+```console
+git status -s
+```
+
+Пример результата выполнения этой команды:
+```console
+ M create_lesson.py
+ M scripts_utils/lesson_template.py
+?? lessons/
+```
+
+Ведущая буква M указывает на то, что файл был изменен. Ведущие знаки вопроса, говорят нам, что файл является неотслеживаемым.
+
+Чтобы добавить, как неотслеживаемые, так и измененные файлы в индекс, необходимо выполнить команду:
+```console
+git add ...
+```
+
+В случае, если вы хотите проиндексировать сразу все изменения, выполните команду:
+```console
+git add .
+```
+
+Чтобы зафиксировать индексированные изменения, необходимо выполнить команду:
+```console
+git commit -m "Add file_name_1 with very useful algo"
+```
+
+**Важно**: следите за тем, чтобы сообщение коммита (текущего изменения) начиналось с заглавной буквы, было информативным, а также являлось ответом на вопрос: *Что делает этот коммит*?
+
+**Пример, как надо**:
+```console
+git commit -m "Add new log format to also track actions's datetime"
+```
+
+ - What does this commit do?
+ - Add new log format to also track actions's datetime
+
+**Примеры, как не надо**:
+
+- Сообщение коммита начинается не с заглавной буквы:
+ ```console
+ git commit -m "add new log format to also track actions's datetime"
+ ```
+
+- Сообщение коммита неинформативно:
+ ```console
+ git commit -m "Update files"
+ ```
+
+- Сообщение коммта не является ответом на вопрос *Что делает этот коммит?*:
+ ```console
+ git commit -m "New log format for tracking actions's datetime"
+ ```
+
+[[К содержанию](#введение-в-git)]
+
+## Как Git хранит данные?
+
+Git хранит данные в виде последовательности снимков. Предположим, что у нас есть проект со следующей структурой:
+
+```console
+├───A1
+├───A2
+```
+
+Т.е. наш проект состоит всего из двух файлов: A1 и A2.
+
+Во время индексации (выполнение команды `git add`) Git вычислит контрольную сумму каждого файла ([SHA-1](https://ru.wikipedia.org/wiki/SHA-1) хеш), добавит файлы в репозиторий, а хеш - в файл индекса. Все файлы сохраняются в репозиторий в виде бинарных файлов и называются *большими бинарными файлами* или *блобами*.
+
+Во время коммита (выполнение команды `git commit`) Git вычисляет контрольные суммы каждого каталога (в нашем случае, только корневого каталога) и сохраняет все в репозиторий в виде объекта дерева каталогов. Этот объект и называется снимком. Затем Git создает объект коммита, который содержит указатель на дерево и метаданные. Это нужно, чтобы иметь возможность восстановить данные, соответствующие определенной версии.
+
+Возвращаясь к нашему примеру, после выполнения коммита, наш репозиторий будет хранить 4 объекта: два блоба - по одному для каждого файла проекта, объект дерева каталогов, содержащий список файлов и соответствующих им блобов, а так же объект коммита, содержащий метаданные и указатель на объект дерева каталогов.
+
+Если вы внесете изменения в ваши файлы и закоммитите их, то новый объект коммита также будет содержать указатель на предыдущий объект коммита.
+
+[[К содержанию](#введение-в-git)]
+
+## Что такое ветка в Git?
+
+Ветка в Git - это просто указатель на один из объектов коммитов в дереве коммитов. По умолчанию в вашем проекте есть основная ветка с именем `master`. После добавления новых коммитов в ветку указатель будет автоматически перемещаться на последний коммит.
+
+Поскольку Git рассчитан на одновременную работу с несколькими ветками, неясно, как Git определяет с какой веткой работает пользователь? Для этих целей в Git существует специальный указатель `HEAD` - указатель на текущую локальную ветку. При переключении ветки Git просто перемещает этот указатель с одной цепочки коммитов на другую.
+
+Теория работы с ветками будет изложена подробнее в других курсах. В рамках данного курса от вас не трубется знания ветвления.
+
+[[К содержанию](#введение-в-git)]
+
+## История коммитов
+
+Для просмотра истории коммитов воспользуйтесь командой:
+```console
+git log
+```
+
+Стандартный вывод команды будет выглядеть следующим образом:
+```console
+commit 5f6f5982322a5145512d31470e4eb8690eba548f (HEAD -> intro-les1, origin/intro-les1)
+Author: EvgrafovMichail
+Date: Sat Aug 10 18:04:09 2024 +0300
+
+ Improve sem_id validation in lesson_template to create lessons for sem_00
+```
+
+Из стандартного вывода команды `git log` можно извлечь следующую информацию:
+- Хеш-сумму коммита, которая также является идентефикатором коммита: 5f6f5982322a5145512d31470e4eb8690eba548f
+- Автора коммита: Author: EvgrafovMichail \
+- Дату и время коммита: Date: Sat Aug 10 18:04:09 2024 +0300
+- Сообщение коммита: Improve sem_id validation in lesson_template to create lessons for sem_00
+
+Однако, вы всегда можете настроить формат вывода истории коммитов под свои нужды, прочитав документацию.
+
+[[К содержанию](#введение-в-git)]
+
+## Назад в прошлое
+
+Все мы люди, а значит время от времени мы совершаем ошибки. Одной из таких ошибок может быть добавление неверных изменений или автоматически генерируемых файлов в репозиторий. Однако, посокльку Git является системой контроля версий, вы всегда можете вернуться к нужной версии проекта, откатив нежелательные изменения.
+
+Для того, чтобы вернуться к определенной версии проекта, используйте команду:
+```console
+git reset
+```
+
+**Важно**: команду стоит использовать только в том случае, если над данной веткой проекта работаете только вы. После применения команды история ветки будет переписана, а значит, вы можете причинить много неудобств человеку, который работал над данной веткой вместе с вами.
+
+Команды `git reset` имеет три возможных режима отката измений:
+
+- `--soft` - позволяет откатиться до указанного коммита с сохранением добавленных изменений в индексе.
+- `--mixed` (настройка по умолчанию) - позволяет откатиться до указанного коммита, изменения останутся в файлах, но не будут внесены в индекс (перед коммитом придется выполнять команду `git add`).
+- `--hard` - позволяет откатиться до указанного коммита, все внесенные изменения будут утеряны. Перед выполненим `git reset` с этой опцией, хорошенько подумайте, точно ли вы хотите потерять все внесенные изменения.
+
+[[К содержанию](#введение-в-git)]
+
+## Работа с удаленными репозиториями
+
+Поскольку Git является распределенной системой контроля версий, он поддерживает возможность работы с удаленными репозиториями. Удаленный репозиторий - это репозиторий Git, расположенный не на вашей локальной машине. Примером удаленного репозитория может являться репозиторий, размещенный в GitHub, как, например, репозиторий данного курса.
+
+Для того, чтобы начать работать с удаленными репозиториями, необходимо добавить их в список удаленных репозиториев, с которыми вы осуществляете работу. Сделать это можно командой:
+```console
+git remote add
+```
+
+При клонировании репозитория Git автоматически добавит удаленный репозиторий, который вы клонировали, в список под именем `origin`.
+
+Для отправки изменений в удаленный репозиторий используйте команду:
+```console
+git push
+```
+
+Для скачивания изменений из удаленного репозитория используйте команду:
+```console
+git pull
+```
+
+Для удаления удаленного репозитория из списка удаленных репозиториев для текущего локального репозитория используйте команду:
+```console
+git remote remove
+```
+
+[[К содержанию](#введение-в-git)]
+
+## Источники
+- [Pro Git](https://git-scm.com/book/ru/v2);
diff --git a/lessons/lesson1/lecture1.pptx b/lessons/sem01/lesson01/git_intro.pptx
similarity index 77%
rename from lessons/lesson1/lecture1.pptx
rename to lessons/sem01/lesson01/git_intro.pptx
index d01ed959..8a37ded5 100644
Binary files a/lessons/lesson1/lecture1.pptx and b/lessons/sem01/lesson01/git_intro.pptx differ
diff --git a/lessons/sem01/lesson01/images/course_repo.png b/lessons/sem01/lesson01/images/course_repo.png
new file mode 100644
index 00000000..c8cdf77f
Binary files /dev/null and b/lessons/sem01/lesson01/images/course_repo.png differ
diff --git a/lessons/sem01/lesson01/images/fork.png b/lessons/sem01/lesson01/images/fork.png
new file mode 100644
index 00000000..a879e4a8
Binary files /dev/null and b/lessons/sem01/lesson01/images/fork.png differ
diff --git a/lessons/sem01/lesson01/images/ssh.png b/lessons/sem01/lesson01/images/ssh.png
new file mode 100644
index 00000000..ac69ddcd
Binary files /dev/null and b/lessons/sem01/lesson01/images/ssh.png differ
diff --git a/lessons/sem01/lesson01/images/your_repos.png b/lessons/sem01/lesson01/images/your_repos.png
new file mode 100644
index 00000000..50fb291a
Binary files /dev/null and b/lessons/sem01/lesson01/images/your_repos.png differ
diff --git a/lessons/sem01/lesson01/lintro.pptx b/lessons/sem01/lesson01/lintro.pptx
new file mode 100644
index 00000000..0e94c482
Binary files /dev/null and b/lessons/sem01/lesson01/lintro.pptx differ
diff --git a/lessons/sem01/lesson01/practice.md b/lessons/sem01/lesson01/practice.md
new file mode 100644
index 00000000..57ddac45
--- /dev/null
+++ b/lessons/sem01/lesson01/practice.md
@@ -0,0 +1,217 @@
+# Введение в Git. Практическое занятие.
+
+## Установка Git
+
+Прежде, чем начинать работать с git, необходимо установить git на ваше устройство:
+
+- [Установка git на Windows](https://git-scm.com/download/win)
+- [Установка git на MacOS](https://git-scm.com/download/mac)
+- [Установка git на Linux](https://git-scm.com/download/linux)
+
+После завершения установки в командной строке выполните команду:
+```console
+git --version
+```
+
+Установка прошла успешно, если в результате выполнения команды вы получили сообщение похожего формата:
+```console
+git version 2.42.0.windows.2
+```
+
+## Настройка Git
+
+Прежде чем начинать работу с git вам необходимо сообщить системе контроля версий сведения о себе: имя пользователя и адрес электронной почты. Это важный шаг, поскольку эти данные будут включаться в каждый коммит.
+
+Для настройки имени пользователя и адреса электронной почты выполните следующие команды:
+```console
+git config --global user.name "Your Username"
+git config --global user.email "youremail@mail.ru"
+```
+
+Команда `git config` используется для изменений пользовательских настроек. Флаг `--global` показывает, что вы изменяете системные настройки, которые будут применяться ко всем создаваемым репозиториям. Если вы захотите изменить имя пользователя или адрес электронной почти во время работы над конкретным проектом, вы можете выполнить эти команды без указания флага `--global`. В этом случае выбранные настройки будут применены к конкретному репозиторию. `user.name` - это ключ в конфиге, со значением которого вы намерены работать. `"Your Username"` - строка, которая будет записана в конфиг по ключу `user.name`.
+
+Также может быть полезно изменить текстовый редактор, который будет использоваться для обмена сообщениями с git. Сделать это можно следующим образом:
+
+```console
+git config --global core.editor emacs
+```
+
+Чтобы проверить, что все ваши настройки были применены, выполните команду:
+```console
+git config --list
+```
+
+В выводе вы должны будете увидеть значения, указанные во время настройки. В случае с примерами выше, в выводе должны будут содержаться строки:
+
+```console
+core.editor=emacs
+user.name=Your Username
+user.email=youremail@mail.ru
+```
+
+## Настройка аккаунта в GitHub
+
+Следующий шаг - создать аккаунт на [GitHub](https://github.com/). **GitHub** - это веб-сервис для хостинга IT-проектов и их совместной разработки. Если сильно упростить, то GitHub - это хранилище удаленных репозиториев.
+
+Итак, для начала перейдите по [ссылке](https://github.com/) и создайте аккаунт.
+
+Далее нам необходимо настроить аутентефикацию. При отправке ваших изменений в удаленный репозитория в **GitHub**, система попросит вас предоставить информацию, подтверждающую, что вы - это вы. Это можно сделать несколькими способами, в зависимости от используемого протокола передачи данных. Ниже рассмотрен один из вожмножных способов.
+
+### Публичный и приватный SSH-key
+Если вы намерены использовать SSH в качестве основного протокола для обмена данными с удаленным репозиторием, вам необходимо создать пару SSH-ключей, и привязать публичный ключ к вашему аккаунту в **GitHub**. Сделать это можно следующим образом:
+- Сперва проверьте, а есть ли у вас существующие SSH-ключи. Сделать это можно с помощью следующей команды:
+ ```console
+ ls -al ~/.ssh
+ ```
+ Чтобы выполнить команду на Linux/MacOS, используйте терминал. Чтобы выполнить команду на Windows, используйте Git Bash.
+- Если вывод команды содержит один из следующих файлов:
+ ```console
+ id_rsa.pub
+ id_ecdsa.pub
+ id_ed25519.pub
+ ```
+ значит на вашем компьютере есть пара SSH-ключей и вы можете привязать существующий публичный ключ к своему аккаунту.
+- Если команда завершилась ошибкой, значит на вашем компьбетере нет SSH-ключей и вам придется сгенерировать новый:
+ - Откройте терминал, если вы пользуетесь Unix-подобной ОС. Если вы используете Windows, откройте Git Bash.
+ - Выполните следующую команду для генерации пары SSH-ключей:
+ ```console
+ ssh-keygen -t ed25519 -C "your_email@example.com"
+ ```
+ **ВАЖНО**: не забудьте указать свой адрес электронной почты, который привязан к вашему аккаунту.
+ - Далее система попросит вас ввести путь до файл, в который будет сохранен ключ. Нажмите `ENTER`, чтобы использовать локацию по умолчанию.
+ - Далее система попросит вас ввести секретное слово, ассоциированное со сгенерированным ключом. Нажмите `ENTER`, чтобы не связывать никакого слова с ключом.
+ - Добавим сгенерированный ключ в `ssh-agent`, чтобы система могла автоматически использовать его. Для этого откройте терминал/PowerShell от имени администратора и выполните команду:
+ **Windows**
+ ```console
+ Get-Service -Name ssh-agent | Set-Service -StartupType Manual
+ Start-Service ssh-agent
+ ```
+ **Linux/MacOS**
+ ```console
+ eval "$(ssh-agent -s)"
+ ```
+ - Затем откройте терминал/PowerShell не от имени администратора и введите следующую команду:
+ **Windows**
+ ```console
+ ssh-add c:/Users/YOU/.ssh/id_ed25519
+ ```
+ **Linux**
+ ```console
+ ssh-add ~/.ssh/id_ed25519
+ ```
+ **MacOS**
+ ```console
+ ssh-add --apple-use-keychain ~/.ssh/id_ed25519
+ ```
+ **ВАЖНО**: после `ssh-add` должен следовать путь до вашего приватного SSH-ключа.
+- Теперь привяжем SSH-ключ к нашему аккаунту GitHub. Для того, чтобы это сделать, следуйте [инструкции на официальном сайте](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account?platform=windows).
+
+Также вы можете создать собственный **Acces-token**, чтобы иметь возможность работать с репозиториями через протокол HTTP/HTTPS. Ознакомиться с тем, как создать **Access-token**, можно [тут](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic).
+
+## Клонирование репозитория курса
+
+Теперь, когда мы настроили Git и наш аккаунт на GitHub, время перейти к практике. Начнем с клонирования репозитория:
+
+- Перейдите в [репозиторий курса](https://github.com/EvgrafovMichail/python_mipt_dafe) и выполните форк. Форк - это копия репозитория курса, в которую вы сможете вносить изменения.
+
+ 
+
+- Перейдите в список своих репозиториев:
+
+ 
+
+- Выберете форк репозитория курса:
+
+ 
+
+- Скопируйте репозиторий, используя ssh-ссылку:
+
+ 
+
+- Клонируйте репозиторий, используя следующую команду:
+ ```console
+ git clone
+ ```
+
+Если вы все сделали правильно, то в текущей директории должна появиться папка с файлами курса. Это и будет ваш локальный репозиторий.
+
+## Задача 1
+
+Теперь попробуем создать наш первый коммит:
+
+- В папке `/lessons/sem_01/lesson_01` создайте файл `hello_world.py`
+
+- Добавьте в файл следующий код:
+ ```python
+ if __name__ == "__main__":
+ print("Hello, World!")
+ ```
+
+- Запустите полученный скрипт с помощью команды:
+ ```console
+ python hello_world.py
+ ```
+
+ Или
+
+ ```console
+ python3 hello_world.py
+ ```
+ Скрипт должен напечать приветствие.
+
+**Задание**:
+- С помощью Git просмотрите, в каком состоянии находится созданный нами файл?
+- Добавьте файл в индекс. В каком теперь состоянии находится файл?
+- Закоммитьте файл.
+
+## Задача 2
+
+Внесем изменения в наш скрипт:
+```python
+if __name__ == "__main__":
+ print("Hello, World!")
+ print()
+```
+
+**Задание 1**:
+- В каком состоянии находится файл после внесения изменений?
+- Добавьте файл в индекс.
+
+Погодите. Мы добавили файл в индекс, но забыли добавить сообщение для вывода во втором вызове `print`. Давайте это исправим:
+
+```python
+if __name__ == "__main__":
+ print("Hello, World!")
+ print("This is my first Python script!")
+```
+
+**Задание 2**:
+- В каком состоянии находится файл теперь?
+ Мы наблюдаем странную картину: файл находится одновременно в двух состояних. Git считает файл одновременно проиндексированным и измененным. Как же так? Все дело в том, что Git не делает ничего за вас, чтобы минимизировать ошибки и попадаение ошибочных изменений в репозиторий. Сейчас мы проиндексировали предыдущую версию скрипта с пустым `print`. Эти изменения готовы к коммиту. Новые изменения не попадают в индекс автоматически. Если мы выполним коммит сейчас, то файл с пустым `print` будет сохранен в репозиторий, а текущая версия - нет. Чтобы сохранить текущую версию, нам придется ее сначала проиндексировать, чтобы обновить версию скрипта в индексе.
+
+- Проиндексируйте новую версию скрипта.
+- Сохраните ее в репозиторий.
+
+## Задача 3
+
+Для выполнения следующего задания, внесем изменения в наш скрипт.
+```python
+if __name__ == "__main__":
+ print("Hello, World!")
+ print("This is my first Python script!")
+ print(f"To infinity and beyond: {1 / 0}")
+```
+
+**Задание**:
+- В каком теперь состоянии находится наш файл?
+- Закоммитьте новые изменения.
+
+## Задача 4
+
+Запустим наш скрипт. О нет! Похоже мы допустили ошибку, и закоммитили эти изменения в наш репозиторий! Не беда, ведь мы умеем путешествовать во времени.
+
+**Задание**
+
+- Просмотрите историю коммитов и определите хеш коммита, для которого скрипт был рабочим.
+- Откатитесь в ту версию проекта, когда скрипт был рабочим. Откатитесь таким образом, чтобы внесенные вредоносные изменения не сохранились.
+- Отправьте изменения в свой удаленный репозиторий.
diff --git a/lessons/sem01/lesson02/python_programms.pptx b/lessons/sem01/lesson02/python_programms.pptx
new file mode 100644
index 00000000..8f8274de
Binary files /dev/null and b/lessons/sem01/lesson02/python_programms.pptx differ
diff --git a/lessons/sem01/lesson02/tasks.md b/lessons/sem01/lesson02/tasks.md
new file mode 100644
index 00000000..8b5478dc
--- /dev/null
+++ b/lessons/sem01/lesson02/tasks.md
@@ -0,0 +1,230 @@
+# Задачи
+
+## 1. Факториал (Решаем вместе)
+
+*Факториалом* числа $n$ называют произведение $n$ первых положительных целых чисел. Факториал числа $n$ обозначается следующим образом: $n!$.
+
+$$n! = \prod_{i=1}^{n}i$$
+
+Давайте реализуем расчет факториала числа $n$ на `Python`. Сделать это можно с помощью цикла `while`:
+
+```python
+num = 5
+factorial_of_num = 1
+
+while 0 < num:
+ factorial_of_num *= num
+ num -= 1
+
+print(f"{factorial_of_num = }")
+```
+
+Давайте разберем написанное по частям. В первой строке определяется переменная `num`. Это переменная соответствует числу $n$, чей факториал мы намерены рассчитать. Во второй строке мы определяем переменную `factorial_of_num`, в которой будет храниться рассчитанное значение факториала. В строках 4-6 происходит расчет факториала в цикле `while`. В условии цикла мы проверяем значение переменной `num`. Если `num > 1`, мы увеличиваем значение переменной `factorial_of_num` в `num` раз, а саму переменную `num` уменьшаем на `1`. Таким образом переменная `num` используется в качестве уменьшающегося счетчика. С ее помощью мы перечисляем все множители, принимающие участие в расчете факториала. В последней строке мы выводим отформатированный результат вычисления в стандартный поток вывода с помощью встроенной функции `print()`.
+
+На самом деле реализовать расчет факториала на `Python` можно было бы гораздо эффективнее, используя другой цикл - цикл `for`. В отличие от цикла `while` цикл `for` выполняет определенное количество итераций, а не произвольное количество итераций, ограниченное истинностью условия цикла. В следующем примере, тело цикла `for` будет выполнено `3` раза:
+
+```python
+for i in range(3):
+ print(i)
+# Вывод:
+# 0
+# 1
+# 2
+```
+
+В заголовке составной инструкции `for` происходит определение переменной `i`. Эта переменная называется *переменной цикла*. В качестве имени этой переменной не обязательно использовать `i`, можно использовать любое другое имя, которое подходит по контексту. На каждой итерации цикла с переменной цикла связывается новое значение, порождаемое *итерируемым объектов*. Об итерируемых объектах мы подробно поговорим позже. Сейчас достаточно знать, что итерируемые объекты могут быть использованы в цикле `for`.
+
+Для отсчета определенного числа итераций используется специальный итерируемый объект `range`. На вход этому объекту необходимо передать целое число `n` - число итераций. Сам объект `range` позволяет произвести числа из полуинтервала $[0; n)$. Обращаем ваше внимание, что число $n$ в диапазон производимых значений не включено. Наибольшее число, производимое `range` - число $n - 1$. Также можно задать значение начального числа, производимого `range`:
+
+```python
+for i in range(1, 3):
+ print(i)
+# Вывод:
+# 1
+# 2
+```
+
+В этом случае `range` произведет числа из диапазона $[m; n)$, где $m$ - значение первого числа. О других особенностях работы с `range` мы поговорим в следующих занятиях.
+
+Реализация факториала с помощью `range` будет выглядеть следующим образом:
+
+```python
+num = 5
+factorial_of_num = 1
+
+for i in range(1, num + 1):
+ factorial_of_num *= i
+
+print(f"{factorial_of_num = }")
+```
+
+Этот вариант предпочтительнее по нескольким причинам. Во-первых, цикл `for` выполняется быстрее из-за реализации интерпретатора. Во-вторых, вам не нужно беспокоиться за условие цикла и обновление значения счетчика - цикл `for` сделает все за вас. Тогда как в цикле `while` вам придется следить за всем самостоятельно.
+
+## 2. Двойной факториал
+
+**Условие**:
+
+Помимо обычного факториала целого числа в математике также существует функция *двойного факториала* $n!!$, которая определяется следующим образом:
+
+$$ n!! =
+\begin{cases}
+n \times (n - 2)!!, n > 1\\
+1, n \le 1
+\end{cases}
+$$
+
+Ваша задача - реализовать расчет двойного факториала. Допишите код в файле [task2](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task2.py).
+
+**Входные данные**:
+
+- $n$ - целое положительное число меньшее или равное 20 или 0 - число, для которого нужно посчитать двойной факториал.
+
+**Выходные данные**
+
+- Целое положительное число - двойной факториал числа $n$.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 2 | 2 |
+| 3 | 3 |
+| 4 | 8 |
+
+## 3. Вверх по лестнице
+
+**Условие**:
+
+Студент К. забирается вверх по лестнице. Лестница состоит из $n$ ступеней. Студент К. осторожный, поэтому на каждом шаге он поднимается вверх только на одну или две ступени. Но осторожность - не единственная положительная черта студента К. Еще одна положительная черта - любовь к алгоритмическим задачам. Студент К. решил определить, сколькими способами он сможете добраться до самого верха лестницы, если на каждом шаге он может делать или один, или два шага. Помогите студенту К. реализовать алгоритм подсчета количества способов подъема по лестнице.
+
+Ваша задача - реализовать алгоритм расчета количества способов подъема по лестнице. Допишите код в файле [task3](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task3.py).
+
+**Входные данные**:
+
+- $n$ - целое число от 1 до 45 - число ступеней на лестнице.
+
+**Выходные данные**
+
+- Целое число, количество способов подъема по лестнице.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 3 | 3 |
+| 4 | 5 |
+
+## 4. Подсчет умножений
+
+**Условие**:
+
+При стандартном возведении в степень число умножается само на себя несколько раз. В результате, для вычисления $x^n$ нам необходимо $n$ операций умножения. К сожалению, операция умножения выполняется достаточно долго по сравнению с аддитивными операциями по типу сложения. Поэтому для ускорения программ люди начали искать алгоритмы вычисления степени числа с использованием меньшего количества операций умножения. Разберем один из самый простых вариантов алгоритма быстрого возведения в степень. В этом алгоритме мы будем использовать равенство $x^n = {(x^2)}^{\frac{n}{2}}$. В результате алгоритм будет формулироваться следующем образом:
+
+- На вход поступает число $x$ и степень $n$;
+- Если степень делится нацело на 2, то вычисляем $x^2$, и ищем $x^2$ в степени $\frac{n}{2}$;
+- Если степень не делится нацело на 2, то вычисляем $x^{n-1}$, а потом умножаем получившееся число на $x$.
+
+Таким образом, мы можем сильно уменьшить количество операций умножения в нашей программе, например, для вычисления $2^8$ вместо 8-ми умножений нам понадобится только 3: $2^2 = 4$, $4^2 = 16$ и $16^2 = 256$.
+
+Ваша задача - реализовать алгоритм, который подсчитывает количество умножений при вычислении быстрого возведения в степень для заданной степени $n$. Допишите код в файле [task4](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task4.py).
+
+**Входные данные**:
+
+- $n$ - целое число от 1 до 1000 - степень числа.
+
+**Выходные данные**
+
+- Целое число - количество операций умножения, которое требуется для возведения числа в степень $n$ с помощью описанного алгоритма.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 8 | 3 |
+| 10 | 4 |
+
+## 5. НОД
+
+**Условие**:
+
+*Наибольшим общим делителем* (НОД) для двух целых чисел $m$ и $n$ называется наибольшее число, на которое $m$ и $n$ делятся без остатка. Есть множество алгоритмов поиска НОД. Один из самых популярных алгоритмов - это алгоритм Евклида. Он работает так:
+
+- На вход подаются 2 числа $m$ и $n$;
+- Берется остаток от деления большего из этих чисел на меньшее;
+- Если остаток равен 0, то меньшее число является НОД;
+- Если остаток не равен 0, то действия 1-3 повторяются для меньшего из чисел $m$ и $n$ и полученного остатка;
+
+Например, найдем НОД для чисел 1071 и 462:
+
+- остаток от деления 1071 на 462 равен 147;
+- остаток от деления 462 на 147 равен 21;
+- остаток от деления 147 на 21 равен 0;
+- НОД(1071, 462) равен 21.
+
+Ваша задача - реализовать алгоритм Евклида. Допишите код в файле [task5](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task5.py).
+
+**Входные данные**:
+
+- $n$ - целое положительное число число, меньшее или равное $10^{10}$;
+- $m$ - целое положительное число число, меньшее или равное $10^{10}$;
+
+**Выходные данные**
+
+- Целое число - НОД чисел $n$ и $m$.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 1071 147| 21 |
+| 13 17 | 1 |
+
+## 6. Сумма простых делителей
+
+**Условие**:
+
+Простым делителем числа $n$ называется простое число, на которое $n$ делится без остатка. Найдите сумму всех простых делителей числа $n$ без повторений. Например, сумма простых делителей числа 12 равна 5.
+
+Реализуйте алгоритм подсчета суммы простых делителей числа. Допишите код в файле [task6](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task6.py).
+
+**Пояснения**:
+
+- Простое число - это число, которое делится без остатка только на 1 и на само себя;
+- $12 = 2 \times 2 \times 3$. Простые множители без повторений: 2 и 3. $2 + 3 = 5$;
+
+**Входные данные**:
+
+- $n$ - натуральное число, меньшее или равное $10^{10}$.
+
+**Выходные данные**
+
+- Целое число - сумма простых делителей числа $n$.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 12 | 5 |
+| 13 | 13 |
+| 1 | 0 |
+
+## 7. Число-палиндром
+
+Число называется *палиндромом*, если оно читается слева направо точно так же как и справа налево.
+
+Реализуйте алгоритм, который определяет является ли переданное число $n$ палиндромом или нет. Допишите код в файле [task7](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson02/task7.py).
+
+**Входные данные**:
+
+- $n$ - целое число от $-10^{10}$ до $10^{10}$.
+
+**Выходные данные**
+
+- Булево значение. `True`, если число является числом-палиндромом, `False` - иначе.
+
+**Примеры**:
+
+| Входные данные | Выходные данные |
+|---|---|
+| 121 | True |
+| 13 | False |
diff --git a/lessons/sem01/lesson03/numbers.pptx b/lessons/sem01/lesson03/numbers.pptx
new file mode 100644
index 00000000..e591b0dd
Binary files /dev/null and b/lessons/sem01/lesson03/numbers.pptx differ
diff --git a/lessons/sem01/lesson03/practice.ipynb b/lessons/sem01/lesson03/practice.ipynb
new file mode 100644
index 00000000..e6ad6c4a
--- /dev/null
+++ b/lessons/sem01/lesson03/practice.ipynb
@@ -0,0 +1,279 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "f7d17eff",
+ "metadata": {},
+ "source": [
+ "# Сравнение чисел с плавающей точкой\n",
+ "Как уже было сказано на лекции при операциях с числами с плавающей точкой результат может отличасться от ожидаемого."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "6a962822",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.30000000000000004"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "0.2 + 0.1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "26c06d23",
+ "metadata": {},
+ "source": [
+ "В результате прямые сравнения `==` и `!=` с числами с плавающей точкой также будут работать не так, как мы хотели бы. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a1ffa01d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "False"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "a = 0.2 + 0.1\n",
+ "0.3 == a"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0c0e7d91",
+ "metadata": {},
+ "source": [
+ "Поэтому вместо прямого сравнения чисел с плавающей точкой, вы можете использовать специализированные функции из сторонних библиотек или сравнить значения с требуемой точностью следующим образом:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "f3f5e402",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "equals\n"
+ ]
+ }
+ ],
+ "source": [
+ "eps = 1e-6\n",
+ "num1 = 0.1 + 0.2\n",
+ "num2 = 0.3\n",
+ "\n",
+ "if abs(num1 - num2) < eps:\n",
+ " print(\"equals\")\n",
+ "\n",
+ "else:\n",
+ " print(\"not equals\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "21f84d32",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "not equals\n"
+ ]
+ }
+ ],
+ "source": [
+ "eps = 1e-6\n",
+ "num1 = 0.1 + 0.2\n",
+ "num2 = 0.31\n",
+ "\n",
+ "if abs(num1 - num2) < eps:\n",
+ " print(\"equals\")\n",
+ "\n",
+ "else:\n",
+ " print(\"not equals\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "892407f4",
+ "metadata": {},
+ "source": [
+ "В данном примере мы определили точность сравнения, как 6 знаков после запятой. Если абсолютная разница чисел не превышает 1e-6, т.е. если числа равны до шестого знака после запятой включительно, то мы считаем такие числа равными."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "676d1d74",
+ "metadata": {},
+ "source": [
+ "# Битовые операции \n",
+ "\n",
+ "Битовые операции позволяют работать с целыми числами на уровне отдельных битов. Они особенно полезны при низкоуровневом программировании, шифровании, сжатии данных или оптимизации алгоритмов. Основные битовые операторы:\n",
+ "- побитовое НЕ (`~`)\n",
+ "- побитовое И (`&`)\n",
+ "- побитовое ИЛИ (`|`)\n",
+ "- побитовое исключающее ИЛИ (`^`) \n",
+ "- побитовое сдвиг влево (`<<`)\n",
+ "- побитовое сдвиг вправо (`>>`). \n",
+ "\n",
+ "НЕ (`~`) является унарной операцией, а все остальные бинарными. \n",
+ "\n",
+ "_Примечания:_\n",
+ "- Все эти операции применяются к двоичному представлению целых чисел и возвращают новое целое число.\n",
+ "- В Python целые числа имеют неограниченную длину, поэтому побитовое НЕ (`~x`) эквивалентно `-(x + 1)`, так как используется представление в дополнительном коде. \n",
+ "- Битовые сдвиги эффективно умножают или делят число на степени двойки: сдвиг влево на n бит — это умножение на `2**n`, а сдвиг вправо — целочисленное деление на `2**n`.\n",
+ "\n",
+ "**Примеры работы битовых операций:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "1d5d3794",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "a = 0b01101 = 13\n",
+ "b = 0b00110 = 6\n",
+ "~a = 0b00010 = -14 # побитовое НЕ (в 4-битном представлении)\n",
+ "\n",
+ "a & b = 0b00100 = 4 # побитовое И\n",
+ "a | b = 0b01111 = 15 # побитовое ИЛИ\n",
+ "a ^ b = 0b01011 = 11 # исключающее ИЛИ\n",
+ "a << 1 = 0b11010 = 26 # сдвиг влево на 1\n",
+ "a >> 1 = 0b00110 = 6 # сдвиг вправо на 1\n"
+ ]
+ }
+ ],
+ "source": [
+ "a = 13 \n",
+ "b = 6 \n",
+ "\n",
+ "print(f\"a = 0b{a:05b} = {a:2d}\")\n",
+ "print(f\"b = 0b{b:05b} = {b:2d}\")\n",
+ "print(f\"~a = 0b{~a & 0b1111:05b} = {~a:3d} # побитовое НЕ (в 4-битном представлении)\")\n",
+ "print()\n",
+ "print(f\"a & b = 0b{a & b:05b} = {a & b:3d} # побитовое И\")\n",
+ "print(f\"a | b = 0b{a | b:05b} = {a | b:3d} # побитовое ИЛИ\")\n",
+ "print(f\"a ^ b = 0b{a ^ b:05b} = {a ^ b:3d} # исключающее ИЛИ\")\n",
+ "print(f\"a << 1 = 0b{a << 1:05b} = {a << 1:3d} # сдвиг влево на 1\")\n",
+ "print(f\"a >> 1 = 0b{a >> 1:05b} = {a >> 1:3d} # сдвиг вправо на 1\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "45c9b865",
+ "metadata": {},
+ "source": [
+ "## Задача: Бит-детектор чужих\n",
+ "\n",
+ "**Условие:**\n",
+ "\n",
+ "Дан фиксированный набор целых неотрицательных чисел: $7, 10, 13, 18, 23$ (набор в данной задаче не важен и может быть другим). \n",
+ "Требуется реализовать функцию, которая по заданному целому числу определяет, содержится ли оно в этом наборе.\n",
+ "\n",
+ "**Ограничения:** \n",
+ "- Нельзя использовать списки, кортежи, множества, строки и любые другие типы коллекций или последовательностей. \n",
+ "- Разрешено использовать только целые числа, арифметические и битовые операции. \n",
+ "- Решение должно быть эффективным и не содержать явного перебора значений при проверке.\n",
+ "\n",
+ "**Входные данные:**\n",
+ "\n",
+ "- `x` - целое число от -32 до 32.\n",
+ "\n",
+ "**Выходные данные:**\n",
+ "\n",
+ "- Логическое значение: \n",
+ " - `True`, если `x` принадлежит набору `{7, 10, 13, 18, 23}`; \n",
+ " - `False` — в противном случае.\n",
+ "\n",
+ "**Примеры:**\n",
+ "\n",
+ "| Вход (`x`) | Выход | Комментарий |\n",
+ "|------------|------------|---------------------------------|\n",
+ "| `-5` | `False` | -5 не входит в набор |\n",
+ "| `0` | `False` | 0 не входит в набор |\n",
+ "| `13` | `True` | 13 есть в наборе |\n",
+ "| `15` | `False` | 15 отсутствует в наборе |\n",
+ "| `23` | `True` | 23 есть в наборе |\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "a7b7bd02",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_in_set(x):\n",
+ " # Ваш код\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "20205eaa",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "assert not is_in_set(-5)\n",
+ "assert not is_in_set(0)\n",
+ "assert is_in_set(13)\n",
+ "assert not is_in_set(15)\n",
+ "assert is_in_set(23)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson03/task.md b/lessons/sem01/lesson03/task.md
new file mode 100644
index 00000000..93a1536d
--- /dev/null
+++ b/lessons/sem01/lesson03/task.md
@@ -0,0 +1,81 @@
+# Обезьяний XOR
+
+**Условие:**
+
+Дано неотрицательное целое число `num` и два целых числа `left_bit` и `right_bit`.
+
+Требуется инвертировать (перевернуть: 0 → 1, 1 → 0) все биты числа `num` в позициях от `left_bit` до `right_bit` включительно, считая с младшего бита под номером 1.
+
+Допишите код в файле [task1](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson03/task1.py).
+
+**Входные данные:**
+
+- `num` - целое число от $0$ до $10^9$ - число, в котором надо инвертировать биты;
+- `left_bit` - целое число от $0$ до $32$ - номер бита, с которого надо начать инвертировать;
+- `right_bit` - целое число от $0$ до $32$ - номер бита, до которого надо начать инвертировать `left_bit` $\le$ `right_bit`.
+
+**Выходные данные:**
+
+- Целое число — результат инвертирования битов в указанном диапазоне.
+
+**Примеры:**
+| num | left_bit | right_bit | Двоичное num (до) | Двоичное (после) | Ответ |
+|-----|----------|-----------|-------------------|-------------------|--------|
+| 13 | 2 | 3 | `1101` | `1011` | 11 |
+| 8 | 1 | 3 | `1000` | `1111` | 15 |
+
+# Кубический корень для терпеливых
+
+**Условие:**
+
+Дано вещественное число $n$ и требуемая точность $\varepsilon > 0$. Требуется найти кубический корень из $n$, то есть такое число $x$, что $x^3 = n$, с абсолютной погрешностью не более $\varepsilon$. Допишите код в файле [task2](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson03/task2.py).
+
+**Важно:** Запрещено использовать любые встроенные функции для возведения извлечения корня (`**`, `pow`, `math.pow`, `math.sqrt`, `numpy.cbrt` и т.п.). Разрешается использовать только арифметические операции: сложение, вычитание, умножение, деление и сравнение. Для решения задачи необходимо применить метод бинарного поиска.
+
+## Входные данные
+
+- $n$ - вещественное число, из которого нужно извлечь кубический корень. Может быть положительным, отрицательным или нулём.
+- $\varepsilon$ — положительное вещественное число (например, `1e-6`), задающее допустимую абсолютную погрешность результата.
+
+## Выходные данные
+
+- Вещественное число $x$, такое что $|x^3 - n| \leq \varepsilon$.
+
+
+## Примеры
+
+| Входные данные (`n`, `eps`) | Выходные данные (примерно) |
+|-----------------------------|-------------------------------|
+| `27`, `1e-6` | `3.0` |
+| `-8`, `1e-6` | `-2.0` |
+| `0`, `1e-5` | `0.0` |
+| `2`, `1e-7` | `1.259921...` |
+
+> **Примечание:** Точные значения могут незначительно отличаться в пределах заданной точности $\varepsilon$.
+
+# Считаем цифры
+
+**Условие:**
+
+Рассмотрим бесконечную последовательность всех положительных чётных чисел:
+$$0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, ...$$
+
+Требуется определить, какая цифра находится на **n-й позиции** в этой последовательности (нумерация позиций начинается с $1$). Допишите код в файле [task3](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson03/task3.py).
+
+**Входные данные:**
+
+- $n$ - целое число от $1$ до $10^9$ - номер цифры в последовательности.
+
+**Выходные данные:**
+
+- Целое число от $0$ до $9$ - цифра, стоящая на n-й позиции в последовательности.
+
+**Примеры:**
+
+| Входные данные | Выходные данные |
+|-----------------|-----------------|
+| 5 | 8 |
+| 10 | 1 |
+
+
+
diff --git a/lessons/sem01/lesson04/lists_n_tuples.pptx b/lessons/sem01/lesson04/lists_n_tuples.pptx
new file mode 100644
index 00000000..91bc51d4
Binary files /dev/null and b/lessons/sem01/lesson04/lists_n_tuples.pptx differ
diff --git a/lessons/sem01/lesson04/practice.ipynb b/lessons/sem01/lesson04/practice.ipynb
new file mode 100644
index 00000000..88c4e713
--- /dev/null
+++ b/lessons/sem01/lesson04/practice.ipynb
@@ -0,0 +1,455 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "bcda20f5",
+ "metadata": {},
+ "source": [
+ "# Дополнительные темы\n",
+ "\n",
+ "## Сортировка\n",
+ "\n",
+ "Сортировка - одна из фундаментальных задач в программировании. Сортировка очень часто используется в различных алгоритмах, поэтому в `Python` существует встроенная функция `sorted()`, для выполнения сортировки.\n",
+ "\n",
+ "На вход функция `sorted()` ожидает получить любой итерируемый объект. Результат выполнения функции - список, элементы которого отсортированы в порядке возрастания. Очевидно, что все элементы списка должны поддерживать выполнение операции сравнения между собой."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "234bed68",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers_list = [3, -1, 4, -2]\n",
+ "numbers_sorted = sorted(numbers_list)\n",
+ "print(\n",
+ " f\"list original: {numbers_list}\",\n",
+ " f\"list sorted: {numbers_sorted}\",\n",
+ " sep=\"\\n\",\n",
+ ")\n",
+ "\n",
+ "numbers_tuple = tuple(numbers_list)\n",
+ "numbers_sorted = sorted(numbers_tuple)\n",
+ "print(\n",
+ " f\"tuple original: {numbers_list}\",\n",
+ " f\"tuple sorted: {numbers_sorted}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fad69973",
+ "metadata": {},
+ "source": [
+ "По умолчанию функция `sorted()` позволяет отсортировать данные в порядке возрастания. Однако, если вам требуется отсортировать данные в порядке убывания, вы можете связать с необязательным параметром `reverse` значение `True`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7eda5bca",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sorted([1, 3, 2], reverse=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "56e6e7cf",
+ "metadata": {},
+ "source": [
+ "Обратите внимание, что в результате выполнения `sorted()` **всегда** создается новый список. В случае с изменяемыми типами данных это может быть не очень удобно. Например, на практике часто может потребоваться отсортировать список на месте, не создавая копии. Для этого у списков есть метод `sort()`, который позволяет отсортировать элементы на месте, без создания копии. Метод `sort()` имеет те же параметры, что и функция `sorted()`, за исключением параметра, с которым связывается итерируемый объект."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d9834e12",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums = [3, 1, 4]\n",
+ "nums.sort()\n",
+ "print(nums)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "39635f64",
+ "metadata": {},
+ "source": [
+ "## Последовательности и оператор =\n",
+ "\n",
+ "### Повторение\n",
+ "\n",
+ "Напоминаем, что оператор привязки `=` используется в `Python` для связывания указателей на объекты в памяти с идентификаторами. В самом простом варианте этот оператор используется так:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ed9f3788",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1 = 1000\n",
+ "num2 = num1\n",
+ "print(f\"{num1 = }; {num2 = };\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d5f45ea4",
+ "metadata": {},
+ "source": [
+ "Не стоит забывать, что если справа от оператора `=` находится идентификатор, то новый объект в памяти создан не будет. После выполнения привязки идентификатор, находившийся слева от знака `=`, будет связан с новой ссылкой на уже существующий объект в памяти. Таким образом объекты `num1` и `num2` указывают на один и тот же объект в памяти."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "55b52a34",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "if num1 is num2:\n",
+ " print(\"num1 and num2 are the same\")\n",
+ "\n",
+ "else:\n",
+ " print(\"num1 and num2 are different\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b69a7fd9",
+ "metadata": {},
+ "source": [
+ "Этот факт особенно важно помнить при работе с объектами изменяемых типов данных. Если забыть об этой особенности работы с переменными в `Python`, можно спокойно создать себе кучу неприятностей на ровном месте:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "58c9b544",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lst1 = [1, 2]\n",
+ "lst2 = lst1\n",
+ "lst2[-1] = 42\n",
+ "\n",
+ "print(\n",
+ " f\"{lst1 = };\",\n",
+ " f\"{lst2 = };\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "059872a6",
+ "metadata": {},
+ "source": [
+ "В данном примере во время определения переменной `lst1` мы не создали нового списка в памяти, а всего лишь создали новую ссылку на уже существующий список. Таким образом `lst1` и `lst2` связаны со ссылками на один и тот же объект в памяти. Поскольку списки - изменяемые объекты, мы можем вносить изменения, используя обе ссылки: `lst1` и `lst2`. Нельзя независимо изменять один и тот же объект в памяти, используя две разные ссылки на него."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "88b7076a",
+ "metadata": {},
+ "source": [
+ "### Цепное привязывание\n",
+ "\n",
+ "На практике нередко встречаются ситуации, когда необходимо связать одно и то же значение с несколькими переменными. Например, перед выполнением некоторого алгоритма вам может потребоваться проинициализировать несколько переменных нулем. Как это можно сделать? Самый очевидный способ выглядит так:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "eb2b3166",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1 = 0\n",
+ "num2 = 0\n",
+ "num3 = 0\n",
+ "\n",
+ "print(f\"{num1 = }; {num2 = }; {num3 = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "78ba49fa",
+ "metadata": {},
+ "source": [
+ "Однако, для таких случаев существует более удобный способ привязки: *цепная привязка*."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9e6943ef",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1 = num2 = num3 = 0\n",
+ "print(f\"{num1 = }; {num2 = }; {num3 = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b01691a9",
+ "metadata": {},
+ "source": [
+ "### Распаковка (Unpacking)\n",
+ "\n",
+ "Мы поняли, как поступить в случае, если нужно связать одно и то же значение с несколькими переменными. Но как быть, если нужно связать разные значения с разными переменными? Неужели придется неэкономно определять переменные на отдельных физических строках?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "081c1797",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1 = 34\n",
+ "num2 = 42\n",
+ "num3 = 69\n",
+ "\n",
+ "print(f\"{num1 = }; {num2 = }; {num3 = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "08d52745",
+ "metadata": {},
+ "source": [
+ "На самом деле и на этот случай мы можем воспользоваться специальным трюком:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9cf25936",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1, num2, num3 = 34, 42, 69\n",
+ "print(f\"{num1 = }; {num2 = }; {num3 = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b6b47c53",
+ "metadata": {},
+ "source": [
+ "Эта запись гораздо компактнее. Однако не стоит ею злоупотреблять, иначе вы рискуете нанести урон читабельности вашего кода.\n",
+ "\n",
+ "На самом деле то, что мы видим в примере выше - это комбинация особенностей оператора `=` и итерируемых объектов. Вот что произошло в этом примере:\n",
+ "\n",
+ "- Справа от оператора `=` был определен кортеж с помощью литерала `34, 42, 69`.\n",
+ "- Слева от оператора `=` были перечислены идентификаторы через запятую. Перечисление идентификаторов подсказывает интерпретатору `Python`, что он должен проитерироваться по объекту справа от оператора `=` и связать каждый идентификатор слева с соответствующим элементом итерируемого объекта.\n",
+ "- Интерпретатор выполняет связывания идентификаторов с соответствующими элементами.\n",
+ "\n",
+ "Эти особенности удобно использовать для одновременного \"обемена\" значениями: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b222b8a5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1, num2 = 1, 1\n",
+ "\n",
+ "for _ in range(5):\n",
+ " print(f\"{num1 = }; {num2 = }\")\n",
+ " num1, num2 = num2, num1 + num2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f24d0afa",
+ "metadata": {},
+ "source": [
+ "Важно, что если слева находится несколько идентификаторов, перечисленных через запятую, справа должен находиться итерируемый объект. С противном случае, вы получите ошибку:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d032b921",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num1, num2 = 42"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "be529ff7",
+ "metadata": {},
+ "source": [
+ "Обращаем ваше внимание, что `Python` позволяет распаковывать любой итерируемый объект, а не только кортежи:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cf25d052",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "coord_x, coord_y, coord_z = [1, 2, 3]\n",
+ "print(f\"{coord_x = }; {coord_y = }; {coord_z = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "117f0d09",
+ "metadata": {},
+ "source": [
+ "### Запаковка\n",
+ "\n",
+ "В случае, когда вам необходимо распаковать итерируемый объект, но вы не уверены, сколько элементов в нем содержится, вы можете столкнуться с ошибкой:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "beab94ad",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tag_start, tag_end = [\"init\", \"\", \"stop\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6e233185",
+ "metadata": {},
+ "source": [
+ "Если вы знаете наверняка, что итерируемый объект содержит не меньше определенного количества значений, и вас интересуют значения конкретных элементов (например, первого и последнего), вы можете запаковать оставшиеся элементы в список:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "73570d68",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tag_start, *messages, tag_end = [\"init\", \"\", \"stop\"]\n",
+ "print(f\"{tag_start = }; {messages = }; {tag_end = }\")\n",
+ "\n",
+ "tag_start, *messages, tag_end = [\"init\", \"stop\"]\n",
+ "print(f\"{tag_start = }; {messages = }; {tag_end = }\")\n",
+ "\n",
+ "tag_start, *messages, tag_end = [\"init\", \"\", \"\", \"stop\"]\n",
+ "print(f\"{tag_start = }; {messages = }; {tag_end = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "35cc879b",
+ "metadata": {},
+ "source": [
+ "## Моржовый оператор\n",
+ "\n",
+ "В `Python` 3.8 появился новый оператор `:=` (точки - это глаза, а `=` - это бивни, поэтому морж). Этот оператор решает проблему невозможности использовать операцию привязки там, где требуется получить некоторое значение. Т.е. фактически моржовые оператор превращает операцию привязки в выражение. С его помощью вы можете одновременно связать идентификатор с каким-либо значением и получить значение, с которым происходило связывание, в качестве результата выполнения операции. Это может быть полезно, если мы получаем какое-то значение из функции и хотим использовать его как в заголовках управляющих конструкций, так и далее в коде.\n",
+ "\n",
+ "Без использования `:=`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "042d0c44",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from http import HTTPStatus\n",
+ "\n",
+ "\n",
+ "def make_request() -> int:\n",
+ " return HTTPStatus.OK\n",
+ "\n",
+ "\n",
+ "status = make_request()\n",
+ "\n",
+ "if HTTPStatus.BAD_REQUEST <= status:\n",
+ " print(f\"got bad response with status: {status}\")\n",
+ "\n",
+ "result = {\"status\": status}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0bf44e29",
+ "metadata": {},
+ "source": [
+ "С использованием `:=`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a46cd6b0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from http import HTTPStatus\n",
+ "\n",
+ "\n",
+ "def make_request() -> int:\n",
+ " return HTTPStatus.OK\n",
+ "\n",
+ "\n",
+ "if HTTPStatus.BAD_REQUEST <= (status := make_request()):\n",
+ " print(f\"got bad response with status: {status}\")\n",
+ "\n",
+ "result = {\"status\": status}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c2099dd0",
+ "metadata": {},
+ "source": [
+ "Не забывайте использовать скобки `()` вокруг оперетора, так как у него низкий приоритет выполнения."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9a4c58f9",
+ "metadata": {},
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson05/practice.ipynb b/lessons/sem01/lesson05/practice.ipynb
new file mode 100644
index 00000000..1ad35b76
--- /dev/null
+++ b/lessons/sem01/lesson05/practice.ipynb
@@ -0,0 +1,482 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "5e0a49e0",
+ "metadata": {},
+ "source": [
+ "# Сопоставление шаблонов\n",
+ "\n",
+ "На лекции мы познакомились с методом строк `split()`, который используется для разбиения строк. Очень часто во время разбиения строк заранее неизвестно, сколько фрагментов мы в итоге получим. В большинстве случаев такое поведение нас устраивает. Однако существуют случаи, когда нам важно знать, сколько сегментов в результате разбиения было получено, что это за сегменты, каковы их значения.\n",
+ "\n",
+ "Яркий пример такого подхода к анализу фрагментов строки после разбиения - текстовые ролевые игры. Предположим, что мы разрабатываем как раз такую игру. Процесс игры состоит в обмене сообщениями с компьютером через терминал. Пользователь может вводить определенные команды, по типу `идти <направление>` для перемещения, `продать <предмет_1> [<предмет_2>, ..., <предмет_N>]` для продажи предметов, `выйти` для выхода из игры и т.д. В ответ на действия пользователя компьютер будет печатать в стандартный поток вывода различные сообщения, описывающие текущее состояние игрока и мира вокруг.\n",
+ "\n",
+ "Видно, что команды, которые вводит пользователь, могут состоять из одного слова, как команда `выйти`, из двух слов, как команда `идти <направление>`, или из произвольного числа слов, как команда `продать <предмет_1> [<предмет_2>, ..., <предмет_N>]`. Мы, как разработчики, должны учитывать этот факт, чтобы корректно обработать каждую команду. Вот как мы можем сделать это с помощью ветвления:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4b518bdc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "unknown_command_msg_template = \"Неизвестная команда: {command!r}\"\n",
+ "directions = (\"вперед\", \"назад\", \"вправо\", \"влево\")\n",
+ "\n",
+ "command = \"идти вперед\"\n",
+ "parts = command.split()\n",
+ "\n",
+ "if len(parts) == 1:\n",
+ " if parts[0] == \"выйти\":\n",
+ " ...\n",
+ "\n",
+ " else:\n",
+ " print(unknown_command_msg_template.format(command=parts[0]))\n",
+ "\n",
+ "if len(parts) == 2:\n",
+ " if parts[0] == \"идти\" and parts[-1] in directions:\n",
+ " ...\n",
+ "\n",
+ " else:\n",
+ " print(unknown_command_msg_template.format(command=command))\n",
+ "\n",
+ "if len(parts) == 3:\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dc417f97",
+ "metadata": {},
+ "source": [
+ "Разумеется, этот пример не претендует на продуктовое качество. Однако, надеемся, вы уловили, насколько неудобна такая проверка. Нам приходится выполнять кучу повторяющихся действий, а большое количество кода дублируется. К счастью, в `Python 3.10` был добавлен более удобный инструмент для подобных проверок - сопоставление шаблонов с помощью конструкции `match-case`.\n",
+ "\n",
+ "Итак, далее мы будем пытаться реализовать нашу текстовую игру с помощью `match-case`. Но начнем мы с цикла событий. \n",
+ "\n",
+ "## input и цикл событий\n",
+ "\n",
+ "Цикл событий - это основа любой игры. Цикл событий - это условно бесконечный цикл, который прерывается с помощью специальной команды выхода, введенной пользователем. Каждая итерация цикла событий состоит из трех основных шагов:\n",
+ "\n",
+ "- Пользователь вводит какую-либо команду\n",
+ "- Наша программа обрабатывает команду пользователя\n",
+ "- На экран выводится результат выполнения команды\n",
+ "\n",
+ "После выполнения итерации цикла событий происходит или переход к очередной итерации, или завершение игры, если пользователь ввел соответствующую команду.\n",
+ "\n",
+ "В нашей текстовой игре тоже будет цикл событий. Поскольку мы ограничены по времени, никакая сложная логика обработки команд реализована не будет. Вся обработка будет заключаться в выборе сообщения для печати на экране на основе введенной команды. Обработка будет выполняться с помощью методов строк и конструкции `match-case`. Вывод результата обработки команды будет происходить с помощью знакомой нам функции `print()`. Однако непонятно, что делать со вводом?\n",
+ "\n",
+ "Для того, чтобы пользователь имел возможность ввода команд, мы будем использовать встроенную функцию `input()`. Функция `input()` позволяет считать текст, введенный в стандартный поток ввода, и сконструировать на его основе строку:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3879add8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "name = input(\"Enter your name: \")\n",
+ "age = input(\"Enter your age: \")\n",
+ "hobbies = input(\"Enter your hobbies: \")\n",
+ "\n",
+ "print(\n",
+ " f\"name: {name!r}\",\n",
+ " f\"age: {age!r}\",\n",
+ " f\"hobbies: {hobbies!r}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f9be522c",
+ "metadata": {},
+ "source": [
+ "Обратите внимание, что считывание происходит до появления первого символа перехода на новую строку. Также обратите внимание, что вне зависимости от введенного сообщения результатом выполнения функции `input()` всегда является строка. Даже если в стандартный поток ввода было введено число, в коде вы получите строку. Стоит быть особенно осторожными, если вы планируете использовать введенный результат для каких-либо математических вычислений:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2ae05eb2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num = input(\"Enter non-zero number: \")\n",
+ "print(f\"inversed num: {1 / num}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b6f568dd",
+ "metadata": {},
+ "source": [
+ "В данном примере мы планировали обратить введенное число. Однако вместо успешного результата, мы получили `TypeError`, т.к. переменная `num` указывает на объект строкового типа данных, а деление чисел на строки не допускается. Чтобы избежать подобной ошибки, необходимо сконструировать объект требуемого типа данных явно:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fb3fad97",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num = float(input(\"Enter non-zero number: \"))\n",
+ "print(f\"inversed num: {1 / num}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "441dd554",
+ "metadata": {},
+ "source": [
+ "Итак, зная о функции `input()`, реализуем первое приближения цикла событий:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "40886b4e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command_stop = \"выйти\"\n",
+ "\n",
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " if command == command_stop:\n",
+ " break\n",
+ "\n",
+ "print(\"🍺 Победа! 🍺\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e78eadce",
+ "metadata": {},
+ "source": [
+ "Пока что ничего содержательного мы не сделали. Мы просто считываем команды пользователя в бесконечном цикле и завершаем работу программы, если пользователь ввел `выйти`. Да и `match-case` мы нигде не использовали. Поэтому давайте усложним нашу программу и добавим обработку перемещений.\n",
+ "\n",
+ "## match-case: простейший вариант"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a6843c9c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction]:\n",
+ " print(f\"Вы пошли {direction} 🚶\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ce21d180",
+ "metadata": {},
+ "source": [
+ "Итак, в данном примере мы наконец-то воспользовались `match-case`. `match-case` - это специальная конструкция для сопоставления паттернов. В качестве паттернов могут выступать многие объекты. Паттерны описываются в `case`-блоках. Принцип работы `match-case` следующий:\n",
+ "\n",
+ "- В заголовке `match` происходит \"захват\" значения некоторого объекта. Именно это значение и будет сопоставляться с различными шаблонами в `case`-блоках, перечисленных в теле инструкции `match`. В нашем случае в качестве захватываемого значения будет выступать результат выполнения метода `split()` для считанной команды.\n",
+ "- Захваченное значение будет последовательно сопоставляться с шаблонами, указанными в `case`-блоках. Шаблоны перебираются до тех пор, пока не будет встречен шаблон, которому удовлетворяет захваченное значение, или до тех пор, пока шаблоны не закончатся.\n",
+ "- Если значение соответствует шаблону, указанному в заголовке данного `case`-блока, будет выполнено тело этого `case`-блока. После выполнения тела `case`-блока происходит завершение выполнения инструкции `match-case`. Интерпретатор переходит к выполнению следующей инструкции.\n",
+ "\n",
+ "В данном примере мы указали всего два шаблона: `[\"выйти\"]` и `[\"идти\", direction]`. Первый шаблон описывает любую последовательность, состоящую из одного элемента. Значение этого элемента - `\"выйти\"`. Т.е., фактически, первый `case`-блок используется для обработки команды `\"выйти\"`. Действительно, после применения к этой строке метода `split()` мы получим список `[\"выйти\"]`, который удовлетворяет первому шаблону по определению. В качестве обработки этой команды мы печатаем сообщение о завершении игры и выходим из цикла событий.\n",
+ "\n",
+ "Второй шаблон устроен хитрее. Как и в первом случае он описывает последовательность. Но на этот раз последовательность должна состоять из двух элементов. Значение первого элемента должно быть `\"идти\"`. А вот значение второго элемента может быть любым. В этом шаблоне мы просто связываем второй элемент с идентификатором `direction`. Затем этот идентификатор используется в теле `case`-блока, чтобы напечатать на экране направление движения пользователя. С помощью этого шаблона мы получаем возможность обрабатывать такие сообщения: `идти вперед`, `идти назад`, `идти прямо` и т.д.\n",
+ "\n",
+ "Итак, начало положено. Теперь давайте улучшим нашу игру."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "277478ae",
+ "metadata": {},
+ "source": [
+ "## match-case: случай по умолчанию\n",
+ "\n",
+ "Очевидный минус текущей версии: если пользователь напечатает команду, которая не удовлетворяет ни одному шаблону, игра никак не отреагирует на это. Т.е. пользователь останется в неведении и будет думать, что игра зависла или сломалась. Давайте это исправим и добавим случай по умолчанию для тех команд, которые мы не умеем обрабатывать. В этом случае мы будем выводить сообщение о невозможности обработать команду пользователя:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "42d9b34c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction]:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8ae6e6cd",
+ "metadata": {},
+ "source": [
+ "Для обработки случая по умолчанию мы использовали специальную конструкцию `case _`. В контексте `match-case` идентификатор `_` имеет смысл случая по умолчанию, если используется как шаблон в заголовке `case`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2bb95593",
+ "metadata": {},
+ "source": [
+ "## match-case: subpatterns\n",
+ "\n",
+ "Следующий момент, который нам нужно улучшить - ограничить направления перемещений. Сейчас пользователь может идти куда угодно. Мы бы хотели, чтобы пользователь перемещался только в четырех направлениях: вперед, назад, вправо, влево. Давайте ограничим значение элемента `direction`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "69c4dd3e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", (\"вперед\" | \"назад\" | \"вправо\" | \"влево\") as direction]:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c510786e",
+ "metadata": {},
+ "source": [
+ "С помощью круглых скобок и разделителей `|` мы перечислили допустимые значения для данного элемента захваченного значения. А с помощью оператора `as` мы по-прежнему имеем возможность связывать второй элемент захваченного значения с идентификатором `direction` и использовать его в теле `case`-блока."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "550a8aaf",
+ "metadata": {},
+ "source": [
+ "## match-case: дополнительные условия\n",
+ "\n",
+ "На самом деле перечислять значения направлений так, как мы это сделали в прошлом примере - не очень удобно. Гораздо удобнее завести какую-нибудь коллекцию с перечислением всех допустимых направлений. Тогда мы получим возможность использовать коллекцию направлений в других частях программы без дублирования кода. Однако непонятно, как это реализовать, сохранив проверку ограничений в паттерне. На самом деле это можно сделать с помощью дополнительных условий:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6e9bb847",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "directions = (\"вперед\", \"назад\", \"вправо\", \"влево\")\n",
+ "\n",
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction] if direction in directions:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3ab680a0",
+ "metadata": {},
+ "source": [
+ "За описанием шаблона, после ключевого слова `if`, можно перечислить дополнительные условия, которые необходимо проверить. Обращаем ваше внимание, что в этих условиях могут фигурировать переменные, определенные во время проверки соответствия захваченного значения шаблону."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "57753ccf",
+ "metadata": {},
+ "source": [
+ "## match-case: случай по умолчанию для отдельной команды\n",
+ "\n",
+ "Итак, теперь наша программа корректно обрабатывает команду перемещения. Команды формата `идти прямо` будут обработаны как неизвестные команды. С одной стороны, это логичная обработка, т.к. `прямо` не входит в число известных направлений. С другой стороны команда `идти прямо` - это скорее известная нам команда `идти` с неизвестной опцией `прямо`. Поэтому не совсем корректно было бы отвечать на эту команду сообщением `\"Я не знаю такой команды ...\"`. Корректнее было бы ответить как-то иначе, указав пользователю, что команда верная, но выбранная опция не поддерживается.\n",
+ "\n",
+ "Сделать это можно, указав в шаблоне на месте второго параметра `_`: `[\"идти\", _]`. Такой шаблон описывает последовательность из двух элементов. Первый элемент - `\"идти\"`, а второй элемент - любой объект, значение которого отличается от разрешенных направлений. Это суждение справедливо, если расположить паттерн `[\"идти\", _]` после паттерна с обработкой известных направлений:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dc25e6a1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "directions = (\"вперед\", \"назад\", \"вправо\", \"влево\")\n",
+ "\n",
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction] if direction in directions:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case [\"идти\", _]:\n",
+ " print(\"Сюда ходить нельзя 🚫\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fa6ec589",
+ "metadata": {},
+ "source": [
+ "## match-case: объединение паттернов\n",
+ "\n",
+ "Теперь давайте добавим еще пару команд для взаимодействия с окружающим миром. Добавим команду `взять <объект>`, для того, чтобы брать объекты окружающего мира себе в инвентарь, и команду `выбросить <объект>`, чтобы избавляться от ненужных предметов в инвентаре. Эти команды по сути своей очень похожи, поэтому мы объединим их обработку в один кейс, используя `|` для объединения паттернов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4963beb3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "directions = (\"вперед\", \"назад\", \"вправо\", \"влево\")\n",
+ "\n",
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction] if direction in directions:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case [\"идти\", _]:\n",
+ " print(\"Сюда ходить нельзя 🚫\")\n",
+ "\n",
+ " case [\"взять\" as action, obj] | [\"выбросить\" as action, obj]:\n",
+ " action = action.replace(\"ть\", \"ли\")\n",
+ " print(f\"Вы {action} {obj} 🎒\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d994c8e4",
+ "metadata": {},
+ "source": [
+ "## match-case: запаковка\n",
+ "\n",
+ "Итак, теперь мы умеем брать и выбрасывать различные предметы. Однако у текущей реализации есть существенная проблема: чтобы взять или выбросить несколько вещей, нам придется выполнить соответствующую команду несколько раз подряд. Очевидно, это неудобно и создает плохой опыт пользователя. Поэтому давайте позволим пользователям брать несколько вещей и избавляться от нескольких вещей за одну команду. В реализации этой задумки нам поможет запаковка:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e0f72f97",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "directions = (\"вперед\", \"назад\", \"вправо\", \"влево\")\n",
+ "\n",
+ "while True:\n",
+ " command = input(\"Ваш ход 🎲: \")\n",
+ "\n",
+ " match command.split():\n",
+ " case [\"выйти\"]:\n",
+ " print(\"Игра окончена 💔\")\n",
+ " break\n",
+ "\n",
+ " case [\"идти\", direction] if direction in directions:\n",
+ " print(f\"Вы пошли {direction} 🚶\")\n",
+ "\n",
+ " case [\"идти\", _]:\n",
+ " print(\"Сюда ходить нельзя 🚫\")\n",
+ "\n",
+ " case [\"взять\" as action, *objs] | [\"выбросить\" as action, *objs]:\n",
+ " action = action.replace(\"ть\", \"ли\")\n",
+ " objects = \", \".join(objs)\n",
+ " print(f\"Вы {action} {objects} 🎒\")\n",
+ "\n",
+ " case _:\n",
+ " print(f\"Я не знаю такой команды {command!r} 😔\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dbadb70e",
+ "metadata": {},
+ "source": [
+ "## Дополнительно\n",
+ "\n",
+ "В этом примере рассмотрены далеко не все особенности работы с `match-case`. Если вы хотите рассмотреть больше примеров использования этой составной инструкции, советуем ознакомиться с [официальным мануалом](https://peps.python.org/pep-0636/).\n",
+ "\n",
+ "## Практика\n",
+ "\n",
+ "Условия практических задач вы найдете [тут](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/conditions/lesson05/tasks.md)."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson05/strings.pptx b/lessons/sem01/lesson05/strings.pptx
new file mode 100644
index 00000000..a76b69ae
Binary files /dev/null and b/lessons/sem01/lesson05/strings.pptx differ
diff --git a/lessons/sem01/lesson06/dicts_n_sets.pptx b/lessons/sem01/lesson06/dicts_n_sets.pptx
new file mode 100644
index 00000000..fca6c307
Binary files /dev/null and b/lessons/sem01/lesson06/dicts_n_sets.pptx differ
diff --git a/lessons/sem01/lesson07/functions.pptx b/lessons/sem01/lesson07/functions.pptx
new file mode 100644
index 00000000..cebff56f
Binary files /dev/null and b/lessons/sem01/lesson07/functions.pptx differ
diff --git a/lessons/sem01/lesson08/decorators.pptx b/lessons/sem01/lesson08/decorators.pptx
new file mode 100644
index 00000000..cfb1fabe
Binary files /dev/null and b/lessons/sem01/lesson08/decorators.pptx differ
diff --git a/lessons/sem01/lesson08/images/strategy.png b/lessons/sem01/lesson08/images/strategy.png
new file mode 100644
index 00000000..8b91db38
Binary files /dev/null and b/lessons/sem01/lesson08/images/strategy.png differ
diff --git a/lessons/sem01/lesson08/images/strategy_discount.png b/lessons/sem01/lesson08/images/strategy_discount.png
new file mode 100644
index 00000000..3e6e9c92
Binary files /dev/null and b/lessons/sem01/lesson08/images/strategy_discount.png differ
diff --git a/lessons/sem01/lesson08/practice.ipynb b/lessons/sem01/lesson08/practice.ipynb
new file mode 100644
index 00000000..e9f67eb7
--- /dev/null
+++ b/lessons/sem01/lesson08/practice.ipynb
@@ -0,0 +1,426 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Функции\n",
+ "\n",
+ "В книге \"Паттерны объектно-ориентированного проектирования\" рассматриваются 24 шаблона, которые, в зависимости от контекста, могут быть использованы для создания расширяемых программ. Как следует из названия книги, все паттерны проектирования в той или иной форме используют классы и различные концепции ООП. Однако все примеры в книге были написаны на С++ и Smalltalk, где подобные подходы к реализации паттернов были естественными. Авторы и сами признаются, что количество паттернов и их содержание может варьироваться от языка к языку. Так, например, в языках, не поддерживающих парадигму ООП, инкапсуляция будет выступать в роли паттерна. Так же в зависимости от того, насколько легко реализовать ту или иную вещь, реализации паттернов будут отличаться от языка к языку.\n",
+ "\n",
+ "Чтобы проиллюстрировать это суждение, сегодня мы с вами переработаем один из классических паттернов проектирования, который называется **Стратегия**, и покажем, что некоторые паттерны проектирования в Python могут быть реализованы в виде набора функций без использования классов и наследования.\n",
+ "\n",
+ "## Паттерн стратегия\n",
+ "\n",
+ "В оригинальном труде паттерн **Стратегия** описывается следующим образом:\n",
+ "\n",
+ " Определить семейство алгоритмов, инкапсулировать каждый из\n",
+ " них и сделать их взаимозаменяемыми. Стратегия позволяет заменять\n",
+ " алгоритм независимо от использующих его клиентов.\n",
+ "\n",
+ "Суть паттерна позволяет раскрыть следующая UML-диаграмма:\n",
+ "\n",
+ "\n",
+ "\n",
+ "Прокомментируем иллюстрацию:\n",
+ "\n",
+ "- `Context` - объект, который хранит входные данные для алгоритма. Как видно из диаграммы, с данным классом ассоциируется стратегия, однако сложные взаимоотношения в нашей реализации будут опущены;\n",
+ "- `AbstractStrategy` - это интерфейс стратегии, т.е. объект, который описывает множество допустимых операций, но не реализует их. Подробнее об интерфейсах мы поговорим в лекции об ООП в Python. Интерфейс стратегий объявляет метод `do_something()`, в котором и будет происходить вычисление определенного алгоритма, и определение которого ложится на классы-наследники;\n",
+ "- `Strategy...` - это наследники интерфейса `AbstractStrategy` - конкретные алгоритмы, которые реализуют метод `do_something()`. Эти классы взаимозаменяемы и в зависимости от тех или иных обстоятельств мы можем легко менять один алгоритм на другой, поскольку использование разных алгоритмов унифицировано.\n",
+ "\n",
+ "Вот так выглядит исходный паттерн. Ничего непонятно, правда? Не стоит отчаиваться, ниже мы рассмотрим практический пример и избавимся от всех этих классов и наследований путем использования обычных функций.\n",
+ "\n",
+ "## Практический пример\n",
+ "\n",
+ "Чтобы понять суть паттерна, рассмотрим следующий пример. Представим, что мы работаем в некоторой компании, которая развивает свой Интернет-магазин. Мы с вами занимаемся разработкой серверных функций. При проектировании процессов используются следующие абстракции:\n",
+ "\n",
+ "- `Item` - это некоторая позиция в Интернет-магазине, например, *банан*. `Item` описывается тремя характеристиками: названием `label`, ценой за одну единицу `price` и количеством купленных единиц `amount`;\n",
+ "- `Customer` - покупатель нашего Интернет-магазина. Покупатель описывается следующими характеристиками: `customer_id` - уникальный идентификатор пользователя, `username` - имя пользователя, `loyalty_points` - количество баллов лояльности в нашем магазине. Баллы начисляются за каждую покупку;\n",
+ "- `Order` - заказ. Заказ описывается следующими полями: `customer` - покупатель, совершивший заказ, `items` - список позиций, содержащихся в данном заказе, `price` - суммарная стоимость заказа. Также объект `Order` обладает методом `get_discounted_price()` для вычисления общей стоимости товара с учетом скидки;\n",
+ "\n",
+ "В нашем Интернет-магазине к заказу могут быть применены следующие скидки в зависимости:\n",
+ " \n",
+ "- **Скидка на основе баллов лояльности**. Если покупатель, оформивший заказ, обладает не менее 1000 баллами лояльности нашего магазина, общая скидка на заказ составляет 5%;\n",
+ "- **Скидка на основе количество товаров для данной позиции**. Если для данной позиции было заказано не менее 20 единиц товара, скидка для данной позиции составляет 10%;\n",
+ "- **Скидка на основе количества заказанных позиций**. Если в заказе содержится не менее 10 уникальных позиций, общая скидка на заказ составляет 7%;\n",
+ "\n",
+ "Алгоритмы начисления скидки и составляют собой доступные нам конкретные стратегии. Объект `Order` в свою очередь выступает в виде контекста, который предоставляет данные для начисления скидки. Согласно схеме описанной выше, мы могли бы реализовать алгоритмы начисления скидок следующим образом:\n",
+ "\n",
+ "\n",
+ "\n",
+ "Однако, поскольку алгоритмы начисления скидок не обладают внутренним состоянием, и поскольку функции в Python - это *полноправные объекты*, у нас нет нужды в реализации всех этих классов. Нам достаточно реализовать три функции, принимающие на вход объект типа `Order` и возвращающие размер скидки. Таким образом, паттерн редуцируется до реализации пары функций."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from uuid import uuid4\n",
+ "\n",
+ "from utils.models import (\n",
+ " Customer,\n",
+ " Item,\n",
+ " Order,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Вспомогательная функция"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_floats_eq(lhs: float, rhs: float, eps: float = 1e-6) -> bool:\n",
+ " \"\"\"\n",
+ " Сравнивает числа с плавающей точкой на равенство с заданной точностью.\n",
+ "\n",
+ " Args:\n",
+ " lhs: левый аргумент сравнения.\n",
+ " rhs: правый аргумент сравнения.\n",
+ " eps: точность. По умолчанию сравнение происходит с точностью до 6 знаков после запятой.\n",
+ "\n",
+ " Returns:\n",
+ " Булево значение. True, если числа равны, False - иначе.\n",
+ " \"\"\"\n",
+ " return abs(lhs - rhs) < eps"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Задание 1\n",
+ "\n",
+ "Итак, реализуйте функции начисления скидки согласно описаниям выше.\n",
+ "\n",
+ "### Скидка на основе баллов лояльности\n",
+ "\n",
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_loyalty_discount(order: Order) -> float:\n",
+ " # ваш код\n",
+ " return 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Проверка:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "test_items = [\n",
+ " Item(\n",
+ " label=\"item#1\",\n",
+ " price=25,\n",
+ " amount=2,\n",
+ " ),\n",
+ " Item(\n",
+ " label=\"item#2\",\n",
+ " price=10,\n",
+ " amount=5,\n",
+ " ),\n",
+ "]\n",
+ "\n",
+ "test_data = {\n",
+ " \"too-few-points\": (\n",
+ " Order(\n",
+ " customer=Customer(\n",
+ " customer_id=uuid4(),\n",
+ " username=\"user\",\n",
+ " loyalty_points=999,\n",
+ " ),\n",
+ " items=test_items,\n",
+ " ),\n",
+ " 0,\n",
+ " ),\n",
+ " \"equal-points\": (\n",
+ " Order(\n",
+ " customer=Customer(\n",
+ " customer_id=uuid4(),\n",
+ " username=\"user\",\n",
+ " loyalty_points=1000,\n",
+ " ),\n",
+ " items=test_items,\n",
+ " ),\n",
+ " 5,\n",
+ " ),\n",
+ " \"lot-of-points\": (\n",
+ " Order(\n",
+ " customer=Customer(\n",
+ " customer_id=uuid4(),\n",
+ " username=\"user\",\n",
+ " loyalty_points=1500,\n",
+ " ),\n",
+ " items=test_items,\n",
+ " ),\n",
+ " 5,\n",
+ " ),\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for test_case, test_data_item in test_data.items():\n",
+ " order, discount_expected = test_data_item\n",
+ " discount = get_loyalty_discount(order)\n",
+ " assert is_floats_eq(discount, discount_expected), test_case"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Скидка на основе количество товаров для данной позиции\n",
+ "\n",
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_item_amount_discount(order: Order) -> float:\n",
+ " # ваш код\n",
+ " return 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Проверка:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "test_customer = Customer(\n",
+ " customer_id=uuid4(),\n",
+ " username=\"user\",\n",
+ ")\n",
+ "\n",
+ "test_data = {\n",
+ " \"no-discount\":(\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[\n",
+ " Item(\n",
+ " label=\"item1\",\n",
+ " price=25,\n",
+ " amount=2,\n",
+ " ),\n",
+ " Item(\n",
+ " label=\"item2\",\n",
+ " price=10,\n",
+ " amount=5,\n",
+ " )\n",
+ " ]\n",
+ " ),\n",
+ " 0,\n",
+ " ),\n",
+ " \"whole-order-discount\": (\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[\n",
+ " Item(\n",
+ " label=\"item1\",\n",
+ " price=25,\n",
+ " amount=20,\n",
+ " ),\n",
+ " Item(\n",
+ " label=\"item2\",\n",
+ " price=10,\n",
+ " amount=20,\n",
+ " )\n",
+ " ]\n",
+ " ),\n",
+ " 70,\n",
+ " ),\n",
+ " \"several-position-discount\": (\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[\n",
+ " Item(\n",
+ " label=\"item1\",\n",
+ " price=25,\n",
+ " amount=2,\n",
+ " ),\n",
+ " Item(\n",
+ " label=\"item2\",\n",
+ " price=10,\n",
+ " amount=100,\n",
+ " )\n",
+ " ]\n",
+ " ),\n",
+ " 100,\n",
+ " ),\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for test_case, test_data_item in test_data.items():\n",
+ " order, discount_expected = test_data_item\n",
+ " discount = get_item_amount_discount(order)\n",
+ " assert is_floats_eq(discount_expected, discount_expected), test_case"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Скидка на основе количества заказанных позиций\n",
+ "\n",
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_general_amount_discount(order: Order) -> float:\n",
+ " # ваш код\n",
+ " return 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Проверка:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "test_customer = Customer(\n",
+ " customer_id=uuid4(),\n",
+ " username=\"user\",\n",
+ ")\n",
+ "\n",
+ "test_data = {\n",
+ " \"too-few-items\": (\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[Item(label=str(i), price=10) for i in range(9)]\n",
+ " ),\n",
+ " 0,\n",
+ " ),\n",
+ " \"equal-items\": (\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[Item(label=str(i), price=10) for i in range(10)],\n",
+ " ),\n",
+ " 7,\n",
+ " ),\n",
+ " \"lot-of-items\": (\n",
+ " Order(\n",
+ " customer=test_customer,\n",
+ " items=[Item(label=str(i), price=10) for i in range(20)],\n",
+ " ),\n",
+ " 14,\n",
+ " ),\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for test_case, test_data_item in test_data.items():\n",
+ " order, discount_expected = test_data_item\n",
+ " discount = get_general_amount_discount(order)\n",
+ " assert is_floats_eq(discount, discount_expected), test_case"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Задание 2\n",
+ "\n",
+ "Продолжим представлять, что мы работаем в Интернет-магазине. Любой Интернет-магазин пытается увеличить свою прибыль, поэтому мы хотим запретить применять больше одной скидки к заказу в нашем магазине. Однако, чтобы не потерять клиентов, руководство решило поступить следующим образом: для каждого заказа может быть применена только одна скидка - скидка дающая больший бонус покупателю. Если несколько скидок позволяют получить одинаковую выгоду, применяется любая из этих скидок. Нам необходимо реализовать функцию, которая выбирает лучшую стратегию начисления скидки из заданного набора и вычисляет итоговую стоимость заказа с учетом примененной оптимальной скидки."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# ваш код"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/homeworks/hw1/lsm_project/lsm/__init__.py b/lessons/sem01/lesson08/utils/__init__.py
similarity index 100%
rename from homeworks/hw1/lsm_project/lsm/__init__.py
rename to lessons/sem01/lesson08/utils/__init__.py
diff --git a/lessons/sem01/lesson08/utils/models.py b/lessons/sem01/lesson08/utils/models.py
new file mode 100644
index 00000000..10718441
--- /dev/null
+++ b/lessons/sem01/lesson08/utils/models.py
@@ -0,0 +1,92 @@
+from dataclasses import dataclass
+from typing import Callable
+from uuid import UUID
+
+
+@dataclass
+class Item:
+ """
+ Товар из Интернет-магазина.
+
+ Attrs:
+ label: наименования товара.
+ price: цена единицы товара.
+ amount: количество товаров. Значение по умолчанию - 1.
+ """
+ label: str
+ price: float
+ amount: int = 1
+
+
+@dataclass
+class Customer:
+ """
+ Основная информация о пользователе Интенет-магазина.
+
+ Attrs:
+ customer_id: идентификатор пользователя.
+ username: имя пользователя.
+ loyalty_points: количество баллов лояльности. Значение по умолчанию - 0.
+ """
+ customer_id: UUID
+ username: str
+ loyalty_points: int = 0
+
+
+class Order:
+ """
+ Заказ из Интернет-магазина.
+
+ Attrs:
+ customer: структура Customer с информацией о покупателе.
+ items: список купленных товаров.
+ price: стоимость заказа.
+ """
+
+ customer: Customer
+ items: list[Item]
+ price: float
+
+ def __init__(
+ self,
+ customer: Customer,
+ items: list[Item],
+ ) -> None:
+ """
+ Инициализирует заказ.
+
+ При инициализации вычисляется общая стоимость заказа.
+
+ Args:
+ customer: информация о покупателе.
+ items: список купленных товаров.
+ """
+ self.customer = customer
+ self.items = items
+ self.price = sum(
+ item.price * item.amount for item in self.items
+ )
+
+ def get_discounted_price(
+ self, apply_discount: Callable[["Order"], float]
+ ) -> float:
+ """
+ Вычисляет стоимость заказа с учетом примененной скидки.
+
+ Args:
+ apply_discount: функция для расчета скидки на основании информации о заказе.
+
+ Returns:
+ Число с плавающей точкой - стоимость заказа с учетом скидки.
+
+ Raises:
+ ValueError, если расчитанная скидка превышает общую стоимость заказа.
+ """
+ discount = apply_discount(self)
+
+ if self.price < discount:
+ raise ValueError(
+ f"discount {discount} is grater than total price {self.price}"
+ )
+
+ return self.price - discount
diff --git a/lessons/sem01/lesson09/exceptions.pptx b/lessons/sem01/lesson09/exceptions.pptx
new file mode 100644
index 00000000..8bcab3d6
Binary files /dev/null and b/lessons/sem01/lesson09/exceptions.pptx differ
diff --git a/lessons/sem01/lesson09/practice.ipynb b/lessons/sem01/lesson09/practice.ipynb
new file mode 100644
index 00000000..defdffa8
--- /dev/null
+++ b/lessons/sem01/lesson09/practice.ipynb
@@ -0,0 +1,532 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "3eb3ad14",
+ "metadata": {},
+ "source": [
+ "# Типичные ошибки"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6749c2d8",
+ "metadata": {},
+ "source": [
+ "## Неинформативное название переменных\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "713718a8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "a = 343\n",
+ "b = 3e8"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "709acdb3",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1ab12688",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "velocity_sound = 343\n",
+ "velocity_light = 3e8"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8068205d",
+ "metadata": {},
+ "source": [
+ "## Неправильное сравнение типов\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "eb2628a4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "string = \"this is string\"\n",
+ "\n",
+ "if type(string) == str:\n",
+ " print(\"var has str type\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d418e674",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "102fb661",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "string = \"this is string\"\n",
+ "\n",
+ "if isinstance(string, str):\n",
+ " print(\"var has str type\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4c023fff",
+ "metadata": {},
+ "source": [
+ "## Слишком большой уровень отступа\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "332afb88",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums = list(range(10))\n",
+ "\n",
+ "for num in nums:\n",
+ " if num % 2 == 0:\n",
+ " print(f\"{num} dividable by 2\")\n",
+ "\n",
+ " if num % 3 == 0:\n",
+ " print(f\"{num} dividable by 6\")\n",
+ "\n",
+ " if num % 5 == 0:\n",
+ " print(f\"{num} dividable by 30\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5a683f5e",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4b411297",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums = list(range(10))\n",
+ "\n",
+ "for num in nums:\n",
+ " if num % 2 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 2\")\n",
+ "\n",
+ " if num % 3 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 6\")\n",
+ "\n",
+ " if num % 5 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 30\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8377d352",
+ "metadata": {},
+ "source": [
+ "## Дублирование логики\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d168d804",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums = list(range(10))\n",
+ "\n",
+ "for num in nums:\n",
+ " if num % 2 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 2\")\n",
+ "\n",
+ " if num % 3 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 6\")\n",
+ "\n",
+ " if num % 5 != 0:\n",
+ " continue\n",
+ "\n",
+ " print(f\"{num} dividable by 30\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4409f8f8",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ad2c1add",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums = list(range(10))\n",
+ "dividers: tuple[int, ...] = (2, 3, 5)\n",
+ "\n",
+ "for num in nums:\n",
+ " dividers_product = 1\n",
+ "\n",
+ " for divider in dividers:\n",
+ " dividers_product *= divider\n",
+ "\n",
+ " if num % divider != 0:\n",
+ " break\n",
+ "\n",
+ " print(f\"{num} dividable by {dividers_product}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2d3fa92f",
+ "metadata": {},
+ "source": [
+ "## Магические константы\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "775c60d6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "status_code = 200\n",
+ "\n",
+ "if 400 <= status_code:\n",
+ " print(f\"HTTP error with code: {status_code}\")\n",
+ "\n",
+ "else:\n",
+ " print(\"OK\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "222cbfbd",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8e3198a4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Final\n",
+ "\n",
+ "\n",
+ "HTTP_CODE_BAD_REQUEST: Final[int] = 400\n",
+ "\n",
+ "status_code = 200\n",
+ "\n",
+ "if HTTP_CODE_BAD_REQUEST <= status_code:\n",
+ " print(f\"HTTP error with code: {status_code}\")\n",
+ "\n",
+ "else:\n",
+ " print(\"OK\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0098fddf",
+ "metadata": {},
+ "source": [
+ "## Изобретение велосипедов"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "83d20c34",
+ "metadata": {},
+ "source": [
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b65aaf87",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "punctuation = \"\\\"!#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "069b24fa",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bf98f6e0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from string import punctuation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1e388557",
+ "metadata": {},
+ "source": [
+ "## Неправильное ветвление в функциях\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "73b4888d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def compute_result(num: int) -> int:\n",
+ " if num % 2 == 0:\n",
+ " return num\n",
+ " \n",
+ " else:\n",
+ " return num * 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7cf9b159",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "16b04541",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def compute_result(num: int) -> int:\n",
+ " if num % 2 == 0:\n",
+ " return num\n",
+ "\n",
+ " return num * 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d94d8b94",
+ "metadata": {},
+ "source": [
+ "## Ненужное ветвление в функциях\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "82949b75",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_even(num: int) -> bool:\n",
+ " if num % 2 == 0:\n",
+ " return True\n",
+ "\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "df392496",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0e132127",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_even(num: int) -> bool:\n",
+ " return num % 2 == 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "93a2eca2",
+ "metadata": {},
+ "source": [
+ "## Неправильная проверка коллекций на пустоту\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8db92548",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums_unique = set(range(5))\n",
+ "\n",
+ "if len(nums_unique) != 0:\n",
+ " print(\"collection is not empty\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "76fd92d0",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "898f5f38",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums_unique = set(range(5))\n",
+ "\n",
+ "if nums_unique:\n",
+ " print(\"collection is not empty\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "36f1a277",
+ "metadata": {},
+ "source": [
+ "## Неправильное сравнение с None\n",
+ "\n",
+ "**Пример**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "83b72d5d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num = None\n",
+ "\n",
+ "if num == None:\n",
+ " print(\"num is None\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ebdf5b8a",
+ "metadata": {},
+ "source": [
+ "**Исправление**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "aec4c062",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num = None\n",
+ "\n",
+ "if num is None:\n",
+ " print(\"num is None\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "92af9edd",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson10/classes.pptx b/lessons/sem01/lesson10/classes.pptx
new file mode 100644
index 00000000..ec016a60
Binary files /dev/null and b/lessons/sem01/lesson10/classes.pptx differ
diff --git a/lessons/sem01/lesson10/practice.ipynb b/lessons/sem01/lesson10/practice.ipynb
new file mode 100644
index 00000000..016ab2db
--- /dev/null
+++ b/lessons/sem01/lesson10/practice.ipynb
@@ -0,0 +1,579 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Пользовательские классы"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## dataclass\n",
+ "\n",
+ "На лекции мы видели пример создания класса `Point2D`. Это простой класс, который содержит два атрибута - абсциссу и ординату, и описывает точку двумерного пространства. Класс `Point2D` может быть описан так:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Point2D:\n",
+ " abscissa: float\n",
+ " ordinate: float\n",
+ "\n",
+ " def __init__(\n",
+ " self,\n",
+ " abscissa: float = 0.,\n",
+ " ordinate: float = 0.,\n",
+ " ) -> None:\n",
+ " self.abscissa = abscissa\n",
+ " self.ordinate = ordinate"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "На примере данного кода видно, что по умолчанию классы в Python обладают рядом недостатков. Один из таких недостатков - необходимость определять конструктор, даже если конструктор не обладает никакой сложной логикой. В данном примере при инициализации экземпляра `Point2D` в конструкторе не происходит никаких сложных вычислений. Мы просто принимаем данные от вызывающей стороны и сохраняем их в соответствующие атрибуты. И для реализации такой простой логики нам пришлось самим написать конструктор, который занимает больше половины определения класса `Point2D`. И это при том, что наш объект имеет всего два атрибута. Если бы атрибутов было бы больше, нам бы пришлось проделать еще больше утомительной работы для определения генерик конструктора.\n",
+ "\n",
+ "Следующий минус - отсутствие читаемого строкового представления. Если мы попытаемся использовать экземпляр класса `Point2D` в качестве аргумента функции `print()`, то в стандартный поток вывода будет напечатана абра-кадабра. Использовать такой вывод в целях отладки или, тем более, для демонстрации пользователю крайне проблематично. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "point1 = Point2D()\n",
+ "point2 = Point2D(abscissa=3.14, ordinate=2.72)\n",
+ "\n",
+ "print(\n",
+ " f\"point1: {point1}\",\n",
+ " f\"point2: {point2}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Еще один минус заключается в логике сравнения экземпляров класса с помощью оператора `==`. В большинстве случаев под сравнением экземпляров класса с помощью оператора `==` удобно понимать сравнение с помощью оператора `==` значений соответствующих атрибутов экземпляров. Однако в Python по умолчанию сравнение экземпляров с помощью `==` происходит по `id`. Иными словами, по умолчанию результат сравнения экземпляров пользовательских классов с помощью оператора `==` соответствует результату сравнения этих же экземпляров с помощью оператора `is`. В большинстве задач это очень неудобно и нелогично."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " point1 == point2,\n",
+ " point1 == Point2D(),\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Чтобы избавиться от всех этих недостатков, в стандартной библиотеки Python существует модуль `dataclasses`. Ниже мы рассмотрим некоторые возможности данного модуля. Для детального ознакомления рекомендуем обратиться к [официальной документации](https://docs.python.org/3/library/dataclasses.html).\n",
+ "\n",
+ "Основной объект модуля `dataclasses` - декоратор `dataclass`. Использование этого декоратора в момент определение класса позволяет определить простой `__init__` на основе \"объявлени\" атрибутов класса, добавить функционал для читаемого вывода экземпляров класса в `stdout`, а также определить адекватное сравнение экземпляров с помощью оператора `==`. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from dataclasses import dataclass\n",
+ "\n",
+ "\n",
+ "@dataclass\n",
+ "class Point2D:\n",
+ " abscissa: float = 0.\n",
+ " ordinate: float = 0."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "point1 = Point2D()\n",
+ "point2 = Point2D(abscissa=3.14, ordinate=2.72)\n",
+ "\n",
+ "print(\n",
+ " f\"point1: {point1}\",\n",
+ " f\"point2: {point2}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " point1 == point2,\n",
+ " Point2D() == point1,\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "При работе с `dataclass` могут возникнуть проблемы, если нам потребуется определить атрибут, который должен иметь некоторое значение по умолчанию изменяемого типа данных."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " @dataclass\n",
+ " class ClassWithMutable:\n",
+ " lst: list[str] = []\n",
+ "\n",
+ "except ValueError as exc:\n",
+ " print(f\"error: {exc}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В этом случае следует воспользоваться объектом `dataclasses.field`. Это специальный объект, который позволяет более подробно описывать поля класса. В частности, объект `field` имеет аргумент `default_factory`. С помощью этого аргумента можно определить фабрику для создания значений по умолчанию указанного атрибута."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from dataclasses import field\n",
+ "\n",
+ "\n",
+ "@dataclass\n",
+ "class ClassWithMutable:\n",
+ " lst: list[str] = field(default_factory=list)\n",
+ "\n",
+ "\n",
+ "print(ClassWithMutable())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Также полезно знать о функциях `asdict`, `astuple` и `fields`, т.к. с их помощью можно выполнять преобразования дата-классов в объекты других типов данных. Например, с помощью функции `asdict` можно сконструировать словарь на базе переданного дата-класса. Ключами словаря будут являться строки, соответствующие идентификаторам атрибутов экземпляра данного класса. Значения словаря - значения соответствующих атрибутов."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from dataclasses import (\n",
+ " asdict,\n",
+ " astuple,\n",
+ " fields,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "@dataclass\n",
+ "class SegmentData:\n",
+ " segment_id: str\n",
+ " segment_type: str\n",
+ " segment_start: float\n",
+ " segment_end: float"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "segment_data = SegmentData(\n",
+ " segment_id=\"12345678\",\n",
+ " segment_type=\"voice\",\n",
+ " segment_start=0.0,\n",
+ " segment_end=3.14,\n",
+ ")\n",
+ "\n",
+ "print(\n",
+ " f\"dict: {asdict(segment_data)}\",\n",
+ " f\"tuple: {astuple(segment_data)}\",\n",
+ " f\"fields: {[field.name for field in fields(SegmentData)]}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Подобные преобразования могут быть полезны в различных задачах."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Задача\n",
+ "\n",
+ "Представим, что мы занимаемся разработкой некоторого стримингового сервиса. Для того, чтобы пользователи получили возможность использовать наш сервис, они должно пройти регистрацию, придумав уникальный логин и надежный пароль. Также во время регистрации пользователи должны придумать себе никнейм, однако никнеймы не обязаны быть уникальными. После регистрации пользователь получает уникальный ID в формате UUID, а его данные сохраняются в базе данных. В рамках нашей задачи под базой данных будет подразумеваться некоторый словарь.\n",
+ "\n",
+ "Однако, работать напрямую с базой данных не очень удобно. Поэтому для упрощения задач по работе с базой данных, мы решили создать обертку, которая реализует следующие операции:\n",
+ "\n",
+ "- `create_person(person: Person) -> UUID` - создает новую запись о пользователе в базе данных. Прежде, чем создать запись о пользователе, происходит проверка логина и пароля. Логин должен быть уникальным и содержать только английские буквы в верхнем и нижнем регистре, а также цифры от 0 до 9. Логин не может быть пустой строкой. Также происходит проверка надежности пароля. Пароль считается надежным, если\n",
+ " - пароль содержит хотя бы одну букву английского алфавита в верхнем регистре;\n",
+ " - пароль содержит хотя бы одну букву английского алфавита в нижнем регистре;\n",
+ " - пароль содержит хотя бы одну цифру от 0 до 9;\n",
+ " - пароль состоит не менее чем из 10 символов;\n",
+ " - пароль не содержит никаких символов, кроме разрешенных. \n",
+ " \n",
+ " Если хотя бы одна проверка не проходит, обертка должна возбудить исключение `ValueError`. Иначе, создается новая запись в базе данных. Записи присваивается уникальный UUID, который возвращается в качестве результата вызывающей стороне. Это сделано, чтобы в дальнейшем вызывающая сторона могла манипулировать созданной записью по полученному UUID.\n",
+ "\n",
+ "- `read_person(person_id: UUID) -> Person` - читает данные о пользователе из базы данных. На вход подается UUID пользователя. Если пользователя с полученным UUID нет в базе, возбуждается исключение `KeyError`. Иначе, метод читает данные о пользователе и возвращает их вызывающей стороне.\n",
+ "\n",
+ "- `update_person(person_id: UUID, person_info_new: Person) -> None` - обновляет данные пользователя. Сначала происходит проверка переданного UUID. Если пользователя с переданным UUID нет в базе данных, возбуждается исключение `KeyError`. Если пользователь с переданным UUID есть в базе данных, происходит обновление полей записи. Новые значения берутся из аргумента `person_info_new`. Поле записи обновляется, но только если значение соответствующего ему поля в `person_info_new` - не пустая строка. Иначе поле остается без изменений. Если происходит обновление пароля или логина, необходимо осуществить их проверку по правилам, описанным выше, и возбудить исключение `ValueError`, если проверка не пройдена.\n",
+ "\n",
+ "- `delete_person(person_id: UUID) -> None` - удаляет пользователя с переданным UUID из базы данных. Если пользователя с переданным UUID не было в базе данных, необходимо возбудить исключение `KeyError`.\n",
+ "\n",
+ "Ваша задача - реализовать обертку для базы данных с описанным функционалом.\n",
+ "\n",
+ "## Решение"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from dataclasses import dataclass, asdict\n",
+ "from uuid import (\n",
+ " UUID,\n",
+ " uuid4,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@dataclass\n",
+ "class Person:\n",
+ " \"\"\"\n",
+ " Информация о пользователе.\n",
+ "\n",
+ " Attrs:\n",
+ " login: логин пользователя.\n",
+ " password: пароль пользователя.\n",
+ " username: имя пользователя.\n",
+ " metadata: дополнительные сведения о пользователе.\n",
+ " \"\"\"\n",
+ "\n",
+ " login: str\n",
+ " password: str\n",
+ " username: str\n",
+ " metadata: str = \"\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class PersonDB:\n",
+ " _database: dict[UUID, Person]\n",
+ " _login_registry: dict[str, str]\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " \"\"\"Инициализирует базу данных.\"\"\"\n",
+ " # ваш код\n",
+ "\n",
+ " def create_person(self, person: Person) -> UUID:\n",
+ " \"\"\"\n",
+ " Создает новую запись о пользователе в базе данных.\n",
+ "\n",
+ " Args:\n",
+ " person: данные о пользователе, которые будут помещены в БД.\n",
+ "\n",
+ " Returns:\n",
+ " UUID - идентификатор, который будет связан с созданной записью.\n",
+ "\n",
+ " Raises:\n",
+ " ValueError, если логин или пароль не удовлетворяют требованиям.\n",
+ " \"\"\"\n",
+ " # ваш код\n",
+ " \n",
+ " def read_person_info(self, person_id: UUID) -> Person:\n",
+ " \"\"\"\n",
+ " Читает актуальные данные пользователя из базы данных.\n",
+ "\n",
+ " Args:\n",
+ " person_id: идентификатор пользователя в формате UUID.\n",
+ "\n",
+ " Returns:\n",
+ " Данные о пользователе, упакованные в структуру Person.\n",
+ "\n",
+ " Raises:\n",
+ " KeyError, если в базе данных нет пользователя с person_id.\n",
+ " \"\"\"\n",
+ " # ваш код\n",
+ " \n",
+ " def update_person_info(self, person_id: UUID, person_info_new: Person) -> None:\n",
+ " \"\"\"\n",
+ " Обновляет данные о пользователе.\n",
+ "\n",
+ " Args:\n",
+ " person_id: идентификатор пользователя в формате UUID.\n",
+ " person_info_new: модель со значениями на обновление. Будут обновлены\n",
+ " только те поля, чье значение отличается от пустой строки '',\n",
+ " остальные поля будут оставлены без изменений.\n",
+ "\n",
+ " Raises:\n",
+ " ValueError, если при обновлении логина или пароля логин или пароль\n",
+ " не прошли этап валидации.\n",
+ " KeyError, если в базе данных нет пользователя с person_id.\n",
+ " \"\"\"\n",
+ " # ваш код\n",
+ " \n",
+ " def delete_person(self, person_id: UUID) -> None:\n",
+ " \"\"\"\n",
+ " Удаляет запись о пользователе.\n",
+ "\n",
+ " Args:\n",
+ " person_id: идентификатор пользователя в формате UUID.\n",
+ "\n",
+ " Raises:\n",
+ " KeyError, если в базе данных нет пользователя с person_id.\n",
+ " \"\"\"\n",
+ " # ваш код"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Проверки\n",
+ "\n",
+ "### create_person"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "person1 = Person(\n",
+ " password=\"Aa1Bb2Cc3Dd4\",\n",
+ " login=\"login1\",\n",
+ " username=\"user#1\",\n",
+ ")\n",
+ "\n",
+ "database = PersonDB()\n",
+ "person1_id = database.create_person(person1)\n",
+ "\n",
+ "assert len(database._database) == 1\n",
+ "assert len(database._login_registry) == 1\n",
+ "assert person1_id in database._database\n",
+ "assert person1.login in database._login_registry\n",
+ "assert database._database[person1_id] == person1\n",
+ "\n",
+ "persons_wrong = {\n",
+ " \"no-login\": Person(\n",
+ " password=\"Aa1Bb2Cc3Dd4\",\n",
+ " login=\"\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ " \"existed-login\": Person(\n",
+ " password=\"Aa1Bb2Cc3Dd4\",\n",
+ " login=\"login1\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ " \"too-short-password\": Person(\n",
+ " password=\"12345\",\n",
+ " login=\"login2\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ " \"no-lower\": Person(\n",
+ " password=\"A1B2C3D4E5F\",\n",
+ " login=\"login2\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ " \"no-upper\": Person(\n",
+ " password=\"a1b2c3d4e5f\",\n",
+ " login=\"login2\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ " \"no-digits\": Person(\n",
+ " password=\"aAbBcCdDeEf\",\n",
+ " login=\"login2\",\n",
+ " username=\"user#2\",\n",
+ " ),\n",
+ "}\n",
+ "\n",
+ "for test_name, wrong_person in persons_wrong.items():\n",
+ " try:\n",
+ " database.create_person(wrong_person)\n",
+ " assert False, test_name\n",
+ "\n",
+ " except ValueError:\n",
+ " assert True\n",
+ " assert len(database._database) == 1\n",
+ " assert len(database._login_registry) == 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### read_person"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "person = database.read_person_info(person1_id)\n",
+ "assert person1 == person\n",
+ "assert len(database._database) == 1\n",
+ "assert len(database._login_registry) == 1\n",
+ "\n",
+ "try:\n",
+ " fake_id = uuid4()\n",
+ " person = database.read_person_info(fake_id)\n",
+ " assert False\n",
+ "\n",
+ "except KeyError:\n",
+ " assert True\n",
+ " assert len(database._database) == 1\n",
+ " assert len(database._login_registry) == 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### update_person"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "person2 = Person(\n",
+ " password=\"AaBbcC1234Dd\",\n",
+ " login=\"login2\",\n",
+ " username=\"user#2\"\n",
+ ")\n",
+ "person2_id = database.create_person(person2)\n",
+ "assert len(database._database) == 2\n",
+ "assert len(database._login_registry) == 2\n",
+ "assert person2_id in database._database\n",
+ "assert person2.login in database._login_registry\n",
+ "assert database._database[person2_id] == person2\n",
+ "\n",
+ "person2_updated = Person(\n",
+ " password=\"abcDEF123456\",\n",
+ " login=\"LOGIN2\",\n",
+ " username=\"user#2\",\n",
+ ")\n",
+ "person2_update = Person(\n",
+ " password=\"abcDEF123456\",\n",
+ " login=\"LOGIN2\",\n",
+ " username=\"\",\n",
+ ")\n",
+ "\n",
+ "database.update_person_info(person2_id, person2_update)\n",
+ "assert len(database._database) == 2\n",
+ "assert len(database._login_registry) == 2\n",
+ "assert person2_id in database._database\n",
+ "assert person2.login not in database._login_registry\n",
+ "assert person2_updated.login in database._login_registry\n",
+ "assert database._database[person2_id] == person2_updated"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### delete_person"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " fake_id = uuid4()\n",
+ " database.delete_person(fake_id)\n",
+ " assert False\n",
+ "\n",
+ "except KeyError:\n",
+ " assert True\n",
+ " assert len(database._database) == 2\n",
+ " assert len(database._login_registry) == 2\n",
+ "\n",
+ "database.delete_person(person2_id)\n",
+ "assert len(database._database) == 1\n",
+ "assert len(database._login_registry) == 1\n",
+ "assert person2_id not in database._database\n",
+ "assert person2_updated.login not in database._login_registry"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/lessons/sem01/lesson11/images/scheme.png b/lessons/sem01/lesson11/images/scheme.png
new file mode 100644
index 00000000..660f920c
Binary files /dev/null and b/lessons/sem01/lesson11/images/scheme.png differ
diff --git a/lessons/sem01/lesson11/oop.pptx b/lessons/sem01/lesson11/oop.pptx
new file mode 100644
index 00000000..772ce2c4
Binary files /dev/null and b/lessons/sem01/lesson11/oop.pptx differ
diff --git a/lessons/sem01/lesson11/practice.ipynb b/lessons/sem01/lesson11/practice.ipynb
new file mode 100644
index 00000000..f721d1df
--- /dev/null
+++ b/lessons/sem01/lesson11/practice.ipynb
@@ -0,0 +1,987 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "8cc8d8cf",
+ "metadata": {},
+ "source": [
+ "# Перегрузка операторов"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7740fffd",
+ "metadata": {},
+ "source": [
+ "На лекции мы рассмотрели один из принципов объектно-ориентированного программирования - **полиморфизм**. Один из вариантов реализации полиморфизма для пользовательских объектов - это перегрузка операторов и встроенных функций. В этом практическом занятии мы с вами рассмотрим примеры перегрузки, реализуя пользовательский класс `Square`. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "eca57a3a",
+ "metadata": {},
+ "source": [
+ "## repr и str"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "557051b6",
+ "metadata": {},
+ "source": [
+ "Давайте реализуем класс `Square`, который будет позволять описывать квадраты в наших программах. У класса `Square` будет один служебный атрибут - `_side` - длина стороны квадрата. Также у этого класса будет конструктор, на вход которому передается целое число - длина стороны квадрата. Переданное целое число будет сохраняться в служебный атрибут `_side`.\n",
+ "\n",
+ "Реализация может выглядеть так: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "01e05be3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b2fba60c",
+ "metadata": {},
+ "source": [
+ "Как мы знаем из предыдущего занятия, по умолчанию экземпляры пользовательских классов в Python не имеют информативного строкового представления. Т.е. если мы попытаемся получить строковое представления экземпляра класса `Square`, мы получим абсолютно неинформативную строку:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b24f8423",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square = Square(side=5)\n",
+ "print(\n",
+ " f\"repr: {square!r}\",\n",
+ " f\"str: {square!s}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b7ddd6a2",
+ "metadata": {},
+ "source": [
+ "Мы знаем, что определить вменяемое строковое представления объекта можно с помощью декоратора `dataclass`. Однако такой подход определения строковых представлений удобен далеко не всегда. Определение строкового представления объекта с помощью декоратора `dataclass` сильно ограничено. Как у разработчиков, у нас нет полного контроля за тем, как это представление будет выглядеть в итоге. С помощью `dataclass` мы не можем настроить представление под наши нужды.\n",
+ "\n",
+ "Однако мы можем пойти по другому пути и научить встроенные объекты `repr()` и `str` работать с нашим объектом по-особенному. Для этого нам необходимо определить специальные методы `__repr__` и `__str__` для нашего объекта `Square`. Каждый из этих методов не должен принимать на вход ни одного аргумента, кроме `self`, а результатом выполнения этих методов должны быть объекты строковых типов данных."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3fe17b53",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __str__(self) -> str:\n",
+ " print(\"call str\")\n",
+ " return f\"square with side '{self._side}'\"\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " print(\"call repr\")\n",
+ " return f\"Square(side={self._side})\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "05660e39",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square = Square(side=5)\n",
+ "print(\n",
+ " f\"repr: {square!r}\",\n",
+ " f\"str: {square!s}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4eb2d075",
+ "metadata": {},
+ "source": [
+ "Как видно из этого примера, теперь, при вызове встроенных объектов `repr()` и `str` с объектом типа `Square` в качестве аргумента, для получения строкового представления объекта вызываются соответствующие магические методы. Важно понять, что явно эти методы вызывать не нужно, хотя это и корректно с точки зрения языка. Однако подобный стиль является нежелательным, и другие разработчики могут вас не понять. Вызов магических методов происходит неявно внутри объектов `repr()` и `str`.\n",
+ "\n",
+ "Если `repr()` и `str` должны возвращать одинаковые строковые представления, вы можете определить только `__repr__`. В этом случае отсутствующий специальный метод `__str__` будет использовать определенную реализацию специального метода `__repr__` для получения строкового представления:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "32a515d3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5758f686",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square = Square(side=5)\n",
+ "print(\n",
+ " f\"repr: {square!r}\",\n",
+ " f\"str: {square!s}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0d6f472b",
+ "metadata": {},
+ "source": [
+ "## Логическое представление"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "73b55028",
+ "metadata": {},
+ "source": [
+ "Не лишним бывает определить логическое представление для объекта. Пусть наш объект типа `Square` имеет логическое представление `True`, если его площадь больше нуля, и `False` - иначе. Для сравнения числа с плавающей точкой с нулем будем использовать метод `isclose()` из модуля стандартной библиотеки `math`. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3c52c044",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2ea7b7d0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " bool(Square(side=5)),\n",
+ " bool(Square(side=0)),\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "86ffd698",
+ "metadata": {},
+ "source": [
+ "Чтобы иметь возможность получать логическое представление нашего объекта типа `Square`, мы научили встроенный объект `bool` работать с нашим объектом. Для этого мы определили магический метод `__bool__` в нашем объекте-класса `Square`. Этот метод принимает на вход экземпляр данного класса и возвращает объект логического типа данных. В теле данного метода мы вычисляем площадь квадрата с помощью метода `get_area()` и возвращаем результат сравнения площади с 0."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "281ab6eb",
+ "metadata": {},
+ "source": [
+ "## Операции сравнения"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f48315fb",
+ "metadata": {},
+ "source": [
+ "Перегрузим операторы `==` и `!=` для объектов типа `Square`. Будем считать, что два квадрата равны тогда и только тогда, когда равны длины их сторон. Для сравнения так же будем использовать метод `isclose()` из модуля `math`, т.к. стороны квадратов описываются числами с плавающей точкой.\n",
+ "\n",
+ "Для перегрузки операторов `==` и `!=` необходимо реализовать специальные методы `__eq__` (сокращение от *equal*) и `__ne__` (сокращение от *not equal*). На вход оба этих метода принимают текущий экземпляр класса и объект, с которым происходит сравнение. В нашем случае это объект типа `Square`. В качестве результата эти методы традиционно возвращают объекты логического типа данных. Но если логика вашей программы требует иного, вы можете вернуть из этих методов объект нужного вам типа данных. Однако делать это крайне не рекомендуется."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "43c2acd8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __ne__(self, other: Square) -> bool:\n",
+ " return not (self == other)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0547b6f0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square1 = Square(5)\n",
+ "square2 = Square(3.14)\n",
+ "\n",
+ "print(\n",
+ " square1 == square2,\n",
+ " square1 != square2,\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ca43826f",
+ "metadata": {},
+ "source": [
+ "Обратите внимание на реализацию метода `__ne__`. Метод `__ne__` был реализован, как отрицание результата выполнения метода `__eq__`. На самом деле такое определение излишне. Мы можем реализовать только метод `__eq__`, а Python по умолчанию сам будет использовать эту реализацию для определения метода `__ne__`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f80419ba",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " print(\"call __eq__\")\n",
+ " return math.isclose(self._side, other._side)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b52f0589",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square1 = Square(5)\n",
+ "square2 = Square(3.14)\n",
+ "\n",
+ "print(\n",
+ " square1 == square2,\n",
+ " square1 != square2,\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "618101ed",
+ "metadata": {},
+ "source": [
+ "Также обратите внимание, что эти методы вызываются интерпретатором неявно при обработке соответствующих операций. В самом коде явно вызывать эти методы не нужно. Вместо этого вы можете использовать соответствующие операторы напрямую."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "babfe694",
+ "metadata": {},
+ "source": [
+ "## Отношения порядка"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bd20045b",
+ "metadata": {},
+ "source": [
+ "Определим для объектов типа `Square` отношение порядка. Будем считать, что один квадрат больше другого, в том и только в том случае, когда его площадь больше.\n",
+ "\n",
+ "Для реализации отношения строгого порядка мы должны выполнить перегрузку операторов `>` и `<`. Для этого нам необходимо определить специальные методы `__gt__` (сокращение от *greater than*) и `__lt__` (сокращение от *lower than*). Оба метода на вход принимают два аргумента по аналогии с `__eq__` и `__ne__`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d462730b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " if side < 0:\n",
+ " raise ValueError(\n",
+ " \"side must be greater than or equal to 0\"\n",
+ " f\" but the next value was given: {side}\"\n",
+ " )\n",
+ "\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __gt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side > other._side\n",
+ "\n",
+ " def __lt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side < other._side\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "29c125a0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square1 = Square(5)\n",
+ "square2 = Square(3.14)\n",
+ "\n",
+ "print(\n",
+ " square1 > square2,\n",
+ " square1 < square2,\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "53f3670f",
+ "metadata": {},
+ "source": [
+ "Для определения отношения нестрогого порядка мы можем перегрузить операторы `>=` и `<=`. Делается это путем определения специальных методов `__ge__` (сокращение от *greater or equal*) и `__le__` (сокращение от *lower or equal*). Сигнатуры этих методов похожи на сигнатуры методов для определения отношения строгого порядка. Реализация этих методов предлагается вам в качестве самостоятельного упражнения."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "30e48bd5",
+ "metadata": {},
+ "source": [
+ "## Аддитивные операции"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f74cc1fd",
+ "metadata": {},
+ "source": [
+ "### Прямые операции"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "eb0320ec",
+ "metadata": {},
+ "source": [
+ "Определим операцию сложения и вычитания для наших объектов типа `Square`. Результатом сложения квадратов будет новый объект типа `Square`, площадь которого равна сумме площадей исходных квадратов. Отсюда мы сможем вычислить длину стороны нового квадрата. Аналогично определим вычитание квадратов. Результатом выполнения обеих операций должен быть новый объект типа `Square`.\n",
+ "\n",
+ "Для перегрузки бинарных операторов `+` и `-` мы должны определить специальные методы `__add__` и `__sub__`, соответственно:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b6583109",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __gt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side > other._side\n",
+ "\n",
+ " def __lt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side < other._side\n",
+ " \n",
+ " def __add__(self, other: Square) -> Square:\n",
+ " area_common = self.get_area() + other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def __sub__(self, other: Square) -> Square:\n",
+ " if self < other:\n",
+ " raise ValueError(\n",
+ " f\"impossible to subtract {other} from {self}\"\n",
+ " )\n",
+ " \n",
+ " area_common = self.get_area() - other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1325e415",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square1 = Square(3)\n",
+ "square2 = Square(4)\n",
+ "\n",
+ "print(\n",
+ " square1 + square2,\n",
+ " square2 - square1,\n",
+ " sep=\"\\n\",\n",
+ ")\n",
+ "\n",
+ "\n",
+ "try:\n",
+ " square1 - square2\n",
+ "\n",
+ "except ValueError as exc:\n",
+ " print(exc)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "669fb8c9",
+ "metadata": {},
+ "source": [
+ "Обратите внимание на реализацию метода `__sub__`. Перед началом вычитания мы проверяем, что вычитание происходит из квадрата с большей площадью, чтобы не получить отрицательную площадь.\n",
+ "\n",
+ "Помимо сложения и вычитание квадратов, давайте определим операцию сложения и вычитания с числами. При алгебраическом суммировании объекта типа `Square` с объектом числового типа данных будем считать, что объект числового типа данных представляет квадрат, длина стороны которого задана значением объекта числового типа данных. Для определения этих операций дополним определения методов `__add__` и `__sub__`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c47f24bf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "from numbers import Real\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __gt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side > other._side\n",
+ "\n",
+ " def __lt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side < other._side\n",
+ " \n",
+ " def __add__(self, other: Square | Real) -> Square:\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " area_common = self.get_area() + other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def __sub__(self, other: Square | Real) -> Square:\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " if self < other:\n",
+ " raise ValueError(\n",
+ " f\"impossible to subtract {other} from {self}\"\n",
+ " )\n",
+ " \n",
+ " area_common = self.get_area() - other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c7a70085",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square1 = Square(3)\n",
+ "square2 = Square(4)\n",
+ "\n",
+ "print(\n",
+ " square1 + square2,\n",
+ " square2 - square1,\n",
+ " square1 + 4,\n",
+ " square2 - 3,\n",
+ " sep=\"\\n\",\n",
+ ")\n",
+ "\n",
+ "\n",
+ "try:\n",
+ " square1 - square2\n",
+ "\n",
+ "except ValueError as exc:\n",
+ " print(exc)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "88e69fe1",
+ "metadata": {},
+ "source": [
+ "Реализации методов `__add__` и `__sub__` для вычисления алгебраических сумм могли бы быть эффективнее. Однако мы намеренно упростили реализацию, чтобы вам было легче читать этот код.\n",
+ "\n",
+ "Также обратите внимание на использование объекта `Real`. Опуская подробности, можно сказать, что этот объект был использован для корректной проверки типов с помощью `isinstance`. Дело в том, что объект числового типа данных может быть как целым числом, так и числом с плавающей точкой. Объект `Real` позволяет без использования сложных конструкций, типа `isinstance(obj, int | float)`, проверить, что объект является или целым числом, или числом с плавающей точкой."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "73a23fe2",
+ "metadata": {},
+ "source": [
+ "### Отраженные операции"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6b2f0e5a",
+ "metadata": {},
+ "source": [
+ "Текущая реализация объекта `Square` имеет существенный недостаток. Операция сложения объекта типа `Square` с объектом числового типа не коммутативна:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5586ec23",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " 1 + Square(5)\n",
+ "\n",
+ "except TypeError as exc:\n",
+ " print(exc)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "33506bb9",
+ "metadata": {},
+ "source": [
+ "Это происходит потому, что при сложении объектов с помощью бинарного оператора `+`, интерпретатор действует так:\n",
+ "- Если у левого операнда определен специальный метод `__add__`, интерпретатор вызывает его. Иначе интерпретатор переходит к анализу правого операнда.\n",
+ "- Если был вызван метод `__add__` левого операнда и в результате его выполнения был получен осмысленный результат, выполнение прекращается. \n",
+ "- Если был вызван метод `__add__` левого операнда и в результате его выполнения был получен объект-синглтон `NotImplemented`, интерпретатор переходит к анализу правого операнда.\n",
+ "- При анализе правого операнда интерпретатор проверяет наличие у него специального метода `__radd__`. Если этот метод не определен, результат выполнения операции - исключение типа `TypeError`.\n",
+ "- Если `__radd__` определен и результат его выполнения - `NotImplemented`, интерпретатор возбудит исключение.\n",
+ "- Если `__radd__` определен и результат его выполнения - не `NotImplemented`, интерпретатор вернет полученный объект в качестве результата выполнения бинарной операции.\n",
+ "\n",
+ "Проиллюстрируем эту логику работы бинарной операции `+` схемой:\n",
+ "\n",
+ "\n",
+ "\n",
+ "Логика работы прочих бинарных арифметических операций аналогична.\n",
+ "\n",
+ "Т.е. для того, чтобы операция сложения была коммутативной при работе с объектами числовых типов данных и объектами типа `Square`, мы должны реализовать специальный метод `__radd__`. `r` в имение метода указывает на то, что метод вызывается у операнда, расположенного справа от оператора."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e9ea5e92",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "from numbers import Real\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __gt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side > other._side\n",
+ "\n",
+ " def __lt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side < other._side\n",
+ " \n",
+ " def __add__(self, other: Square | Real) -> Square:\n",
+ " if not isinstance(other, Square | Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " area_common = self.get_area() + other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ " \n",
+ " def __radd__(self, other: Real) -> Square:\n",
+ " return self + other\n",
+ "\n",
+ " def __sub__(self, other: Square | Real) -> Square:\n",
+ " if not isinstance(other, Square | Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " if self < other:\n",
+ " raise ValueError(\n",
+ " f\"impossible to subtract {other} from {self}\"\n",
+ " )\n",
+ " \n",
+ " area_common = self.get_area() - other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cc264be3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " Square(3) + 4,\n",
+ " 4 + Square(3),\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "48eee8ba",
+ "metadata": {},
+ "source": [
+ "Обратите внимание, что мы также добавили логику работы с объектом `NotImplemented` в методы, реализующие прямые аддитивные операции. Это сделано для того, чтобы другие пользовательские объекты, в случае необходимости, могли реализовать свою логику работы с нашим классом при выполнении арифметических операций."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e9f40244",
+ "metadata": {},
+ "source": [
+ "## Мультипликативные операции"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "937d5e76",
+ "metadata": {},
+ "source": [
+ "Определим операцию умножения и деления объекта типа `Square` на объект числового типа данных. Умножение квадрата на число эквивалентно созданию нового квадрата, площадь которого равна площади исходного квадрата, увеличенной в указанное число раз. Деление квадрата на число эквивалентно созданию нового квадрата, площадь которого равна площади исходного квадрата, уменьшенной в указанное число раз.\n",
+ "\n",
+ "Для перегрузки операторов `*` и `/` необходимо определить методы `__mul__` и `__truediv__`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "413d2767",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "from numbers import Real\n",
+ "\n",
+ "\n",
+ "class Square:\n",
+ " _side: float\n",
+ "\n",
+ " def __init__(self, side: float) -> None:\n",
+ " self._side = side\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " return f\"square with side '{self._side}'\"\n",
+ " \n",
+ " def __bool__(self) -> bool:\n",
+ " return not math.isclose(self.get_area(), 0)\n",
+ " \n",
+ " def __eq__(self, other: Square) -> bool:\n",
+ " return math.isclose(self._side, other._side)\n",
+ " \n",
+ " def __gt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side > other._side\n",
+ "\n",
+ " def __lt__(self, other: Square) -> bool:\n",
+ " return self != other and self._side < other._side\n",
+ " \n",
+ " def __add__(self, other: Square | Real) -> Square:\n",
+ " if not isinstance(other, Square | Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " area_common = self.get_area() + other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ " \n",
+ " def __radd__(self, other: Real) -> Square:\n",
+ " return self + other\n",
+ "\n",
+ " def __sub__(self, other: Square | Real) -> Square:\n",
+ " if not isinstance(other, Square | Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " if isinstance(other, Real):\n",
+ " other = Square(side=other)\n",
+ "\n",
+ " if self < other:\n",
+ " raise ValueError(\n",
+ " f\"impossible to subtract {other} from {self}\"\n",
+ " )\n",
+ " \n",
+ " area_common = self.get_area() - other.get_area()\n",
+ " return Square(side=area_common ** 0.5)\n",
+ "\n",
+ " def __mul__(self, scale: Real) -> Square:\n",
+ " if not isinstance(scale, Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " area = self.get_area() * scale\n",
+ " return Square(side=area ** 0.5)\n",
+ "\n",
+ " def __truediv__(self, scale: Real) -> Square:\n",
+ " if not isinstance(scale, Real):\n",
+ " return NotImplemented\n",
+ "\n",
+ " return self * (1 / scale)\n",
+ "\n",
+ " def get_area(self) -> float:\n",
+ " return self._side ** 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f9197cca",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "square = Square(2)\n",
+ "\n",
+ "print(\n",
+ " square * 4,\n",
+ " square / 4,\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "30a074bd",
+ "metadata": {},
+ "source": [
+ "Методы `__mul__` и `__truediv__` являются прямыми. Они также имеют отраженные аналоги `__rmul__` и `__rtruediv__`, соответственно. Реализация отраженного умножения предлагается вам в качестве самостоятельного упражнения."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c1dbd554",
+ "metadata": {},
+ "source": [
+ "## Материалы"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3b1011af",
+ "metadata": {},
+ "source": [
+ "В данном занятии были рассмотрены далеко не все специальные методы для перегрузки операторов и встроенных функций. Для детального ознакомления с полным списком специальных методов вы можете воспользоваться [официальной документацией](https://docs.python.org/3/library/operator.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "51f40d1b",
+ "metadata": {},
+ "source": [
+ "## Задание"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "090640ea",
+ "metadata": {},
+ "source": [
+ "По [данной ссылке](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/conditions/lesson11/task.md) вы найдете самостоятельное задание. Задание необходимо выполнить до 23:59 7 декабря."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson12/important_info.txt b/lessons/sem01/lesson12/important_info.txt
new file mode 100644
index 00000000..62dae443
--- /dev/null
+++ b/lessons/sem01/lesson12/important_info.txt
@@ -0,0 +1,18 @@
+ ⡴⠑⡄⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+⠸⡇⠀⠿⡀⠀⠀⠀⣀⡴⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠑⢄⣠⠾⠁⣀⣄⡈⠙⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⢀⡀⠁⠀⠀⠈⠙⠛⠂⠈⣿⣿⣿⣿⣿⠿⡿⢿⣆⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⢀⡾⣁⣀⠀⠴⠂⠙⣗⡀⠀⢻⣿⣿⠭⢤⣴⣦⣤⣹⠀⠀⠀⢀⢴⣶
+⠀⠀⢀⣾⣿⣿⣿⣷⣮⣽⣾⣿⣥⣴⣿⣿⡿⢂⠔⢚⡿⢿⣿⣦⣴⣾⠁⠸⣼
+⠀⢀⡞⠁⠙⠻⠿⠟⠉⠀⠛⢹⣿⣿⣿⣿⣿⣌⢤⣼⣿⣾⣿⡟⠉⠀⠀⠀⠀
+⠀⣾⣷⣶⠇⠀⠀⣤⣄⣀⡀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀
+⠀⠉⠈⠉⠀⠀⢦⡈⢻⣿⣿⣿⣶⣶⣶⣶⣤⣽⡹⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⠀⠉⠲⣽⡻⢿⣿⣿⣿⣿⣿⣿⣷⣜⣿⣿⣿⡇⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⣶⣮⣭⣽⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀
+⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠛⠉
+Congrats, you found a shrek in the code
+this means that you're now shrekd for the
+rest of your life
diff --git a/lessons/sem01/lesson12/practice.ipynb b/lessons/sem01/lesson12/practice.ipynb
new file mode 100644
index 00000000..8276cc39
--- /dev/null
+++ b/lessons/sem01/lesson12/practice.ipynb
@@ -0,0 +1,616 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "35a03765",
+ "metadata": {},
+ "source": [
+ "# Контекстные менеджеры и генераторы"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4660e657",
+ "metadata": {},
+ "source": [
+ "## Контекстные менеджеры"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e1392478",
+ "metadata": {},
+ "source": [
+ "На занятии про исключения мы впервые столкнулись с контекстными менеджерами. Напомним, что контекстные менеджеры - это объекты, которые могут быть использованы в конструкции `with`. Обычно контекстные менеджеры используются для гарантированного выполнения каких-либо действий после выполнения вашего блока кода. К типичным действиям, выполняемым с помощью контекстных менеджеров, относится освобождение ресурсов ОС: закрытие файлового дескриптора, закрытие сетевого соединения и т.д.\n",
+ "\n",
+ "Ярким примером использования контекстного менеджера является работа с файлами:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a527fcb3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "path_to_file = \"./important_info.txt\"\n",
+ "\n",
+ "with open(path_to_file, encoding=\"utf-8\") as file:\n",
+ " print(\"\".join(file.readlines()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "57ee4941",
+ "metadata": {},
+ "source": [
+ "В данном случае использование `with` с результатом выполнения функции `open` гарантирует закрытие файлового дескриптора в независимости от того, произойдет ли какая-либо ошибка в теле блока `with`.\n",
+ "\n",
+ "До сегодняшнего дня работа контекстных менеджеров была похожа на магию. Однако на самом деле никакой магии нет. Все дело в протоколе контекстного менеджера. Любой объект, который удовлетворяет этому протоколу, может использоваться в заголовке блока `with`. Рассмотрим протокол контекстного менеджера подробнее:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "250471b7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from types import TracebackType\n",
+ "from typing import Optional\n",
+ "\n",
+ "\n",
+ "class MyContextManager:\n",
+ " def __enter__(self) -> None:\n",
+ " print(\"call __enter__\")\n",
+ "\n",
+ " def __exit__(\n",
+ " self,\n",
+ " exc_type: Optional[type[BaseException]],\n",
+ " exc_val: Optional[BaseException],\n",
+ " exc_tb: Optional[TracebackType],\n",
+ " ) -> bool:\n",
+ " print(\n",
+ " \"call __exit__\",\n",
+ " f\"exc_type: {exc_type}\",\n",
+ " f\"exc_val: {exc_val}\",\n",
+ " f\"exc_tb: {exc_tb}\",\n",
+ " sep=\"\\n\",\n",
+ " )\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c39deed0",
+ "metadata": {},
+ "source": [
+ "Для того, чтобы объект удовлетворял протоколу контекстного менеджера, для него необходимо реализовать специальные методы `__enter__` и `__exit__`:\n",
+ "- `__enter__` - вызывается до выполнения тела блока `with`. В этом методе обычно происходит подготовка контекстного менеджера. Сам метод не принимает на вход никаких параметров, кроме данного экземпляра класса. В качестве возвращаемого значения метода `__enter__` можно использовать любой объект. Однако на практике чаще всего в качестве результата выполнения `__enter__` используется `None`, или данный экземпляр класса.\n",
+ "- `__exit__` - вызывается после выполнения тела блока `with` или после возникновения исключения в нем. Помимо данного экземпляра класса, в качестве параметров в этот метод неявно могут быть переданы тип возбужденного исключения, само возбужденное исключение и трейсбек. По умолчанию в качестве этих аргументов передается `None`. `__exit__` возвращает `True` в том случае, если возбужденное исключение было обработано в данном методе, и `False` - иначе."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1044c2d6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with MyContextManager():\n",
+ " print(\"do something\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "73f0498b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with MyContextManager():\n",
+ " raise Exception(\"something was broken\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f6c06293",
+ "metadata": {},
+ "source": [
+ "В некоторых случаях контекстные менеджеры используется не только для захвата ресурсов, но и для предоставления интерфейса по работе с этими ресурсами. В таких случаях в `__enter__` полезно возвращать сам созданный экземпляр. Тогда пользователь сможет связать результат выполнения `__enter__` с переменной и использовать ее в теле блока `with`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7d9961ad",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Any, Self\n",
+ "\n",
+ "\n",
+ "class ConnectionDB:\n",
+ " _is_closed: bool\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def __enter__(self) -> Self:\n",
+ " return self.connect()\n",
+ "\n",
+ " def __exit__(self, *_: Any) -> bool:\n",
+ " self.close()\n",
+ " return False\n",
+ " \n",
+ " def connect(self) -> Self:\n",
+ " if not self._is_closed:\n",
+ " raise RuntimeError(\"connection is already opened\")\n",
+ " \n",
+ " print(\"open connection to database\")\n",
+ " self._is_closed = False\n",
+ " return self\n",
+ "\n",
+ " def close(self) -> None:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\"connection is already closed\")\n",
+ "\n",
+ " print(\"close connection to database\")\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def get_user_amount(self) -> int:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\n",
+ " \"impossible to send request without opened connection\"\n",
+ " )\n",
+ "\n",
+ " return 42"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "77ffd510",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with ConnectionDB() as connection:\n",
+ " user_amount = connection.get_user_amount()\n",
+ " print(f\"user_amount: {user_amount}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "599e6433",
+ "metadata": {},
+ "source": [
+ "Альтернативное решение:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a3e331c8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Any\n",
+ "\n",
+ "\n",
+ "class ConnectionDB:\n",
+ " _is_closed: bool\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def connect(self) -> None:\n",
+ " if not self._is_closed:\n",
+ " raise RuntimeError(\"connection is already opened\")\n",
+ " \n",
+ " print(\"open connection to database\")\n",
+ " self._is_closed = False\n",
+ "\n",
+ " def close(self) -> None:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\"connection is already closed\")\n",
+ "\n",
+ " print(\"close connection to database\")\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def get_user_amount(self) -> int:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\n",
+ " \"impossible to send request without opened connection\"\n",
+ " )\n",
+ "\n",
+ " return 42\n",
+ "\n",
+ "\n",
+ "class ConnectionDBManager:\n",
+ " _connection: Optional[ConnectionDB]\n",
+ "\n",
+ " def __enter__(self) -> ConnectionDB:\n",
+ " self._connection = ConnectionDB()\n",
+ " self._connection.connect()\n",
+ " return self._connection\n",
+ "\n",
+ " def __exit__(self, *_: Any) -> bool:\n",
+ " self._connection.close()\n",
+ " self._connection = None\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8b2003ea",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with ConnectionDBManager() as connection:\n",
+ " user_amount = connection.get_user_amount()\n",
+ " print(f\"user_amount: {user_amount}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9706eff0",
+ "metadata": {},
+ "source": [
+ "## Генераторы"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d37db400",
+ "metadata": {},
+ "source": [
+ "На лекции мы рассмотрели протокол итерируемого объекта. До этого мы уже встречались с итерируемыми объектами, и все они были коллекциями, например, списки, словари и т.д. У всех них есть особенность: они хранят данные в памяти. Но в некоторых случаях эффективнее не хранить все данные, а вычислять их на лету, особенно, когда объемы данных слишком большие. \n",
+ "\n",
+ "Примером объекта, который не хранит в себе данные, а вычисляет их на лету, является встроенный объект `map`. Этот объект позволяет применить некоторую функцию ко всем элементам некоторого итерируемого объекта на лету, не создавая новую коллекцию."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "00932f96",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import sys\n",
+ "\n",
+ "objects_amount = 100\n",
+ "squares = [i ** 2 for i in range(objects_amount)]\n",
+ "squares_map = map(lambda x: x ** 2, range(objects_amount))\n",
+ "\n",
+ "print(\n",
+ " f\"size of list: {sys.getsizeof(squares)} bytes\",\n",
+ " f\"size of map: {sys.getsizeof(squares_map)} bytes\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ba5c0c91",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for square in squares_map:\n",
+ " print(square, end=\" \")\n",
+ "\n",
+ "for square in squares_map:\n",
+ " print(square, end=\" \")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c02e8c79",
+ "metadata": {},
+ "source": [
+ "Как видно из данного примера, объект `map` ведет себя как итератор. Фактически `map` и является итератором. Однако каждый раз вручную создавать итератор для того, чтобы вычислять некоторые значения на лету - утомительное занятие. Поэтому в Python существуют специальные объекты - генераторы. Фактически, генераторы являются частными случаями итераторов. Они также реализуют специальные методы `__iter__` и `__next__`. Однако они это делают неявно, и вам не придется определять их вручную. Также генераторы обладают дополнительными методами, но их в нашем курсе мы рассматривать не будем.\n",
+ "\n",
+ "Простейший способ создать генератор - использовать генераторное выражение. Синтаксис генераторного выражения очень похож на уже знакомые нам списковые, словарные и множественные включения, однако вместо квадратных и фигурных скобочек используются круглые скобки:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fbfde5ee",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import sys\n",
+ "\n",
+ "objects_amount = 1000\n",
+ "squares = [i ** 2 for i in range(objects_amount)]\n",
+ "squares_gen = (i ** 2 for i in range(objects_amount))\n",
+ "\n",
+ "print(\n",
+ " f\"size of list: {sys.getsizeof(squares)} bytes\",\n",
+ " f\"size of gen: {sys.getsizeof(squares_gen)} bytes\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f4021635",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(type(squares_gen).__name__)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b39d980f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for square in squares_gen:\n",
+ " print(square, end=\" \")\n",
+ "\n",
+ "for square in squares_gen:\n",
+ " print(square, end=\" \")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2ba3cdba",
+ "metadata": {},
+ "source": [
+ "Генераторные выражения полезны, но они имеют существенное ограничение: мы не можем реализовать произвольную по сложности логику создания и обработки данных. Для обхода этого ограничения в Python существуют генераторные функции. Чтобы сделать обычную функцию генераторной, необходимо использовать ключевое слово `yield` в теле этой функции:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cd82c628",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Generator\n",
+ "\n",
+ "\n",
+ "def get_fibonachi_sequence() -> Generator[int, None, None]:\n",
+ " num1, num2 = 0, 1\n",
+ "\n",
+ " while True:\n",
+ " yield num2\n",
+ " num1, num2 = num2, num1 + num2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e0f7d3d5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "fibonachi_gen = get_fibonachi_sequence()\n",
+ "\n",
+ "print(\n",
+ " f\"func type: {type(get_fibonachi_sequence).__name__}\",\n",
+ " f\"func res type: {type(fibonachi_gen).__name__}\",\n",
+ " sep=\"\\n\",\n",
+ ")\n",
+ "\n",
+ "for _ in range(5):\n",
+ " print(next(fibonachi_gen))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3080760e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "next(fibonachi_gen)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f53496a8",
+ "metadata": {},
+ "source": [
+ "С помощью функции `get_fibonachi_sequence()` мы получили бесконечный генератор чисел из последовательности Фибоначчи.\n",
+ "\n",
+ "Часто в теле генераторных функциях приходится итерироваться по некоторым итерируемым объектам и \"производить\" данные из этих итерируемых объектов. Специально для таких случаев существует конструкция `yield from`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a4661bcb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Generator\n",
+ "\n",
+ "\n",
+ "def pyramid_range(stop: int) -> Generator[int, None, None]:\n",
+ " yield from range(stop)\n",
+ " yield from range(stop, -1, -1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c6dca3c2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for i in pyramid_range(3):\n",
+ " print(i, end=\" \")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "17f8ef13",
+ "metadata": {},
+ "source": [
+ "Обращаем ваше внимание, что генераторы - это частные случаи итераторов. Т.е. генераторы одноразовые: вы сможете сгенерировать значения с помощью данного генератора всего один раз. При повторном использовании генератора с функцией `next()` вы будете получать исключение `StopIteration`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "87749f27",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pyramid_gen = pyramid_range(3)\n",
+ "\n",
+ "for i in pyramid_gen:\n",
+ " print(i, end=\" \")\n",
+ " \n",
+ " if i == 3:\n",
+ " break\n",
+ " \n",
+ "print()\n",
+ "\n",
+ "for i in pyramid_gen:\n",
+ " print(i, end=\" \")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4db71a48",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pyramid_gen = pyramid_range(3)\n",
+ "\n",
+ "for i in pyramid_gen:\n",
+ " print(i, end=\" \")\n",
+ "\n",
+ "next(pyramid_gen)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9382dec3",
+ "metadata": {},
+ "source": [
+ "## Контекстные менеджеры и генераторы"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "124f8d1d",
+ "metadata": {},
+ "source": [
+ "Думаем, вы оценили всю избыточность синтаксиса при определении своих контекстных менеджеров. Когда контекстные менеджеры делают очень простые вещи, определять целый класс со специальными методами для этих целей не нужно. Вместо этого можно описать контекстный менеджер с помощью генератора и декоратора `contextmanager` из модуля стандартной библиотеки `contextlib`.\n",
+ "\n",
+ "Давайте перепишем наш пример с `ConnectionDB`, используя новый подход:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a811df5d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from contextlib import contextmanager\n",
+ "from typing import Generator\n",
+ "\n",
+ "\n",
+ "class ConnectionDB:\n",
+ " _is_closed: bool\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def connect(self) -> None:\n",
+ " if not self._is_closed:\n",
+ " raise RuntimeError(\"connection is already opened\")\n",
+ " \n",
+ " print(\"open connection to database\")\n",
+ " self._is_closed = False\n",
+ "\n",
+ " def close(self) -> None:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\"connection is already closed\")\n",
+ "\n",
+ " print(\"close connection to database\")\n",
+ " self._is_closed = True\n",
+ "\n",
+ " def get_user_amount(self) -> int:\n",
+ " if self._is_closed:\n",
+ " raise RuntimeError(\n",
+ " \"impossible to send request without opened connection\"\n",
+ " )\n",
+ "\n",
+ " return 42\n",
+ "\n",
+ "\n",
+ "@contextmanager\n",
+ "def create_connection() -> Generator[ConnectionDB, None, None]:\n",
+ " connection = ConnectionDB()\n",
+ " connection.connect()\n",
+ "\n",
+ " try:\n",
+ " yield connection\n",
+ "\n",
+ " finally:\n",
+ " connection.close()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8927035d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with create_connection() as connection:\n",
+ " user_amount = connection.get_user_amount()\n",
+ " print(f\"user_amount: {user_amount}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "689aefec",
+ "metadata": {},
+ "source": [
+ "Суть данного подхода заключается в следующем. Весь код, который расположен до `yield`, соответствует телу метода `__enter__`. Инструкция `yield` соответствует инструкции `return` в теле `__enter__`. Весь код, который расположен после инструкции `yield`, соответствует коду в теле метода `__exit__`. `try`-`finally` добавлен для гарантии выполнения кода, соответствующего освобождению ресурсов.\n",
+ "\n",
+ "Как видим из примера, при гораздо меньших усилиях нам удалось достичь того же поведения, что и в исходном примере."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c980c9ce",
+ "metadata": {},
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/sem01/lesson12/protocols.pptx b/lessons/sem01/lesson12/protocols.pptx
new file mode 100644
index 00000000..5fb37b77
Binary files /dev/null and b/lessons/sem01/lesson12/protocols.pptx differ
diff --git a/lessons/sem01/lesson13/modules.pptx b/lessons/sem01/lesson13/modules.pptx
new file mode 100644
index 00000000..226dcf90
Binary files /dev/null and b/lessons/sem01/lesson13/modules.pptx differ
diff --git a/lessons/sem02/lesson01/conspect.md b/lessons/sem02/lesson01/conspect.md
new file mode 100644
index 00000000..bc179702
--- /dev/null
+++ b/lessons/sem02/lesson01/conspect.md
@@ -0,0 +1,273 @@
+# Менеджер пакетов и виртуальное окружение
+
+В этом семестре нам так или иначе придется работать с библиотеками, не являющимися частью стандартной библиотеки Python (так называемые *third party software*). Минимальный набор действий, которые необходимо совершить для работы с подобными библиотеками - скачивание и установка. Однако, если вы хотите организовать комфортную работу над несколькими проектами с большим числом различных, часто несовместимых между собой, зависимостей, а также иметь возможность поделиться своими проектами с прочими разработчиками - вам необходимо нечто большее, чем простое скачивание и установка пакетов с софтом. Именно этому и будет посвящено наше сегодняшнее занятие.
+
+## pip
+
+Несмотря на то, что Python обладает очень богатой стандартной библиотекой, существующих модулей порой может быть недостаточно, чтобы решать некоторые специфичные задачи в определенных доменах. Собственно, для решения таких задач силами сообщества Python и разрабатываются различные библиотеки. Сегодня количество библиотек для Python настолько велико, что с большой долей вероятности вы сможете найти готовое программное решение для вашей конкретной проблемы. Так сообществом Python были созданы библиотеки для обработки больших массивов числовых данных, создания и обучения глубоких нейронных сетей, веб-разработки, расчетов передаточных функций в соответствии с теорией автоматического управления и т.д. и т.п.
+
+Для того чтобы как-то облегчить разработчикам сценарии по работе с таким большим многообразием сторонних библиотек, в Python реализован свой собственный пакетный менеджер, который называется PIP. Название утилиты - рекурсивный акроним, который расшифровывается, как *pip install packages* (*pip устанавливает библиотеки*). Т.к. сторонние библиотеки стали настолько важной частью языка, что начиная с версии 3.4 PIP распространяется вместе с интерпретатором. Если вы следовали мануалу по установке [интерпретатора](../../../docs/guide.pdf), то скорее всего pip уже установлен на ваш компьютер. Иначе вам придется установить его вручную самостоятельно. Для этого можно воспользоваться, например, [этим мануалом](https://pip.pypa.io/en/stable/installation/).
+
+### Проверяем наличие pip
+
+Чтобы удостовериться, что PIP установлен и добавлен в path, выполним следующую команду:
+
+*Windows (cmd, не PowerShell)*:
+```console
+where pip
+```
+
+*Linux/MacOS*:
+```console
+which pip
+```
+
+Данная команда позволяет получить абсолютный путь до утилиты PIP. В случае, если на вашем компьютере установлено несколько версий Python, команда выведет несколько путей. Если команда не вывела ни одного пути, значить вам нужно или переустановить pip, или добавить путь до pip в path. На Windows это делается через изменение Переменных локальных сред, на Unix-подобных системах - через редактирование переменной **PATH**.
+
+### pip install - простейший вид
+
+Обычно перед началом работы над каким-либо проектом на языке Python первое с чего начинают разработку - создание виртуального окружения для данного проекта. Чуть позже мы с вами разберемся, как это работает, и почему это делают, сейчас же не будем особо задумываться и просто выполним команды ниже:
+
+*Windows*:
+```console
+python -m venv venv
+.\venv\Scripts\activate
+```
+
+*Linux/MacOS*:
+```console
+python -m venv venv
+source venv/bin/activate
+```
+
+После создания и активации виртуального окружения мы можем приступать к скачиванию и установке нужных нам библиотек. Для этого мы можем использовать pip двумя разными способами: как самостоятельную утилиту или как модуль python. Все команды pip могут быть исполнены используя оба этих подхода.
+
+```console
+pip install [ ...]
+python -m pip install [ ...]
+```
+
+В обоих случаях нами была выполнена команда `pip install`, которая делает следующее:
+- Ищет последнюю версию библиотеки с указанным именем в [PyPI](https://pypi.org/) (Python Packet Index) - специальном удаленном хранилище, в котором разработчики публикуют свои библиотеки;
+- Ищет все зависимости для указанной библиотеки;
+- Скачивает и устанавливает библиотеку и её зависимости в текущее виртуальное окружение;
+
+В простейшем случае команда `pip install` в качестве аргумента получает имя библиотеки, однако, как видно из примера выше, вы можете передать данной команде сразу несколько идентификаторов пакетов.
+
+### pip list - просмотр текущих зависимостей
+
+С помощью команды `pip list` вы можете получить информацию о библиотеках, установленных в активное виртуальное окружение. Данная информация представляется в виде удобной таблички, в которой также указаны используемые версии библиотек:
+
+*Вызов в консоле*:
+```console
+pip list
+```
+
+*Пример результата*:
+```console
+Package Version
+----------------- ----------
+flake8 6.1.0
+matplotlib 3.8.0
+numpy 1.26.1
+Pillow 10.0.1
+pydantic 2.5.2
+```
+
+### pip show - просмотр информации о библиотеке
+
+С помощью команды `pip show ` вы можете просмотреть мета информацию библиотеки. Это может быть полезным для того, чтобы выяснить, является ли данная библиотека зависимостью для прочих библиотек в текущем виртуальном окружении, и как следствие, можно ли ее безопасно удалить.
+
+*Вызов в консоле*:
+```console
+pip show numpy
+```
+
+*Пример результата*:
+```console
+Name: numpy
+Version: 1.26.1
+Summary: Fundamental package for array computing in Python
+Home-page: https://numpy.org
+...
+Location: C:\Users\Michail\Desktop\mipt\teaching\python_mipt_dafe\venv\Lib\site-packages
+Requires:
+Required-by: contourpy, matplotlib
+```
+
+### pip install - продвинутое использование
+
+#### Custom Index
+Не все библиотеки публикуются в PyPI. Иногда существует необходимость в создании своего собственного индекса и распространении определенной библиотеки исключительно через подобный кастомный индекс. Например, подобное имеет место в государственных организациях, связанных с ОПК. В этом случае вы можете явно сообщить pip'у по какому адресу находится требуемый индекс, и где именно следует искать необходимую библиотек. Для этого существует специальная опция `-i` или ее полная форма `--index-url`.
+
+*Пример использования*:
+```console
+pip install -i https://index-url.com
+```
+
+#### GitHub
+Также некоторое количество полезных библиотек публикуются в виде репозиториев на GitHub. В этом случае у вас также есть возможность установить библиотеку напрямую из репозитория явно сообщив pip'у, что поиск долже происходить на GitHub.
+
+*Пример*:
+```console
+pip install git+https://github.com/repo/package
+```
+
+#### Редактируемый режим
+Во время работы над собственной библиотекой, обычно осуществляют установку этой библиотеки в редактируемом режиме в активное виртуальное окружение. Это решает две проблемы:
+- При разработке вам не нужно прописывать сложные пути импортов;
+- Вам не придется переустанавливать библиотеку каждый раз при внесении изменений, т.к. в редактируемом режиме интерпретатор делает это за вас самостоятельно;
+
+Для установки библиотеки в редактируемом режиме, необходимо переместиться в деректорию с `setup.py` файлом, в котором описаны детали библиотеки, и выполнить команду `pip install` с флагом `-e`.
+
+*Пример*:
+```console
+pip install -e .
+```
+
+В данном примере мы устанавливаем библиотеку, описание которой хранится в текущий директории, а флаг `-e` указывает на то, что мы хотим установить ее в редактируемом режиме.
+
+### Requirements
+
+Обычно для реализации реальных проектов вам требуется использовать большое количество различных библиотек. При отправке кода на удаленный сервер для дальнейшего запуска или при отправке кода другому разработчику, вам необходимо продумать момент с установкой всех зависимостей, необходимых для корректной работы вашего проекта. Для упрощения процесса установки всех зависимостей и были придуманы списки зависимостей.
+
+Список зависимостей представляет собой текстовый файл, в котором перечислены имена библиотек, необходимые для работы с данным проектом. Обычно этот файл называется `requirements` и имеет расширение `.txt`. Название файла - всего лишь соглашение в сообществе, вы можете использовать любое другое имя, однако делать это не рекомендуется.
+
+В простейшем случае список зависимостей выглядит следующим образом:
+
+*requirements.txt*:
+```txt
+matplotlib
+pandas
+numpy
+```
+
+Список зависимостей легко использовать для настройки виртуального окружения данного проекта. Чтобы скачать нужные библиотеки, необходимо выполнить следующую команду:
+
+```console
+pip instal -r requirements.txt
+```
+
+Однако, использовать requirements.txt в описанном выше виде не всегда удобно. Подумайте, почему?
+
+Для избежания проблем с версионированием библиотек достаточно указать необходимую версию через символы `==` в списке последовательностей:
+
+*requirements.txt*:
+```txt
+matplotlib==3.8.2
+pandas==2.1.4
+numpy==1.26.3
+```
+
+Также существует возможность указывать диапазон версий. В этом случае pip скачает и установит библиотеку, с максимальной версией, удовлетворяющей заданному диапазону. Выглядит это следующим образом.
+
+*requirements.txt*:
+```txt
+matplotlib>=3.8.2
+pandas>=2.1.4, <2.5.1
+numpy==1.26.3
+```
+
+В этом случае pip установит самую свежую версию библиотеки matplotlib, причем версия будет не ниже версии 3.8.2. Также pip установит самую свежую версию библиотеки pandas, при этом версия будет в диапазоне от 2.1.4 до 2.5.1
+
+При разработке крупного проекта в команде, в requirements.txt могут попадать библиотеки, используемые только при разработке, например, `pytest` - фреймворк для написания юниттестов. Однако серверу, на котором ваше приложение будет запускаться, не обязательно знать об этой зависимости, поскольку на удаленном сервере разработка вестись не будет, и тратить его память на установку pytest не стоит. С этой целью создается два списка зависимостей: requirements.txt - зависимости конечного продукта, requirements_dev.txt - список зависимостей, который используется командой разработки. Содержимое requirements_dev копирует содержание requirements и добавляет зависимости фреймворков разработки.
+
+Например для такого файла `requirements.txt`:
+
+*requirements.txt*:
+```txt
+matplotlib
+pandas
+numpy
+```
+
+`requirements_dev.txt` выглядел бы так:
+
+*requirements_dev.txt*:
+```txt
+matplotlib
+pandas
+numpy
+pytest
+```
+
+Чтобы не дублировать содержимое файла `requirements.txt`, мы можем поместить в файл `requirements_dev.txt` следующее:
+
+*requirements_dev.txt*:
+```txt
+-r requirements.txt
+pytest
+```
+
+Вести файл requirements.txt можно вручную, самостоятельно добавляя в него устанавливаемые библиотеки по мере добавления все новых и новых зависимостей в ваш проект. Однако существует более удобный способ:
+
+```console
+pip freeze > requirements.txt
+```
+
+В примере выше мы перенаправили вывод команды `pip freeze`, которая выводит все библиотеки, установленные в активное виртуальное окружение в формате `package==version`, в файл requirements.txt.
+
+### pip uninstall
+
+Для удаления библиотеки достаточно выполнить команду:
+```console
+pip uninstall
+```
+
+Однако будьте внимательны, ведь данная библиотека не всегда может быть безопасно удалена. Иногда установленные библиотеки являются внешними зависимостями для прочих библиотек, в таком случае их удаление может привести к серьезным проблемам. Подумайте, как мы можем узнать, возможно ли безопасное удаление данной библиотеки или нет?
+
+## venv
+
+### Что такое виртуальное окружение и зачем оно нам нужно?
+
+Виртуальное окружение - всего лишь изолированная среда, куда будут устанавиливаться библиотеки. Сам по себе Python не очень хорош в управлении зависимостями, поскольку все скачиваемые библиотеки будут установлены в одну директорию `site-packages/`. Из этого следует большое количество неудобств, которые и призвано решить виртуальное окружение. Давайте рассмотрим самые популярные проблемы, которые можно решить с помощью виртуального окружения:
+
+- **Избежать загрязнения системы**. На некоторых Unix-подобных операционных системах python установлен сразу с некоторыми сторонними пакетами. Как было сказано ранее, все новые библиотеки будут храниться в одной директории, и перемешиваться с этими установленными пакетами. Более того, устанавливамые библиотеки могут затереть предустановленные библиотеки, более релевантные данной системе;
+
+- **Избежать конфликта зависимостей**. Предположим, вы работаете одновременно над двумя проектами. Оба этих проекта требуют numpy для реализации научных вычислений. Однако первый проект требует numpy самой свежей версии, а второй - numpy версии 1.20.1. Из-за того, что все библиотеки хранятся в одном месте, вы не сможете работать одновременно с двумя версиями одной библиотеки, поскольку одна версия всегда будет перезаписывать другую. Тут-то на помощь и приходят виртуальные окружения. Вы можете создать отдельное виртуальное окружение для каждого проекта, установить в них требуемые версии библиотек, и работать одновременно с двумя проектами, не переживая о возможных конфликтах;
+
+- **Менеджмент зависимостей**. Предположим, вы одновременно работаете над двумя проектами: веб-парсер и компьютерная игра в жанре платформер. Для первого проекта вы используете библиотеки `beautiful-soup` и `aiohttp`, для второго - `PyGame`. При этом все библиотеки устанавливаются в глобальное виртуальное окружение. Также предположим, что ваша программа-парсер оказалась настолько полезной, что другие разработчки хотели бы интегрировать её в свои проекты, но для этого им необходимо получить список зависимостей. Вы умеете это делать, и выполняете команду `pip freeze`, однако на выходе, помимо релевантных проекту зависимостей, вы имеете еще и ряд других библиотек, ненужных для работы с вашим парсером, в частности `PyGame`. Чтобы избежать такой ситуации и разделить зависимости, также может быть использовано виртуальное окружение.
+
+### Как с ним работать?
+
+#### Создание
+Для создания виртуального окружения используется модуль стандартной библиотеки `venv`. Чтобы создать виртуальное окружение достаточно выполнить команду:
+
+```console
+python -m venv
+```
+
+`venv-name` - это имя вертуального окружения. Вы можете выбрать произвольное имя, однако чаше всего используются следующие имена: `venv`, `.venv`.
+
+После выполнения этой команды, в текущей директории будет создана папка с именем `venv-name`.
+
+#### Активация/деактивация
+
+Для того, чтобы все библиотеки были установлены в созданное виртуальное окружения, вы должны его активировать. Для этого достаточно выполнить скрипт `activate`, который хранится в папке виртуального окружения.
+
+*Windows*:
+```console
+.\venv-name\Scripts\activate
+```
+
+*Linux/MacOS*:
+```console
+source venv-name/bin/activate
+```
+
+После выполнения данного скрипта происходит две вещи:
+
+- В начало содержимого переменной PATH дописывается путь к интерпретатору, который хранится в папке `venv-name`;
+- В начале командной строке высвечивается сообщение `(venv-name)`, которое сигнализирует пользователю об активном виртуальном окружении.
+
+При завршении работы с текущем виртуальным окружением, необходимо выполнить команду:
+
+```console
+deactivate
+```
+
+Эта команда отменит все изменения, которые были внесены в систему при активации виртуального окружения.
+
+## Источники:
+- [Real Python. Using Python's pip to Manage Your Projects' Dependencies](https://realpython.com/what-is-pip/)
+- [Real Python. Python Virtual Environments: A Primer](https://realpython.com/python-virtual-environments-a-primer/);
diff --git a/lessons/sem02/lesson02/images/arrays.png b/lessons/sem02/lesson02/images/arrays.png
new file mode 100644
index 00000000..43658aff
Binary files /dev/null and b/lessons/sem02/lesson02/images/arrays.png differ
diff --git a/lessons/sem02/lesson02/images/clipping.png b/lessons/sem02/lesson02/images/clipping.png
new file mode 100644
index 00000000..cbb86031
Binary files /dev/null and b/lessons/sem02/lesson02/images/clipping.png differ
diff --git a/lessons/sem02/lesson02/images/image_struct.png b/lessons/sem02/lesson02/images/image_struct.png
new file mode 100644
index 00000000..25ef6bd8
Binary files /dev/null and b/lessons/sem02/lesson02/images/image_struct.png differ
diff --git a/lessons/sem02/lesson02/numpy_intro.ipynb b/lessons/sem02/lesson02/numpy_intro.ipynb
new file mode 100644
index 00000000..5c636aaf
--- /dev/null
+++ b/lessons/sem02/lesson02/numpy_intro.ipynb
@@ -0,0 +1,1293 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Введение в NumPy\n",
+ "\n",
+ "## Мотивация к изучению\n",
+ "\n",
+ "Во многих прикладных задачах вам предстоит иметь дело с данными, хранящимися в массивах различной размерности. Так, например, при работе с радиолокационными или акустическими сигналами вы, скорее всего, будете иметь дело с одномерными массивами. Элементы таких массивов - числа с плавающей точкой, значения которых соответствуют значению амплитуды сигнала в определенный момент времени. При анализе одноканальных (черно-белых) изображений вам предстоит обрабатывать двумерные массивы, в ячейках которых закодированы яркости пикселей изображения. Наконец, при работе с цветными изображениями вы столкнетесь с многомерными массивами - тензорами. Также многомерные массивы лежат в основе большого количества алгоритмов и технологий, к числу которых относятся, например, сверточные нейронные сети.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Из всего выше сказанного, очевидно, что необходим инструмент для работы с массивами большой размерности, содержащими большое количество элементов. Из заголовка семинара и из хода повествования вам должно быть очевидно, что к числу таких инструментов и относится `NumPy`. Однако, прежде, чем перейти к знакомству с ним, давайте подумаем, почему мы не можем использовать \"чистый\" Python для работы с большими и многомерными массивами. Решим пару задач.\n",
+ "\n",
+ "## Задача 1. Перегруз\n",
+ "\n",
+ "### Познавательная минутка\n",
+ "\n",
+ "Думаю, все вы слышали звук перегруженной гитары (если это не так, могу вам только посочувствовать и посоветовать послушать, например, *Black Sabbath - \"Iron Man\"*). Давайте разберемся откуда берется этот звук. Итак, в нашем распоряжении есть электрогитара и гитарный усилитель. С помощью электрогитары мы получаем некоторый электромагнитный сигнал, который поступает на вход усилителя по кабелю. Внутри гитарного усилителя происходит, как это не странно, усиление входного сигнала. Усиленный сигнал поступает на динамик усилителя, и мы слышим музыку. Однако амплитудный диапазон динамика не резиновый. Динамик не может воспроизводить любой усиленный сигнал в исходном виде без искажений. Когда значение амплитуды усиленного сигнала превышает максимально возможное значение амплитуду, которую способен воспроизвести динамик, происходит так называемый клиппинг, т.е. обрезание пиков сигнала. В результате мы слышим усиленный, но искаженный сигнал и получаем тот самый эффект перегруза.\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Задание\n",
+ "\n",
+ "Для того, чтобы понять, почему \"чистый\" Python не подойдет для работы с большими массивами данных, реализуем описанный выше алгоритм клиппинга. Чтобы реализовать алгоритм, нам потребуется:\n",
+ "- Создать сам сигнал;\n",
+ "- Усилить сигнал;\n",
+ "- Обрезать пики сигнал;\n",
+ "\n",
+ "Для каждого описанного выше шага реализуем отдельную функцию.\n",
+ "\n",
+ "**Функция создания сигнала**\n",
+ "\n",
+ "В качестве базового сигнала используется функция $sin(x)$.\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `sampling_period` - положительное число с плавающей точкой, соответствующее периоду дискретизации (шаг по времени);\n",
+ "- `sample_amount` - натуральное число (считаем, что натуральные числа начинаются с $1$), количество дискретов в сигнале;\n",
+ "- `modulation` - `Callable`-объект или `None`, функция модуляции сигнала. Если `None`, то модуляция не осуществляется. Функция модуляции должна иметь следующую сигнатуру:\n",
+ " ```python\n",
+ " def modulation(signal: float) -> float:\n",
+ " ...\n",
+ " ```\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Объект типа `list[float]` - сгенерированный и промодулированный сигнал. Фактически, результат - это список, состоящий из `sample_amount` элементов, `i`-ый элемент которого - это значение функции $sin(x) * modulation(x)$ в точке `sampling_period * i`.\n",
+ "\n",
+ "**Функция усиления**\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `signal` - список чисел с плавающей точкой, входной сигнал;\n",
+ "- `gain` - положительное число с плавающей точкой, большее нуля, коэффициент усиления;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Список чисел с плавающей точкой, `i`-ый элемент которого соответствует `i`-ому элементу списка `signal`, увеличенного в `gain` раз.\n",
+ "\n",
+ "**Функция клиппинга**\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `signal` - список чисел с плавающей точкой, входной сигнал;\n",
+ "- `threshold` - положительное число с плавающей точкой, большее нуля; уровень, по которому будет происходить обрезание сигнала;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Список чисел с плавающей точкой. Значение `i`-ого элемента результирующего списка описывается следующим выражение:\n",
+ "\n",
+ "$$res_i = \\begin{cases} signal_i, |signal_i| \\le threshold \\\\ threshold * sign(signal_i), |signal_i| \\gt threshold \\end{cases}$$\n",
+ "\n",
+ "**Необходимые импорты:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "import pathlib\n",
+ "import sys\n",
+ "\n",
+ "from typing import Callable, Optional\n",
+ "\n",
+ "from utils import read_floats_from_bytes\n",
+ "from utils import visualize_1d_array"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Константы:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample_amount = int(1e7) # количество отсчетов в сигнале \n",
+ "sampling_period = 6 * math.pi *1e-7 # период дискретизации\n",
+ "gane = 10 # коэффициент усиления\n",
+ "threshold = 4 # порог\n",
+ "eps = 1e-6 # точность"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Функция модуляции:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def modulate(signal: float) -> float:\n",
+ " return math.exp(-0.1 * signal)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_signal(\n",
+ " sampling_period: float,\n",
+ " sample_amount: int,\n",
+ " modulation: Optional[Callable[[float], float]] = None,\n",
+ ") -> list[float]:\n",
+ " # ваш код\n",
+ " return []\n",
+ "\n",
+ "\n",
+ "def amplify_signal(\n",
+ " signal: list[float],\n",
+ " gane: float,\n",
+ ") -> list[float]:\n",
+ " # ваш код\n",
+ " return []\n",
+ "\n",
+ "\n",
+ "def clip_signal(\n",
+ " signal: list[float],\n",
+ " threshold: float,\n",
+ ") -> list[float]:\n",
+ " # ваш код\n",
+ " return []"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Профилирование решения:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "signal = get_signal(sampling_period, sample_amount, modulate)\n",
+ "signal_amplified = amplify_signal(signal, gane)\n",
+ "signal_clipped = clip_signal(signal_amplified, threshold)\n",
+ "\n",
+ "print(f\"signal size: {sys.getsizeof(signal)} bytes\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Визуализация результата:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "assert signal is not signal_amplified\n",
+ "assert signal_amplified is not signal_clipped"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "visualize_1d_array(ordinate=signal_clipped)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Сравнение результатов с эталоном:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "path_to_reference = pathlib.Path(\"./test_data/signal_clipped.log\")\n",
+ "assert path_to_reference.exists(), \"no reference data for testing\"\n",
+ "\n",
+ "signal_referense = read_floats_from_bytes(\n",
+ " sample_amount, path_to_reference\n",
+ ")\n",
+ "\n",
+ "assert all(\n",
+ " abs(amp - amp_ref) < eps\n",
+ " for amp, amp_ref in zip(signal_clipped, signal_referense)\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Удалим списки, чтобы они не занимали место в памяти:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del signal\n",
+ "del signal_amplified\n",
+ "del signal_clipped\n",
+ "del signal_referense"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как можно видеть, уже на 10 миллионах чисел с плавающей точкой у нас начинаются проблемы. Клиппинг происходит достаточно медленно. Может, дела обстоят иначе для многомерных массивов?\n",
+ "\n",
+ "## Задача 2. Матрицы\n",
+ "\n",
+ "### Часть 1. Перемножение\n",
+ "\n",
+ "Задача проста: необходимо реализовать функцию, которая занималась бы перемножением двух матриц по всем правилам линейной алгебры. Если матрицы не могу быть перемножены, необходимо возбудить исключение `ShapeMismatchError`.\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `lhs` - двумерный массив, расположенный слева от оператора перемножения матриц; элементы массива - числа с плавающей точкой;\n",
+ "- `rhs` - двумерный массив, расположенный справа от оператора перемножения матриц; элементы массива - числа с плавающей точкой;\n",
+ "\n",
+ "Гарантируются, что матрицы содержат хотя бы одну строку и хотя бы один столбец.\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Двумерный массив - результат произведения матрицы `lhs` на матрицу `rhs` в соответствии со всеми правилами линейной алгебры.\n",
+ "\n",
+ "*Сторонние эффекты*:\n",
+ "- Если матрицы не могут быть перемножены между собой, необходимо возбудить исключение `ShapeMismatchError`. \n",
+ "\n",
+ "\n",
+ "**Необходимые импорты:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from random import randint"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Константы:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "columns_amount = 1000 # число колонок в матрице для тестирования\n",
+ "rows_amount = 500 # число строк в матрице для тестирования\n",
+ "\n",
+ "bottom = -10 # нижняя граница значений чисел в матрице\n",
+ "top = 10 # верхняя граница значений чисел в матрице"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class ShapeMismatchError(Exception):\n",
+ " \"\"\"Возбуждается, если матрицы не могут быть перемножены.\"\"\"\n",
+ "\n",
+ "\n",
+ "def multiply_matrices(\n",
+ " lhs: list[list[float]],\n",
+ " rhs: list[list[float]],\n",
+ ") -> list[list[float]]:\n",
+ " # ваш код\n",
+ " return [[0]]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Тестирование решения:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lhs = [\n",
+ " [7, -1, -4],\n",
+ " [-1, 5, -1],\n",
+ "]\n",
+ "rhs = [\n",
+ " [-2, -5],\n",
+ " [-5, -6],\n",
+ " [-5, 3],\n",
+ "]\n",
+ "reference = [\n",
+ " [11, -41],\n",
+ " [-18, -28]\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "result = multiply_matrices(lhs, rhs)\n",
+ "\n",
+ "assert all(\n",
+ " all(\n",
+ " num_res == num_ref \n",
+ " for num_res, num_ref in zip(row_res, row_ref)\n",
+ " )\n",
+ " for row_res, row_ref in zip(result, reference)\n",
+ ")\n",
+ "\n",
+ "was_raised = False\n",
+ "\n",
+ "try:\n",
+ " result = multiply_matrices(lhs, rhs[:2])\n",
+ "\n",
+ "except ShapeMismatchError:\n",
+ " was_raised = True\n",
+ "\n",
+ "assert was_raised"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Прифилирование решения:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "lhs = [\n",
+ " [randint(bottom, top) for _ in range(columns_amount)]\n",
+ " for _ in range(rows_amount)\n",
+ "]\n",
+ "\n",
+ "rhs = [\n",
+ " [randint(bottom, top) for _ in range(rows_amount)]\n",
+ " for _ in range(columns_amount)\n",
+ "]\n",
+ "\n",
+ "result = multiply_matrices(lhs, rhs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del lhs\n",
+ "del rhs\n",
+ "del result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Часть 2. Транспонирование\n",
+ "\n",
+ "Транспонируйте матрицу.\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `matrix` - двумерный массив, состоящий из чисел с плавающей точкой - матрица, которую необходимо транспонировать;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Двумерный массив, состоящий из чисел с плавающей точкой - транспонированная матрица."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Решение:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def transpose(matrix: list[list[float]]) -> list[list[float]]:\n",
+ " # ваш код\n",
+ " return matrix"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Тестирование:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "matrix = [\n",
+ " [-2, -5],\n",
+ " [-5, -6],\n",
+ " [-5, 3],\n",
+ "]\n",
+ "reference = [\n",
+ " [-2, -5, -5],\n",
+ " [-5, -6, 3],\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "transposed = transpose(matrix)\n",
+ "\n",
+ "assert transposed is not matrix\n",
+ "assert all(\n",
+ " all(\n",
+ " num_res == num_ref \n",
+ " for num_res, num_ref in zip(row_res, row_ref)\n",
+ " )\n",
+ " for row_res, row_ref in zip(transposed, reference)\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Профилирование решения:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "size = int(2e3)\n",
+ "\n",
+ "matrix = [\n",
+ " [randint(bottom, top) for _ in range(size)]\n",
+ " for _ in range(size)\n",
+ "]\n",
+ "\n",
+ "transposed = transpose(matrix)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del transposed"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как видите с многомерными массивами \"чистый\" Python работает достаточно медленно. Тут-то на сцену и выходит `NumPy`.\n",
+ "\n",
+ "\n",
+ "## Что такое NumPy?\n",
+ "\n",
+ "Прежде чем перейти к самому NumPy, давайте разберемся, почему работа с большими массивами данных средствами Python настолько неэффективна. Ключ к пониманию кроется в способе хранения данных в Python. Напомню, что любой объект в CPython - это не просто ячейка памяти со значением, как это было в C или C++. Объект в CPython - это указатель на структуру C. В этой структуре, помимо самого значения объекта хранятся метаданные объекта, которые используются интерпретатором Python. К числу таких данных относится тип объекта, счетчик ссылок, размер объекта. Массивы же - это не просто указатели на непрерывные блоки памяти с необходимыми данными, которые можно было бы быстро обрабатывать за счет кеширования. Массивы в CPython - это непрерывные области памяти, в которых хранятся адреса объектов CPython в памяти. Таким образом при обработке массива, мы читаем данные из памяти два раза: сначала адрес объекта, а потом сам объект. При этом указатели могут указывать на произвольные области памяти, которые не получится эффективно кешировать. Такой способ хранения данных с одной стороны позволяет иметь коллекции объектов различных типов данных, но с другой стороны сильно снижает эффективность обработки этих данных. \n",
+ "\n",
+ "NumPy подходит иначе к хранению данных. `NumPy` (сокращение от *Numerical Python*) - библиотека для эффективной работы с плотными массивами однородных данных. Особенно стоит выделить слово \"однородными\", ведь именно оно позволяет понять причину эффективности NumPy. Массивы NumPy являются оберткой вокруг C-массивов, т.е. массив NumPy инкапсулирует указатель на непрерывный блок в памяти, в котором хранятся данные одного типа. Это сильно ускоряет работу с этими данными и даже позволяет сэкономить память.\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Быстрая демонстрация возможностей\n",
+ "\n",
+ "Чтобы убедиться, что эффективность NumPy - это не пустое преувеличение, реализуем решение всех поставленных выше задач с помощью NumPy."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "from utils import visualize_1d_array"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Перегруз"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample_amount = int(1e7) # количество отсчетов в сигнале \n",
+ "gane = 10 # коэффициент усиления\n",
+ "threshold = 4 # порог\n",
+ "eps = 1e-6 # точность"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "time = np.linspace(0, 6 * np.pi, sample_amount)\n",
+ "\n",
+ "signal = np.sin(time) * np.exp(-0.1 * time)\n",
+ "signal_amplified = gane * signal\n",
+ "\n",
+ "signal_clipped = signal_amplified.copy()\n",
+ "mask = np.abs(signal_clipped) > threshold\n",
+ "signal_clipped[mask] = np.sign(signal_clipped[mask]) * threshold"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "visualize_1d_array(abscissa=time, ordinate=signal_clipped)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del signal\n",
+ "del signal_amplified\n",
+ "del signal_clipped"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Перемножение матриц"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "columns_amount = 1000 # число колонок в матрице\n",
+ "rows_amount = 500 # число строк в матрице\n",
+ "\n",
+ "bottom = -10 # нижняя граница значений числе в матрице\n",
+ "top = 10 # верхняя граница значений числе в матрице"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "lhs = np.random.randint(\n",
+ " low=bottom,\n",
+ " high=top,\n",
+ " size=(rows_amount, columns_amount),\n",
+ ")\n",
+ "rhs = np.random.randint(\n",
+ " low=bottom,\n",
+ " high=top,\n",
+ " size=(columns_amount, rows_amount),\n",
+ ")\n",
+ "\n",
+ "result = lhs @ rhs\n",
+ "result"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del lhs\n",
+ "del rhs\n",
+ "del result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Транспонирование"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# %%timeit -r 1 -n 1\n",
+ "\n",
+ "matrix = np.random.randint(\n",
+ " low=bottom,\n",
+ " high=top,\n",
+ " size=(rows_amount, columns_amount),\n",
+ ")\n",
+ "\n",
+ "transposed = matrix.T\n",
+ "del transposed"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как вы видите NumPy позволяет решать задачи с матричными вычислениями на порядки быстрее.\n",
+ "\n",
+ "## Основы NumPy\n",
+ "\n",
+ "### Создание массивов\n",
+ "\n",
+ "Как вы поняли из всего сказанного выше, центральным элементов библиотеки является многомерный массив. Именно поэтому мы начнем знакомство с создания массивов. Обычный и самый распространненный способ создания массива - функция `array()`, которая принимает на вход массиво-подобные объекты и возвращает массив NumPy.\n",
+ "\n",
+ "#### Создание массивов из объектов Python"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array_python = list(range(5))\n",
+ "\n",
+ "array_ints = np.array(array_python)\n",
+ "print(f\"{array_ints = }\")\n",
+ "\n",
+ "array_python[3] = 3.14\n",
+ "\n",
+ "array_floats = np.array(array_python)\n",
+ "print(f\"{array_floats = }\")\n",
+ "\n",
+ "array_python[3] = 3\n",
+ "\n",
+ "array_doubles = np.array(array_python, dtype=np.float64)\n",
+ "print(f\"{array_doubles = }\")\n",
+ "\n",
+ "array_of_objects = [\n",
+ " 1, 2.27, [1, 2, 3], {\"a\": 1, \"b\": 2}\n",
+ "]\n",
+ "array_of_objects = np.array(array_of_objects, dtype=object)\n",
+ "print(f\"{array_of_objects = }\")\n",
+ "\n",
+ "array_2d = np.array(\n",
+ " [[i + j for j in range(5)] for i in range(5)]\n",
+ ")\n",
+ "print(array_2d)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Массив NumPy содержит данные одного типа. При попытке создания массива из разнородных данных NumPy, если это возможно, будет производить повышающее приведение типов до тех пор, пока все данные не будут унифицированы. \n",
+ "\n",
+ "Так во втором примере, из-за наличия в данных числа `3.14`, итоговый тип данных массива - `float`. В четвертом примере NumPy не сможет самостоятельно привести данные к одному типу, поэтому нам пришлось указать его явно. Тип `object` наиболее общий и означает, что массив состоит из объектов языка Python (в нашем примере `int`, `float`, `list` и `dict`).\n",
+ "\n",
+ "При необходимости можно явно указать желаемый тип данных, путем присвоение аргументу `dtype` допустимого значения. \n",
+ "\n",
+ "Также обратите внимание на последний пример. В NumPy возможно создание многомерных массивов из списков со вложенными списками. Однако для этого требуется, чтобы вложенные списки имели одинаковый размер. Так в нашем примере вложенные списки интерпретируются, как строки двухмерной матрицы.\n",
+ "\n",
+ "#### Создание массивов \"с нуля\"\n",
+ "\n",
+ "Мы не всегда имеем под рукой объект, который бы хотелось представить в виде многомерного массива NumPy. Иногда целесообразно, или даже необходимо, создать новый объект \"с нуля\". Для этих целей в NumPy есть ряд функций.\n",
+ "\n",
+ "**zeros/ones/full:**\n",
+ "\n",
+ "Это набор функций, которые позволяют создавать массивы, заполненные одинаковыми элементами. Так функция `zeros` позволяет создать массив, заполненный нулями, `ones` - массив, заполненный единицами, а `full` - массив, заполненный элементами с указанным значением. Поэтому сигнатуры функций очень похожи между собой."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "zeros_1d = np.zeros(shape=10, dtype=np.uint8)\n",
+ "ones_1d = np.ones(shape=5, dtype=np.uint32)\n",
+ "\n",
+ "zeros_3d = np.zeros(shape=(3, 3, 3), dtype=np.float32)\n",
+ "full_2d = np.full(shape=(5, 5), fill_value=255, dtype=np.uint8)\n",
+ "\n",
+ "print(f\"Zeros 1D:\\n{zeros_1d}\", end=\"\\n\\n\")\n",
+ "print(f\"Zeros 3D:\\n{zeros_3d}\", end=\"\\n\\n\")\n",
+ "print(f\"Ones 1D:\\n{ones_1d}\", end=\"\\n\\n\")\n",
+ "print(f\"Full 2D:\\n{full_2d}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эти функции могут быть полезными при создании некоторых заготовок или аккумуляторов для вычислений."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**arange**:\n",
+ "\n",
+ "Полезно уметь создавать не только массивы, заполненные одинаковыми значениями, но и массивы, чьи элементы имеют разные значения. Одна из функций, которая позволяет создать новый массив с элементами, значения которых различаются - функция `np.arange`. Фактически, с помощью `np.arange` можно сгенерировать массив, элементы которого - числа, описывающие некоторую арифметическую прогрессию. Аргументы функции `np.arange` во многом повторяют аргументы встроенного объекта `range`: `start` - значение первого элемента; `stop` - значение последнего элемента, не включительно; `step` - шаг арифметической прогрессии. Как и в случае `range` аргументы `start` и `stop` можно опустить, в этом случае в качестве их значений будут использованы числа 0 и 1, соответственно.\n",
+ "\n",
+ "По умолчанию `np.arange` создает массив целых чисел. Однако при необходимости вы можете явно указать тип данных элементов созданного массива с помощью аргумента `dtype`. Более того, в отличие от `range`, `np.arange` умеет работать с числами с плавающей точкой."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "arange_int = np.arange(10)\n",
+ "print(f\"integer array:\\n{arange_int}\")\n",
+ "\n",
+ "arange_float = np.arange(0, 5, 0.5)\n",
+ "print(f\"float array:\\n{arange_float}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Далее в курсе мы с вами познакомимся с индексаций массивов с помощью массивов индексов. `np.arange` особенно полезна для этих целей."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**linspace:**\n",
+ "\n",
+ "В задаче на вычисление перегруза для генерации сигнала нам потребовалось вычислять значение времени, зная шаг дискретизации. Однако нам не всегда может быть известен шаг дискретизации, но может быть известен диапазон значений и количество отсчетов, которые необходимо взять из данного диапазона. Поскольку такая задача является типовой и встречается на практике очень часто, в NumPy реализована специальная функция для этого:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "np.linspace(0, np.pi, 32)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Немного о random:**\n",
+ "\n",
+ "В ряде задач бывает полезно создать массивы, заполненые случайными числами из определенного распределения вероятностей. Так, например, в глубоком обучении для повышения качества модели с функцией активации `ReLU` в начале обучения рекомендуется инициализировать веса случайными числами из нормального закона распределения.\n",
+ "\n",
+ "Эту задачу можно решить, используя подмодуль NumPy, который носит характерное название `random`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "normal = np.random.normal(loc=0, scale=1, size=(3, 3))\n",
+ "uniform = np.random.uniform(low=-10, high=10, size=(3, 3))\n",
+ "randomint = np.random.randint(0, 10, size=(3, 3))\n",
+ "\n",
+ "print(f\"Normal:\\n{normal}\", end=\"\\n\\n\")\n",
+ "print(f\"Uniform:\\n{uniform}\", end=\"\\n\\n\")\n",
+ "print(f\"Randint:\\n{randomint}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**eye/diag**\n",
+ "\n",
+ "Поскольку NumPy часто используется для реализации вычислений из линейной алгебры, о чем мы поговорим в будущем, в нем есть отдельные функции для создания единичных матриц и диагональных матриц."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "eye_square = np.eye(5)\n",
+ "eye_rectangle = np.eye(3, 5)\n",
+ "diag = np.diag([3, 5, 9])\n",
+ "\n",
+ "print(f\"Eye Square:\\n{eye_square}\", end=\"\\n\\n\")\n",
+ "print(f\"Eye Rectangle:\\n{eye_rectangle}\", end=\"\\n\\n\")\n",
+ "print(f\"Diagonal:\\n{diag}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**empty:**\n",
+ "\n",
+ "В ряде задач полезно создать \"пустой\" массив, т.е. массив, который не инициализирован никакими данным. Этот массив может быть использован в качестве буффера, в который будут записываться результаты различных матричных операций."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "np.empty(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Атрибуты массивов"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def print_attributes(array: np.ndarray) -> None:\n",
+ " print('\\n--------------------------------------------------')\n",
+ " print(f'\\n{array}\\n')\n",
+ " print(f'Array shape: {array.shape};')\n",
+ " print(f'Array dimension: {array.ndim};')\n",
+ " print(f\"Array element's amount: {array.size};\")\n",
+ " print(f\"Array element's size: {array.itemsize}\")\n",
+ " print(f'Array memory space: {array.nbytes};')\n",
+ " print('--------------------------------------------------')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "arrays = [np.random.uniform(0, 10, size=3),\n",
+ " np.random.uniform(0, 10, size=(3, 3)),\n",
+ " np.random.uniform(0, 10, size=(3, 3, 3))]\n",
+ "\n",
+ "for array in arrays:\n",
+ " print_attributes(array)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Доступ к элементам"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "arr_1d, arr_2d, arr_3d = arrays"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Индексация\n",
+ "\n",
+ "Индексация массивов в NumPy представляет собой улучшенную индексацию списков в Python. Сами по себе массивы NumPy - изменяемый тип данных, т.е. мы можем перезаписывать элементы с помощью индексации."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "line_len, fill_val = 80, \"-\"\n",
+ "\n",
+ "print(\"Python Way\".center(line_len, fill_val))\n",
+ "print(f\"1D:\\n{arr_1d[0]}\")\n",
+ "print(f\"2D:\\n{arr_2d[0][2]}\")\n",
+ "print(f\"3D:\\n{arr_3d[0][1][2]}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\"NumPy Way\".center(line_len, fill_val))\n",
+ "print(f\"2D:\\n{arr_2d[0, 2]}\")\n",
+ "print(f\"3D:\\n{arr_3d[0, 1, 2]}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Срезы\n",
+ "\n",
+ "В одномерном случае срезы аналогичны срезам списков в Python."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(20)\n",
+ "\n",
+ "print(numbers)\n",
+ "print(numbers[5: 10])\n",
+ "print(numbers[::2])\n",
+ "print(numbers[1::2])\n",
+ "print(numbers[::-1])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Однако, помимо знакомых нам одномерных срезов, в NumPy есть крайне удобные многомерные срезы:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(arr_2d, end=\"\\n\\n\")\n",
+ "\n",
+ "arr_2d[1:, 1:] = 100\n",
+ "print(arr_2d, end=\"\\n\\n\")\n",
+ "print(arr_2d[::2, ::2], end=\"\\n\\n\")\n",
+ "print(arr_2d[::-1, 0], end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Важное замечание, которое стоит держать в голове: в отличие от списков в Python, срезы массивов в NumPy возвращают не новый объект, а указатель на область в памяти с элементами существующего объекта. Т.е. связав результат среза с новой переменной, у вас есть возможность безвозвратно изменить исходный объект, совершая манипуляции через эту переменную с исходными данными. Проиллюстрируем эти утверждения следующим примером:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(10)\n",
+ "\n",
+ "print(numbers, end=\"\\n\\n\")\n",
+ "\n",
+ "my_slice = numbers[5: 10]\n",
+ "my_slice[:] = 100\n",
+ "\n",
+ "print(my_slice, end=\"\\n\\n\")\n",
+ "print(numbers, end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Чтобы избежать подобного нежелательного поведения, используйте метод `copy()` при связывании срезов с различными переменными:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(10)\n",
+ "\n",
+ "print(numbers, end=\"\\n\\n\")\n",
+ "\n",
+ "my_slice = numbers[5: 10].copy()\n",
+ "my_slice[:] = 100\n",
+ "\n",
+ "print(my_slice, end=\"\\n\\n\")\n",
+ "print(numbers, end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Индексация массивами\n",
+ "\n",
+ "Помимо срезов и простых индексов, вы можете за раз выбрать сразу несколько элементов, используя массивы в качестве индексов."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data_1d = np.random.randint(0, 10, size=15)\n",
+ "data_2d = data_1d.reshape(5, 3)\n",
+ "\n",
+ "print(f\"Data 1D:\\n{data_1d}\", end=\"\\n\\n\")\n",
+ "print(f\"Data 2D:\\n{data_2d}\", end=\"\\n\\n\")\n",
+ "\n",
+ "indices_1d = [2, 7, 13, 8]\n",
+ "indices_2d = [0, 1, 2], [0, 1, 2]\n",
+ "\n",
+ "print(f\"Index 1D:\\n{data_1d[indices_1d]}\", end=\"\\n\\n\")\n",
+ "print(f\"Index 2D:\\n{data_2d[indices_2d]}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Ну и разумеется через такую индексацию вы тоже имеете возможность перезаписывать элементы исходного массива.\n",
+ "\n",
+ "Более того, помимо простых индексов, вы можете индексировать массив булевыми масками, т.е. массивами, той же формы, что и исходный массив, но заполненными значениями `True` и `False`. Значение `True` будет означать использование элемента в данной позиции в результате применения маски, `False` - нет."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mask_1d = np.random.randint(0, 2, size=data_1d.shape, dtype=bool)\n",
+ "mask_2d = mask_1d.reshape(5, 3)\n",
+ "\n",
+ "print(f\"Mask 1D:\\n{mask_1d}\", end=\"\\n\\n\")\n",
+ "print(f\"Mask 2D:\\n{mask_2d}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(f\"Masked 1D:\\n{data_1d[mask_1d]}\", end=\"\\n\\n\")\n",
+ "print(f\"Masked 2D:\\n{data_2d[mask_2d]}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Изменение размеров массива\n",
+ "\n",
+ "Я ряде случаев бывает полезным изменить форму массива: буквально изменить размеры, или добавить новое измерение. В одном из следующих занятий мы поймем, зачем это нужно, а сейчас рассмотрим самые распространенные варианты для этих действий.\n",
+ "\n",
+ "**reshape:**\n",
+ "\n",
+ "Мы уже использовали этот метод в нескольких примерах выше. С его помощью можно получить новый массив заданной формы, элементами которого являются элементы исходного массива. Если заданный массив не может быть переведен в массив указанного размера, метод возбуждает исключение."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(15)\n",
+ "numbers_reshaped1 = numbers.reshape(5, 3)\n",
+ "numbers_reshaped2 = numbers.reshape(3, 5)\n",
+ "\n",
+ "print(f\"numbers:\\n{numbers}\", end=\"\\n\\n\")\n",
+ "print(f\"reshaped 1:\\n{numbers_reshaped1}\", end=\"\\n\\n\")\n",
+ "print(f\"reshaped 1:\\n{numbers_reshaped2}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers.reshape(6, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Также с помощью этого метода можно \"вытянуть\" исходный многомерный массив в одномерный."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(numbers_reshaped1.reshape(-1))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**flatten:**\n",
+ "\n",
+ "Результат метода аналогичен результату метода `reshape(-1)`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(numbers_reshaped1.flatten())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**transpose/T:**\n",
+ "\n",
+ "Функция `transpose` позволяет транспонировать входной массив по правилам линейной алгебры. Возвращает новый массив, который можно безопасно изменять.\n",
+ "\n",
+ "Атрибут массива `T`, когда это возможно, возвращает представление исходного массива, соответствующее транспонированию массива. Т.е. если связать возвращенное представление с новой переменной, то в вашей программе появится риск изменения исходных данных через эту переменную. Имейте это в виду и всегда думайте, что именно вы хотите получить: представление или копию."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(0, 10, size=(3, 4))\n",
+ "\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "print(f\"transpose:\\n{np.transpose(array)}\", end=\"\\n\\n\")\n",
+ "print(f\"T:\\n{array.T}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**newaxis:**\n",
+ "\n",
+ "Это специальная константа, которая позволяет удобно добавить новое измерения в ваш массив:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = array.flatten()\n",
+ "\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "print(f\"array horizontal:\\n{array[np.newaxis, :]}\", end=\"\\n\\n\")\n",
+ "print(f\"array vertical:\\n{array[:, np.newaxis]}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/lessons/sem02/lesson02/requirements.txt b/lessons/sem02/lesson02/requirements.txt
new file mode 100644
index 00000000..c16bf473
--- /dev/null
+++ b/lessons/sem02/lesson02/requirements.txt
@@ -0,0 +1,2 @@
+matplotlib==3.8.0
+numpy==1.26.1
diff --git a/lessons/sem02/lesson02/test_data/signal_clipped.log b/lessons/sem02/lesson02/test_data/signal_clipped.log
new file mode 100644
index 00000000..6b4627f4
Binary files /dev/null and b/lessons/sem02/lesson02/test_data/signal_clipped.log differ
diff --git a/lessons/sem02/lesson02/utils/__init__.py b/lessons/sem02/lesson02/utils/__init__.py
new file mode 100644
index 00000000..2fc37a2e
--- /dev/null
+++ b/lessons/sem02/lesson02/utils/__init__.py
@@ -0,0 +1,7 @@
+# fmt: off
+from .io_data import (
+ read_floats_from_bytes,
+ print_matrix,
+)
+from .visualize import visualize_1d_array
+# fmt: on
diff --git a/lessons/sem02/lesson02/utils/io_data.py b/lessons/sem02/lesson02/utils/io_data.py
new file mode 100644
index 00000000..9f3dae28
--- /dev/null
+++ b/lessons/sem02/lesson02/utils/io_data.py
@@ -0,0 +1,21 @@
+import struct
+
+from typing import Any
+
+
+def read_floats_from_bytes(
+ bytes_amount: int,
+ path_to_file: str,
+) -> list[float]:
+ with open(path_to_file, "rb") as file:
+ buffer = file.read()
+
+ floats = struct.unpack(f"{bytes_amount}f", buffer)
+ return floats
+
+
+def print_matrix(matrix: list[Any]) -> None:
+ for row in matrix:
+ print(row)
+
+ print("")
diff --git a/lessons/sem02/lesson02/utils/visualize.py b/lessons/sem02/lesson02/utils/visualize.py
new file mode 100644
index 00000000..cb935b1a
--- /dev/null
+++ b/lessons/sem02/lesson02/utils/visualize.py
@@ -0,0 +1,33 @@
+from typing import Sequence, Optional
+from numbers import Real
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def visualize_1d_array(
+ *,
+ ordinate: Sequence[Real],
+ abscissa: Optional[Sequence[Real]] = None,
+ save_path: str = ""
+) -> None:
+ if ordinate is None:
+ return
+
+ with plt.style.context("ggplot"):
+ figure, axis = plt.subplots(figsize=(16, 9))
+
+ if abscissa is None:
+ abscissa = np.arange(len(ordinate))
+
+ else:
+ abscissa = np.array(abscissa)
+
+ axis.plot(abscissa, ordinate, c="royalblue")
+
+ axis.set_xlim(abscissa.min(), abscissa.max())
+ axis.grid(True)
+ plt.show()
+
+ if save_path:
+ figure.savefig(save_path)
diff --git a/lessons/sem02/lesson03/images/broadcasting.png b/lessons/sem02/lesson03/images/broadcasting.png
new file mode 100644
index 00000000..71cef69f
Binary files /dev/null and b/lessons/sem02/lesson03/images/broadcasting.png differ
diff --git a/lessons/sem02/lesson03/numpy_operations.ipynb b/lessons/sem02/lesson03/numpy_operations.ipynb
new file mode 100644
index 00000000..c40b9ec5
--- /dev/null
+++ b/lessons/sem02/lesson03/numpy_operations.ipynb
@@ -0,0 +1,651 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Операции над массивами NumPy\n",
+ "\n",
+ "На прошлом занятии мы с вами начали знакомство с библиотекой NumPy и рассмотрели возможные способы создания массивов NumPy. Однако, очевидно, что работа с данными не ограничивается исключительно созданием массивов. На этом семинаре мы рассмотрим возможные формы взаимодействия с массивами NumPy и разберемся, как взаимодействовать с ними эффективно.\n",
+ "\n",
+ "**Импорт библиотек:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Векторизованные операции\n",
+ "\n",
+ "На прошлом семинаре мы рассмотрели производительность Python при работе с массивами данных, и пришли к выводу о его медлительности, связанной с подходом к хранению данных. На самом деле проблема кроется не только в подходе к хранению данных, но и в издержках динамической типизации. Т.е. если мы просто изменим подход к хранению данных без изменения подхода к работе с ними, мы не увидим никакого качественного прироста в быстродействии программы. Мы можем переходить на NumPy, CuPy, PyTorch, на что угодно, но без изменения способа работы с данными, выигрыша в производительности мы не увидим.\n",
+ "\n",
+ "Чтобы понять это, давайте рассмотрим простой пример: реализуем функцию, которая для значения каждого элемент входного массива вычисляет обратное ему значение и возвращает новый массив вычисленных значений.\n",
+ "\n",
+ "**Необходимые импорты:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Sequence\n",
+ "from numbers import Real"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Функция:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def invert_array(array: Sequence[Real]) -> list[Real]:\n",
+ " inverted = [\n",
+ " 1. / value if value != 0 else value\n",
+ " for value in array\n",
+ " ]\n",
+ "\n",
+ " return inverted"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Тесты:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array_size = int(1e7)\n",
+ "\n",
+ "array_numpy = np.random.uniform(-10, 10, size=array_size)\n",
+ "array_python = array_numpy.tolist()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%timeit invert_array(array_python)\n",
+ "%timeit invert_array(array_numpy)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del array_numpy\n",
+ "del array_python"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как мы видим, использование NumPy не просто не решило проблему, а даже замедлило скорость вычислений. Но почему? Как уже было сказано ранее, все дело в динамической типизации и, в частности, в том, как интерпретатор ее реализует. Дело в том, что из-за динамической природы Python интерпретатор имеет возможность определить типы данных конкретных объектов только в момент выполнения кода. Когда мы используем нативные конструкции, типа цикла `for`, для выполнения однотипных операций, в частности, над однородными данными, помимо описанных вами операций интерпретатору также приходится выполнять проверки типов для каждого элемента однородных данных. Эти проверки не бесплатны, поэтому производительность кода при использовании нативных средств Python для реализации вычислений значительно снижается.\n",
+ "\n",
+ "Однако правильное использование NumPy позволяет обойти эти ограничения и воспользоваться фактом однородности данных в массивах для увеличения быстродействия вычислений. NumPy обладает рядом операций, позволяющих обходить ограничения Python, используя исключительно код на С, выходящий за рамки объектов Python. Эти операции отличаются быстродействием и позволяют значительно ускорить ваши вычисления. Такие операции называются **векторизованными операциями**.\n",
+ "\n",
+ "### Арифметические операции\n",
+ "\n",
+ "Все арифметические операции, с которыми вы работали в \"чистом\" Python, имеют место и в NumPy, и смысл их остается тем же, что и в обычной алгебре."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(5)\n",
+ "\n",
+ "print(\n",
+ " f\"{numbers = }\",\n",
+ " f\"numbers + 2 = {numbers + 2};\",\n",
+ " f\"numbers - 2 = {numbers - 2};\",\n",
+ " f\"numbers * 2 = {numbers * 2};\",\n",
+ " f\"numbers / 2 = {numbers / 2};\",\n",
+ " f\"numbers % 2 = {numbers % 2};\",\n",
+ " f\"numbers // 2 = {numbers // 2};\",\n",
+ " f\"numbers ** 2 = {numbers ** 2};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.arange(1, 10)\n",
+ "\n",
+ "print(\n",
+ " f\"array:\\n{numbers}\",\n",
+ " f\"array-number operation:\\n{numbers * 8}\",\n",
+ " f\"array-array operation:\\n{numbers - numbers[::-1]}\",\n",
+ " f\"2D-array operation:\\n{numbers.reshape(3, 3) ** 2}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как вы видите, применение векторизованной операции к массиву NumPy аналогично поэлементному применению той же операции в цикле. Только в сравнении с векторизованными операциями, циклы будут работать гораздо медленее. Чтобы в этом убедиться, сравним результаты времени работы нашей функции `invert_array` с аналогичной векторизованной операцией."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.arange(1, 1 + int(1e6))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%timeit invert_array(array)\n",
+ "%timeit (1. / array)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "del array"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как можно видеть, разница в быстродействии измеряется несколькими порядками. И чем больше будет размер массива, тем больше будет разница в затрачиваемом на его обработку времени со стороны Python и со стороны NumPy.\n",
+ "\n",
+ "Стоит отметить, что все операторные формы векторизованных операций представляют собой адаптеры векторизованных операций в функциональном виде. Приведем ниже таблицу соответствий:\n",
+ "\n",
+ "| Операторная форма | Функциональная форма | Описание |\n",
+ "|--|--|--|\n",
+ "| + | np.add | Сложение |\n",
+ "| - | np.substract | Бинарный минус |\n",
+ "| - | np.negative | Унарные минус (эквивалент * (-1)) |\n",
+ "| * | np.multiply | Умножение |\n",
+ "| / | np.divide | \"Честное\" деление |\n",
+ "| % | np.mod | Вычисление остатка при делении |\n",
+ "| // | np.floor_divide | Вычисление целой части при делении |\n",
+ "| ** | np.power | Возведение в степень |\n",
+ "\n",
+ "На первый взгляд наличие векторизованных арифметический операций в функциональном виде кажется избыточным. Однако, не стоит спешить с выводами. Функциональная форма предоставляет вам ряд дополнительных возможностей, к число которых относится оптимизация вычислений по используемой памяти, а также превращение простой векторизованной операции в векторизованное агрегирование. Рассмотрим примеры."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array1 = np.arange(5)\n",
+ "array2 = np.random.randint(-10, 10, size=array1.shape)\n",
+ "\n",
+ "print(\n",
+ " f\"{array1 = }\", f\"{array2 = }\", sep=\"\\n\", end=\"\\n\\n\"\n",
+ ")\n",
+ "\n",
+ "np.add(array1, array2, out=array2)\n",
+ "\n",
+ "print(\n",
+ " f\"{array1 = }\", f\"{array2 = }\", sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Итак, в примере выше с помощью специального аргумента `out`, которым обладают все перечисленные в таблице функции, мы явно указали область памяти, в которую будут осуществлена запись результата операции. Это позволяет сэкономить память компьютера, особенно если вы намереваетесь обрабатывать действительно большие массивы данных. Экономия памяти происходит за счет того, что компьютер больше не выделяет дополнительную память для хранения промежуточных вычислений, а сразу записывает результат в указанный буфер. Ведь если бы пример выше был бы реализован более привычным образом:\n",
+ "\n",
+ "```python\n",
+ "array2 = array1 + array2\n",
+ "```\n",
+ "\n",
+ "То интерпретатор действовал бы следующим образом:\n",
+ "1. Выделил бы область памяти, соответствующую размеру результирующего массива;\n",
+ "2. Вычислил бы правую часть и записал бы результат в буфер;\n",
+ "3. Перекопировал вычисленные значения из буфера в `array2`;\n",
+ "\n",
+ "При использовании `out` мы пропускаем шаги с созданием буфера и копированием, вместо этого запись результата происходит сразу в указанную область памяти.\n",
+ "\n",
+ "Помимо этого, функциональные версии векторизованных бинарных арифметических операций имеют возможность редуцирования данных. Например: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = 2 ** (-np.arange(10, dtype=np.float32))\n",
+ "print(numbers, end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"sum reduce: {np.add.reduce(numbers)}\",\n",
+ " f\"sub reduce: {np.subtract.reduce(numbers)}\",\n",
+ " f\"multiply reduce: {np.multiply.reduce(numbers)}\",\n",
+ " f\"divide reduce: {np.divide.reduce(numbers)}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В этом случае мы просто применяем указанную операцию к элементам массива до тех пор, пока в массиве не останется одного элемента. Также вы можете вычислять операции над элементами массива кумулятивно - каждый элемент результирующего массива - результат применения операции ко всем предшествующим ему элементам, а также к нему включительно. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " f\"sum accumulate:\\n{np.add.accumulate(numbers)}\",\n",
+ " f\"sub accumulate:\\n{np.subtract.accumulate(numbers)}\",\n",
+ " f\"multiply accumulate:\\n{np.multiply.accumulate(numbers)}\",\n",
+ " f\"divide accumulate:\\n{np.divide.accumulate(numbers)}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Однако у кумулятивных методов есть более удобные аналоги, которые мы рассмотрим позже.\n",
+ "\n",
+ "### Логические операции и булевы маски\n",
+ "\n",
+ "Вы также можете использовать логические операторы, в контексте массивов NumPy они будут иметь тот же смысл, что и в булевой алгебре. Результатом применения логической операции к массиву будет новый массив булевых значений, элементы которого - значения выполнения соответствующих операций."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numbers = np.random.uniform(-5, 5, size=5)\n",
+ "\n",
+ "print(\n",
+ " f\"numbers: {numbers}\",\n",
+ " f\"numbers < 0 = {numbers < 0}\",\n",
+ " f\"numbers > 0 = {numbers > 0}\",\n",
+ " f\"numbers == 0 = {numbers == 0}\",\n",
+ " f\"numbers <= 0 = {numbers <= 0}\",\n",
+ " f\"numbers >= 0 = {numbers >= 0}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "На первый взгляд может показаться, что это совершенно бесполезные операции, однако это не так. Давайте рассмотрим пару примеров использования булевых массивов.\n",
+ "\n",
+ "Напомним, что булев тип данных - это дочерний класс типа данных \"целые числа\", а соответственно мы можем работать с булевыми значениями, как с целыми числами. Соответственно, с помощью булева массива мы можем, например, подсчитать количество элементов массива, которые удовлетворяют определенному условию:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(\n",
+ " f\"negative values amount: {np.add.reduce(numbers < 0)}\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Однако у булевых массивов есть и более важная функция. Из прошлого семинара мы помним, что массивы NumPy могут быть проиндексированы булевыми массивами. Соответственно, получаемые таким образом булевы массивы могут быть использованы в качестве так называемой булевой маски, чтобы отфильтровывать определенные элементы массива по заданному условию:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"negative values: {numbers[numbers < 0]}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Более того, мы можем использовать сложные условия, комбинируя булевы маски с помощью операторов побитового `И` и побитового `ИЛИ`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mask = (0 <= numbers) & (numbers < 3)\n",
+ "print(f\"condition values: {numbers[mask]}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Обращаем отдельное внимание на то, что комбинирование булевых масок осуществляется с помощью побитовых операций `&` и `|`, а не с помощью логических операций `and` и `or`. Также не забывайте использовать скобки для группировки условий.\n",
+ "\n",
+ "### Математические операции\n",
+ "\n",
+ "Помимо арифметических и логических функций, NumPy имеет достаточно богатую библиотеку арифметических функций: тригонометрических, обратных тригонометрических, гиперболических, обратных гиперболических, экспоненциальных, логарифмических и т.д. Вызовы этих функций очень похожи, поэтому следующая ячейка кода носит скорее демонстрационный характер. При необходимости использования конкретной функции, вы можете обратиться к документации:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "abscissa = np.linspace(0, np.pi, 5)\n",
+ "\n",
+ "print(\n",
+ " f\"{abscissa = }\",\n",
+ " f\"triganometric:\\n{np.sin(abscissa)}\",\n",
+ " f\"inverse trigonometric:\\n{np.arctan(abscissa)}\",\n",
+ " f\"hyperbolic:\\n{np.cosh(abscissa)}\",\n",
+ " f\"inverse hyperbolic:\\n{np.sinh(abscissa)}\",\n",
+ " f\"exponential:\\n{np.exp(abscissa)}\",\n",
+ " f\"logarithmic:\\n{np.log2(abscissa[abscissa > 0])}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В примерах выше описано использование операций с одномерными массивами, однако стоит понимать, что данные функции также могут быть использованы и с массивами произвольной размерности.\n",
+ "\n",
+ "## Транслирование (Broadcasting)\n",
+ "\n",
+ "В одном из примеров выше мы видели, что аргументами векторизованных арифметических операций могут выступать два массива NumPy одинаковой размерности. Однако одна из ключевых особенностей NumPy заключается в том, что массивы не обязательно должны иметь одинаковые размеры. Проиллюстрируем это утверждение примером:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lhs = np.arange(9).reshape(3, 3)\n",
+ "rhs = np.arange(3)\n",
+ "\n",
+ "print(\n",
+ " f\"lhs:\\n{lhs}\",\n",
+ " f\"rhs:\\n{rhs}\",\n",
+ " f\"sum:\\n{lhs + rhs}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Первое, что появляется в голове у неподготовленного человека, увидевшего нечто подобное - это вопрос: почему так? Почему этот кусок кода не возбудил ошибку, по типу `Несовместимые размеры массивов`? На самом деле размеры операндов совместимы, просто NumPy трактует совместимость размеров несколько шире банального равенства значений атрибутов `shape`. Проведение операций над массивами разных, но совместимых размеров называется транслированием (broadcasting). Однако это не значит, что мы можем использовать в качестве операндов одной операции массивы абсолютно любых форм, потому что механизм транслирования имеет свои ограничения и подчиняется следующим правилам:\n",
+ "\n",
+ "- Если размерности (`ndim`) двух массивов отличаются, форма (`shape`) массива с меньшей размерностью дополняется единицей с левой стороны:\n",
+ " ```concole\n",
+ " 1. lhs.ndim != rhs.ndim\n",
+ " 2. lhs.ndim > rhs.ndim\n",
+ " 3. rhs.shape == (3, ) -> rhs.shape == (1, 3)\n",
+ " [0 1 2] -> [[0 1 2]]\n",
+ " ```\n",
+ "- Если форма двух массивов не совпадает в каком-то измерении, массив с формой, равной 1 в этом измерении, растягивается вплоть до соответствия форме второго массива:\n",
+ " ```console\n",
+ " 4. lhs.shape[0] != rhs.shape[0]\n",
+ " 5. rhs.shape == (1, 3) -> rhs.shape == (3, 3)\n",
+ " [[0 1 2]] -> [[0 1 2]\n",
+ " [0 1 2]\n",
+ " [0 1 2]]\n",
+ " ```\n",
+ "- Если в каком-то измерении размеры массивов различаются и ни одно значение измерения не равно 1, генерируется ошибка. В нашем примере этот случай не реализуется.\n",
+ "\n",
+ "Наглядно суть транслирования можно понять с помощью следующей иллюстрации:\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Практика 1. Расстояния между точками прямой\n",
+ "\n",
+ "Дан некоторый набор точек $X$, расположенных на прямой: $X \\sub \\R$. Необходимо найти значения расстояний для каждой пары точек из данного множества $X$. Вычисленные расстояния необходимо представить в виде матрицы $D$ размером $N \\times N, N = |X|$, такой, что $D_{ij} = |x_i - x_j| \\forall x_i, x_j \\in X$.\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `points` - np.ndarray, множество точек прямой, для которых необходимо вычислить попарные расстояния.\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- двумерные массив типа np.ndarray - попарные расстояния между выходных точек. Значению элемента `result[i][j]` соответствует расстояние между точкой `points[i]` и точкой `points[j]`.\n",
+ "\n",
+ "**Решение**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_mutual_distances_vectorized(points: np.ndarray) -> np.ndarray:\n",
+ " # код преподавателя\n",
+ " return np.array([], dtype=points.dtype)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Тестирование**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "points = np.arange(5)\n",
+ "distances_expected = np.array(\n",
+ " [\n",
+ " [0, 1, 2, 3, 4],\n",
+ " [1, 0, 1, 2, 3],\n",
+ " [2, 1, 0, 1, 2],\n",
+ " [3, 2, 1, 0, 1],\n",
+ " [4, 3, 2, 1, 0],\n",
+ " ],\n",
+ " dtype=points.dtype,\n",
+ ")\n",
+ "\n",
+ "distances = get_mutual_distances_vectorized(points)\n",
+ "\n",
+ "assert np.allclose(distances, distances_expected)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Практика 2. Полярные координаты: путешествие туда и обратно\n",
+ "\n",
+ "Необходимо реализовать две функции: функцию перевода координат из двухмерной прямоугольной декартовой системы координат в полярные координаты и функцию перевода из полярных координат в двухмерную прямоугольную декартову систему координат.\n",
+ "\n",
+ "### Перевод из декартовых координат в полярные\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `abscissa` - np.ndarray, абсциссы точек;\n",
+ "- `ordinates` - np.ndarray, ординаты точек;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Кортеж (`tuple`) из двух элементов, каждый элемент - np.ndarray. Первый элемент `tuple` - массив расстояний, второй элемент `tuple` - массив углов в диапазоне от $[-\\pi, \\pi]$.\n",
+ "\n",
+ "*Сторонние эффекты*:\n",
+ "- Если количество элементов во входных массивах `abscissa` и `ordinates` отличаются, необходимо возбудить исключение `ShapeMismatchError`.\n",
+ "\n",
+ "*Замечания*:\n",
+ "- Гарантируется, что на вход подаются непустые одномерные массивы чисел с плавающей точкой.\n",
+ "- Предполагаем, что для перевода в полярные координаты используются следующие формулы:\n",
+ " $$x = r*cos(\\phi); y = r*sin(\\phi)$$\n",
+ "\n",
+ "### Перевод из полярных координат в декартовы\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `distances` - np.ndarray, массив расстояний;\n",
+ "- `angles` - np.ndarray, массив углов в диапазоне $[-\\pi, \\pi]$;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Кортеж (`tuple`) из двух элементов, каждый элемент - np.ndarray. Первый элемент `tuple` - массив абсцисс, второй элемент `tuple` - массив ординат.\n",
+ "\n",
+ "*Сторонние эффекты*:\n",
+ "- Если количество элементов во входных массивов `distances` и `angles` отличаются, необходимо возбудить исключение `ShapeMismatchError`.\n",
+ "\n",
+ "**Решение**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class ShapeMismatchError(Exception):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def convert_from_polar(\n",
+ " distances: np.ndarray,\n",
+ " angles: np.ndarray,\n",
+ ") -> tuple[np.ndarray, np.ndarray]:\n",
+ " # код преподавателя\n",
+ "\n",
+ " return distances, angles\n",
+ "\n",
+ "\n",
+ "def convert_to_polar(\n",
+ " abscissa: np.ndarray,\n",
+ " ordinates: np.ndarray,\n",
+ ") -> tuple[np.ndarray, np.ndarray]:\n",
+ " # код преподавателя\n",
+ "\n",
+ " return abscissa, ordinates"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Тестирование**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "abscissa = np.array([1, 0, -1, 0], dtype=np.float64)\n",
+ "ordinates = np.array([0, 1, 0, -1], dtype=abscissa.dtype)\n",
+ "\n",
+ "distances, angles = convert_to_polar(abscissa, ordinates)\n",
+ "abscissa_conv, ordinates_conv = convert_from_polar(distances, angles)\n",
+ "\n",
+ "assert np.allclose(abscissa, abscissa_conv)\n",
+ "assert np.allclose(ordinates, ordinates_conv)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Задачи для самостоятельного выполнения\n",
+ "\n",
+ "Условия задач для самостоятельного выполнения можно найти [тут](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/conditions/sem02/lesson03/tasks.md). Срок сдачи: до 23:59 6 марта."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/lessons/sem02/lesson04/numpy_aggregation.ipynb b/lessons/sem02/lesson04/numpy_aggregation.ipynb
new file mode 100644
index 00000000..06eb6a99
--- /dev/null
+++ b/lessons/sem02/lesson04/numpy_aggregation.ipynb
@@ -0,0 +1,535 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Агрегирование и сортировка.\n",
+ "\n",
+ "**Импорт библиотек:**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Агрегирование\n",
+ "\n",
+ "Согласно одному из возможных определений, агрегированием называется объединение элементов по определенному правилу. В нашем случае, в роли правил для объединения будут выступать различные математические операции, например, суммирование, произведение или вычисление максимума. Операции агрегирования являются весьма важными операциями, поскольку в большом количестве различных алгоритмов так или иначе приходится суммировать различные данные, вычислять максимумы и минимумы. Также большое количество агрегирующих операций используется в статистике и науке о данных при вычислении сводных показателей: среднего, медианы, среднеквадратичного отклонения и т.д.\n",
+ "\n",
+ "Из вышесказанного следует полезность агрегирующих операций, оттого неудивительно их наличие в NumPy.\n",
+ "\n",
+ "### Суммы и произведения\n",
+ "\n",
+ "Мы уже видели возможности нахождения суммы или произведения элементов массива с помощью агрегирующих методов соответствующих векторизованных операций. Однако, есть более удобный и гибкий способ вычисления, как простых сумм/произведений элементов, так и их кумулятивных вариантов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=9)\n",
+ "print(f\"{array = }\")\n",
+ "\n",
+ "print(\n",
+ " f\"sum: {np.sum(array)};\",\n",
+ " f\"prod: {np.prod(array)};\",\n",
+ " f\"cumulative sum: {np.cumsum(array)};\",\n",
+ " f\"cumulative prod:\\n{np.cumprod(array)}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Сводные показатели\n",
+ "\n",
+ "Также в NumPy есть возможность вычислять сводные показатели массива данных. К числу таких показателей относятся максимумы/минимумы, среднее значение, медиана, среднеквадратичное отклонение."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=9)\n",
+ "print(f\"{array = }\")\n",
+ "\n",
+ "print(\n",
+ " f\"min: {np.min(array)};\",\n",
+ " f\"max: {array.max()};\",\n",
+ " f\"mean: {np.mean(array)};\",\n",
+ " f\"median: {np.median(array)};\",\n",
+ " f\"std: {np.std(array)};\",\n",
+ " f\"variance: {np.var(array)};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Булево агрегирование\n",
+ "\n",
+ "Также мы можем агрегировать массивы, используя значения их элементов в булевом контексте с помощью специальных функций `all` и `any`, которые очень похожи на свои аналоги в \"чистом\" Python."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=9)\n",
+ "print(f\"{array = }\")\n",
+ "\n",
+ "print(\n",
+ " f\"all: {array.all()};\",\n",
+ " f\"any: {np.any(array)};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Позиционирование\n",
+ "\n",
+ "В ряде задач важно не столько найти значение экстремума, сколько найти его положение. Например, знание информации о положении экстремума в массиве может оказаться очень полезным при визуализации содержимого массива. Рассмотрим пример, пусть нам дан одномерный массив, описывающий значения некоторой функции на заданном отрезке. При визуализации этого массива мы бы хотели явно отметить точку максимума на рисунке. Для этой цели мы будем использовать специальную функцию `argmax()`, которая позволяет найти позицию максимального элемента. В NumPy также существует аналогичная функция для поиска минимального элемента (угадайте, как она называется)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from utils import visualize_1d\n",
+ "\n",
+ "\n",
+ "time = np.linspace(0, np.pi * 2, 500)\n",
+ "signal_high_freq = np.sin(time * 10)\n",
+ "signal = 5 * np.sin(time * 0.5) * signal_high_freq\n",
+ "\n",
+ "extremum = [(time[np.argmax(signal)], np.max(signal))]\n",
+ "visualize_1d(time, signal, extremum)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Мы также можем искать позицию экстремума и в многомерном массиве, однако, здесь есть небольшой нюанс."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(4, 4))\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "position_min = np.argmin(array)\n",
+ "print(f\"{position_min = }\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В примере выше мы получили одно число вместо ожидаемой позиции минимального элемента в двумерном массиве. Но почему? Все дело в том, что для упрощения поиска, NumPy сначала \"вытягивает\" многомерный массив в одномерны. Т.е. фактически, в примере выше произошло следующее:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array_flatten = array.flatten()\n",
+ "print(f\"array flattened:\\n{array_flatten}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(f\"elem min: {array_flatten[position_min]}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Теперь мы понимаем, как именно была посчитана позиция минимального элемента в случае двумерного массива. Однако от этого легче нам не становится, ведь мы по прежнему не знаем индексы минимума в двумерном массиве. В этом случае мы, конечно, могли бы написать функцию для перевода индекса одномерного массива в индексы колонок и столбцов двумерного массива, которые соответствуют минимальному элементу. Но что делать если мы имеем дело с четырехмерным массивом? А с восьмимерным? Неужели нам придется писать функции перевода для каждого случая?\n",
+ "\n",
+ "На самом деле нет, ведь в NumPy все уже давно реализовано:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "position_min_2d = np.unravel_index(position_min, shape=array.shape)\n",
+ "print(f\"position_min = {position_min_2d}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Стоит иметь в виду описанyю особенность при работе с многомерными массивами и не забывать о функции `unravel_index`.\n",
+ "\n",
+ "### Замечание\n",
+ "\n",
+ "Большая часть функций, описанных выше, является аналогами соответствующих функций в \"чистом\" Python, однако, не стоит считать, что они взаимозаменяемы. Во-первых, аналоги из Python будут работать значительно медленнее функций из NumPy в силу причин, которые были рассмотрены на предыдущих семинарах. Во-вторых, функции Python не умеют корректно работать с многомерными массивами, в отличие от функций NumPy. Более того, функции NumPy позволяют настраивать агрегирование, о чем мы поговорим далее.\n",
+ "\n",
+ "## Агрегирование по измерениям\n",
+ "\n",
+ "Во всех примерах выше мы применяли операции агрегирования к одномерным массивам. Однако, как было сказано в предыдущем абзаце, мы можем применять указанные операции и к многомерным массивам."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(3, 4))\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"sum: {np.sum(array)};\",\n",
+ " f\"cumulative sum: {np.cumsum(array)};\",\n",
+ " f\"max: {np.max(array)};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как вы видите, в этом случае операция будет применяться ко всему массиву, игнорируя его пространственные данные. Однако, с помощью специального аргумента `axis` вы можете выбирать, для каких именно измерений массива применять ту или иную операцию. Рассмотрим эти возможности на примере функции `sum`, поскольку поведение остальных функций в контексте их применения для конкретных измерений, не будет отличаться."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(3, 4))\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"sum whole: {np.sum(array)};\",\n",
+ " f\"sum per column: {np.sum(array, axis=0)};\",\n",
+ " f\"sum per row: {np.sum(array, axis=1)};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как вы видите, теперь у нас есть возможность агрегировать данные по определенным измерениям. Обращаем ваше внимание на то, что массив \"схлопывается\" вдоль указанных измерений, а результирующий массив имеет меньшую размерность, при этом сохраняя свои исходные размеры вдоль оставшихся измерений. При необходимости вы можете сохранить и размерность исходного массива, однако размер вдоль агрегированного измерения все равно будет \"схлопнут\"."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"sum per column: {np.sum(array, axis=0, keepdims=True)};\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Сохранение размерности бывает очень полезным, особенно при использовании результирующего массива в векторизованных операциях с применением трансляции.\n",
+ "\n",
+ "Помимо указания измерения в виде целых неотрицательных чисел, вы можете задавать измерения агрегации с помощью кортежей, в этом случае агрегация будет происходить вдоль указанных измерений. Также вы можете использовать отрицательные целые числа. Например, `-1` - в этом случае агрегация будет осуществлена по последнему измерению массива."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(2, 3, 2))\n",
+ "print(f\"array:\\n{array};\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"axis negative:\\n{np.sum(array, axis=-2)}\",\n",
+ " f\"axis tuple:\\n{np.sum(array, axis=(0, 2))}\",\n",
+ " f\"axis tuple keepdim:\\n{np.sum(array, axis=(0, 2), keepdims=True)}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Сортировки\n",
+ "\n",
+ "### Сортировка\n",
+ "\n",
+ "Из курса по алгоритмам и структурам данных вам должна быть известна важность алгоритмов сортировки. Зачастую сортировка является полезной как сама по себе, так и как шаг в более сложных алгоритмах. Именно поэтому в NumPy реализована возможность сортировки массивов. Подобно \"чистому\" Python функция сортировки существует в двух вариантах: как отдельная функция, которая создает новый массив с отсортированными значениями, и как метод массива, который осуществляет сортировку на месте."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=10)\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "array_sorted = np.sort(array)\n",
+ "print(\n",
+ " f\"array original:\\n{array}\",\n",
+ " f\"array sorted:\\n{array_sorted}\",\n",
+ " f\"array_sorted is array: {array_sorted is array}\",\n",
+ " sep=\"\\n\",\n",
+ " end=\"\\n\\n\"\n",
+ ")\n",
+ "\n",
+ "array.sort()\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В отличие от функций Python данные функции имеют ряд интересных особенностей, помимо разницы в производительности. С одной из таких особенностей мы уже знакомы: возможность применения операции вдоль определенного измерения."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(3, 4))\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"default sort:\\n{np.sort(array)}\",\n",
+ " f\"per row sort:\\n{np.sort(array, axis=-1)}\",\n",
+ " f\"per column sort:\\n{np.sort(array, axis=0, kind='quicksort')}\",\n",
+ " sep=\"\\n\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Следующая интересная особенность функций сортировки - возможность выбора алгоритма сортировки. Если в Python функции сортировки использовали *timsort*, то в NumPy вы можете выбрать подходящий для конкретной задачи алгоритм, передав в функцию параметр `kind`. На выбор доступны следующие варианты: *quicksort*, *mergesort*, *heapsort*, *stable*.\n",
+ "\n",
+ "### Сортировка индексов\n",
+ "\n",
+ "Помимо обычных сортировок в NumPy реализована возможность вычисления сортировки по аргументу. Т.е. существуют специальные функции, которые вычисляет не отсортированный массив, а массив индексов. Индексы расположены так, как соответствующие им элементы должны были бы располагаться в отсортированном массиве, при этом сам массив изменений не претерпевает."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=10)\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "indices = np.argsort(array)\n",
+ "print(\n",
+ " f\"array:\\n{array}\",\n",
+ " f\"indices:\\n{indices}\",\n",
+ " f\"array sorted:\\n{array[indices]}\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Данная функция также может быть использована вдоль определенных измерений массива."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "array = np.random.randint(-10, 10, size=(3, 4))\n",
+ "print(f\"array:\\n{array}\", end=\"\\n\\n\")\n",
+ "\n",
+ "print(\n",
+ " f\"default sort:\\n{np.argsort(array)}\",\n",
+ " f\"per row sort:\\n{np.argsort(array, axis=-1)}\",\n",
+ " f\"per column sort:\\n{np.argsort(array, axis=0)}\",\n",
+ " sep=\"\\n\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Обратите ваше внимание, что сортировка осуществляется в порядке не убывания, а аргумент `reverse` отсутствует. Подумайте о возможных способах реализации сортировки в порядке не возрастания с помощью операций NumPy."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Практика 1. МНК\n",
+ "\n",
+ "Необходимо реализовать функцию для нахождения коэффициентов линейной функции с помощью МНК.\n",
+ "\n",
+ "*Входные данные*:\n",
+ "- `abscissa` - np.ndarray, абсциссы точек;\n",
+ "- `ordinates` - np.ndarray, ординаты точек;\n",
+ "\n",
+ "*Выходные данные*:\n",
+ "- Кортеж (`tuple`) двух чисел с плавающей точкой: коэффициент наклона `a` и смещение `b` функции $y = ax + b$;\n",
+ "\n",
+ "*Сторонние эффекты*:\n",
+ "- Если количества элементов во входных массивах `abscissa` и `ordinates` отличаются, необходимо возбудить исключение `ShapeMismatchError`.\n",
+ "\n",
+ "*Замечания*:\n",
+ "- Гарантируется, что на вход подаются непустые одномерные массивы чисел с плавающей точкой;\n",
+ "- Гарантируется, что входные массивы не описывают функции $y = const$ и $x = const$;\n",
+ "- Формулы для нахождения коэффициентов линейной функции $y = ax + b$ с помощью МНК:\n",
+ "\n",
+ " $$ a = \\frac{ - }{ - ^2} $$\n",
+ " \n",
+ " $$ b = - a $$\n",
+ "\n",
+ " Нотация `<...>` используется для обозначения среднего значения.\n",
+ "\n",
+ "**Решение**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from numbers import Real\n",
+ "\n",
+ "from utils import visualize_lsm"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class ShapeMismatchError(Exception):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_lsm_coefficients(\n",
+ " abscissa: np.ndarray,\n",
+ " ordinates: np.ndarray,\n",
+ ") -> tuple[Real, Real]:\n",
+ " # код семинариста\n",
+ "\n",
+ " return 0, 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Проверка**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "abscissa = np.linspace(0, 10, 100)\n",
+ "ordinates_experiment = abscissa * 5 + 3\n",
+ "ordinates_experiment += 2 * np.random.normal(size=abscissa.size)\n",
+ "\n",
+ "incline, shift = get_lsm_coefficients(abscissa, ordinates_experiment)\n",
+ "print(\n",
+ " f\"computed incline: {incline:.2f};\",\n",
+ " f\"computed shift: {shift:.2f};\",\n",
+ " sep=\"\\n\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "visualize_lsm(\n",
+ " abscissa=abscissa,\n",
+ " ordinates_experiment=ordinates_experiment,\n",
+ " ordinates_computed=incline * abscissa + shift,\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/lessons/sem02/lesson04/requirements.txt b/lessons/sem02/lesson04/requirements.txt
new file mode 100644
index 00000000..039f1d50
--- /dev/null
+++ b/lessons/sem02/lesson04/requirements.txt
@@ -0,0 +1,3 @@
+matplotlib==3.8.0
+numpy==1.26.1
+opencv-python-headless==4.9.0.80
diff --git a/lessons/sem02/lesson04/utils/__init__.py b/lessons/sem02/lesson04/utils/__init__.py
new file mode 100644
index 00000000..4de36006
--- /dev/null
+++ b/lessons/sem02/lesson04/utils/__init__.py
@@ -0,0 +1,7 @@
+from .io_data import get_image
+
+from .visualize import (
+ compare_images,
+ visualize_1d,
+ visualize_lsm,
+)
diff --git a/lessons/sem02/lesson04/utils/io_data.py b/lessons/sem02/lesson04/utils/io_data.py
new file mode 100644
index 00000000..26b2a022
--- /dev/null
+++ b/lessons/sem02/lesson04/utils/io_data.py
@@ -0,0 +1,7 @@
+import cv2 as cv
+import numpy as np
+
+
+def get_image(path_to_image: str) -> np.ndarray:
+ image = cv.imread(path_to_image)
+ return cv.cvtColor(image, code=cv.COLOR_BGR2RGB)
diff --git a/lessons/sem02/lesson04/utils/visualize.py b/lessons/sem02/lesson04/utils/visualize.py
new file mode 100644
index 00000000..6d2b746b
--- /dev/null
+++ b/lessons/sem02/lesson04/utils/visualize.py
@@ -0,0 +1,70 @@
+from typing import Optional
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def visualize_1d(
+ abscissa: np.ndarray,
+ ordinates: np.ndarray,
+ points: Optional[list[tuple[int, int]]] = None,
+) -> None:
+ _, axis = plt.subplots(figsize=(16, 9))
+
+ axis.plot(abscissa, ordinates, c="royalblue")
+
+ shift = 0.05 * (ordinates.max() - ordinates.min())
+
+ axis.set_xlim(np.min(abscissa), np.max(abscissa))
+ axis.set_ylim(np.min(ordinates) - shift, np.max(ordinates) + shift)
+ axis.grid(True)
+
+ if points:
+ points = list(zip(*points))
+ axis.scatter(*points, s=80, c="r", marker="x")
+
+ plt.show()
+
+
+def visualize_lsm(
+ abscissa: np.ndarray,
+ ordinates_experiment: np.ndarray,
+ ordinates_computed: np.ndarray,
+) -> None:
+ _, axis = plt.subplots(figsize=(16, 9))
+ axis: plt.Axes = axis
+
+ axis.scatter(
+ abscissa,
+ ordinates_experiment,
+ c="royalblue",
+ s=10,
+ alpha=0.6,
+ label="experiment",
+ )
+ axis.plot(
+ abscissa,
+ ordinates_computed,
+ c="royalblue",
+ label="lsm",
+ )
+
+ axis.set_xlim(np.min(abscissa), np.max(abscissa))
+ axis.legend()
+ axis.grid()
+
+ plt.show()
+
+
+def compare_images(image1: np.ndarray, image2: np.ndarray) -> None:
+ _, (axis1, axis2) = plt.subplots(1, 2, figsize=(16, 8))
+ axis1: plt.Axes = axis1
+ axis2: plt.Axes = axis2
+
+ axis1.imshow(image1)
+ axis2.imshow(image2)
+
+ axis1.axis("off")
+ axis2.axis("off")
+
+ plt.show()
diff --git a/sources/presentation_template.pptx b/sources/presentation_template.pptx
deleted file mode 100644
index eaf8e2f4..00000000
Binary files a/sources/presentation_template.pptx and /dev/null differ
|