initial commit

main
Alexandr Pilshchikov 2 years ago
commit 53d5933c79

5
.gitignore vendored

@ -0,0 +1,5 @@
.ipynb_checkpoints
*/.ipynb_checkpoints/*
*.swp
.DS_Store
.idea

@ -0,0 +1,136 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Опрос #\n",
"\n",
"Уважаемые слушатели курса «Основы программирования на Python»!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Прошу Вас заполнить опрос, который разработан для изучения общей аудитории курса.\n",
"\n",
"Это займет у Вас не более 5 минут, но я смогу узнать о Вас больше информации, а значит сделать курс еще полезнее для Вас!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"##### 1. Оцените уровень Вашей подготовки в программировании на Python.\n",
" \n",
"- [ ] Высокий\n",
"- [ ] Средний\n",
"- [ ] Начальный\n",
"- [ ] Нулевой"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"##### 2. Был ли у Вас опыт обучения программированию на Python до начала прохождения курса?\n",
"\n",
"- [ ] Нет, опыт обучения отсутствовал\n",
"- [ ] Да, я изучал статьи и литературу, проходил онлайн-курсы\n",
"- [ ] Да, я посещал очные учебные курсы\n",
"- [ ] Да, у меня есть дипломы/сертификаты образовательных программ"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"##### 3. Был ли у Вас опыт работы с применением программирования на Python?\n",
"\n",
"- [ ] Нет, не было\n",
"- [ ] Да, был непродолжительный опыт\n",
"- [ ] Да, был продолжительный опыт"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"##### Что Вы ожидаете от курса:\n",
"___\n",
"___\n",
"___"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"[Далее...](../2.%20Первые%20шаги/О%20языке.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,143 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Приветствие #\n",
"\n",
"Рад приветствовать вас на курсе «Основы программирования на Python»!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Почему именно Python? ##\n",
"\n",
"**Python** - это простой, гибкий, популярный, и, что самое главное, востребованный язык программирования, на котором можно написать практически всё.\n",
"\n",
"Создавать веб-проекты, заниматься машинным обучением и анализом данных, автоматизировать задачи системного администрирования и многое другое."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Создавая этот курс, я стремился покрыть все темы, необходимые разработчику каждый день.\n",
"\n",
"Однако курс — это не просто набор лекций.\n",
"\n",
"Вас также ждёт большое количество практических примеров и задач.\n",
"\n",
"Именно это позволит вам оттачивать ваши знания и навыки по мере их получения."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В первом блоке курса мы познакомимся с интерпретатором Python, посмотрим на основные конструкции и типы языка."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Затем нас ждёт знакомство со структурами данных и с функциями."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Дальше вас ждёт погружение в мир объектно-ориентированного программирования."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"В пятом блоке курса вы познакомитесь с многопоточным и асинхронным программированием."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Чтобы научиться программировать на Python, нужно как можно быстрее начать разрабатывать свои собственные настоящие программы.\n",
"\n",
"У вас будет возможность заняться этим вплотную на шестом блоке обучения.\n",
"\n",
"В итоговом проекте вы создадите своё сетевое клиент-серверное приложение.\n",
"\n",
"С подобными задачами сталкиваются разработчики в нашем институте."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Готовы? Давайте уже начнём программировать. [Далее...](Опрос.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1 @@
<svg width="2500" height="2490" viewBox="0 0 256 255" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><defs><linearGradient x1="12.959%" y1="12.039%" x2="79.639%" y2="78.201%" id="a"><stop stop-color="#387EB8" offset="0%"/><stop stop-color="#366994" offset="100%"/></linearGradient><linearGradient x1="19.128%" y1="20.579%" x2="90.742%" y2="88.429%" id="b"><stop stop-color="#FFE052" offset="0%"/><stop stop-color="#FFC331" offset="100%"/></linearGradient></defs><path d="M126.916.072c-64.832 0-60.784 28.115-60.784 28.115l.072 29.128h61.868v8.745H41.631S.145 61.355.145 126.77c0 65.417 36.21 63.097 36.21 63.097h21.61v-30.356s-1.165-36.21 35.632-36.21h61.362s34.475.557 34.475-33.319V33.97S194.67.072 126.916.072zM92.802 19.66a11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13 11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.13z" fill="url(#a)"/><path d="M128.757 254.126c64.832 0 60.784-28.115 60.784-28.115l-.072-29.127H127.6v-8.745h86.441s41.486 4.705 41.486-60.712c0-65.416-36.21-63.096-36.21-63.096h-21.61v30.355s1.165 36.21-35.632 36.21h-61.362s-34.475-.557-34.475 33.32v56.013s-5.235 33.897 62.518 33.897zm34.114-19.586a11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.131 11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13z" fill="url(#b)"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,197 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Выбор среды разработки (IDE) #"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Писать код можно в любом текстовом редакторе, однако специализированные редакторы и полноценные среды разработки (IDE) добавляют процессу программирования массу удобств таких как подсветка синтаксиса, автодополнение, интроспекция кода (возможность по клику перейти к месту объявления используемого класса или функции) и многие другие."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Если вы не уверены, в чем вам программировать попробуйте PyCharm Community Edition. Это полноценная IDE, которая позволит вам даже запускать код на Python, не выходя из редактора. Чтобы установить PyCharm Community Edition, перейдите по ссылке https://www.jetbrains.com/pycharm/download/ вам будет предложено скачать на выбор Professional или Community версию среды разработки PyCharm. Professional версия платная, для полноценной работы с Python будет достаточно бесплатной Community версии. Скачайте установщик PyCharm Community Edition для вашей операционной системы. После этого запустите установщик и следуйте инструкциям."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Также можете посмотреть в сторону Visual Studio Code (https://code.visualstudio.com/Download) для разработки на Python вам дополнительно потребуется установить плагин \"Python\".\n",
"\n",
"Если вы программист с опытом, то возможно вам будет удобно писать код на Python в редакторах Vim (http://www.vim.org/) или Emacs (http://www.gnu.org/software/emacs/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Среди других вариантов можно отметить редактор Atom (https://atom.io/), а также любимый многими за простоту и расширяемость Sublime Text (https://www.sublimetext.com/) или Notepad++ (https://notepad-plus-plus.org/).\n",
"\n",
"Особенно хочу выделить систему Anaconda (https://www.anaconda.com/), которая содержит в себе IDE Spyder (https://www.spyder-ide.org/) и крайне удобную подсистему Jupyter (https://jupyter.org/), которую в ходе курса я не раз продемонстрирую.\n",
"\n",
"Как вы можете видеть редакторов и IDE много, выбирайте по вкусу. Я же подробно опишу процесс создания нового Python проекта в PyCharm Community Edition если не знаете какой редактор выбрать, советую остановиться именно на нем."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Создание нового проекта в PyCharm Community Edition ##\n",
"\n",
"Когда PyCharm открылся нажмите New Project (Новый Проект)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![PyCharm](pycharm1.png \"PyCharm\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Задайте путь и имя проекта, а также выбирите интерпретатор Python 3, который вы установили. Если вы не знаете, где на файловой системе находится путь до интерпретатора, вот как можно его найти:\n",
"\n",
"- На Windows в терминале наберите `where python3` (или `where python` в зависимости от того, как у вас запускается Python 3)\n",
"- На Unix-системах (Linux, MacOS ...) наберите в терминале `which python3` (или `which python` в зависимости от того, как у вас запускается Python 3)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![PyCharm](pycharm2.png \"PyCharm\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"После задания имени проекта и пути до интерпретатора в окне PyCharm нажмите кнопку Create.\n",
"\n",
"Откроется окно редактора. Слева на боковой панели нажмите на файле main.py. Файл откроется в окне редактора с примером кода на Python."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![PyCharm](pycharm3.png \"PyCharm\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В меню щелкните Run -> Run. Внизу должно появиться окно терминала, и в нем должен присутствовать вывод нашей программы."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![PyCharm](pycharm4.png \"PyCharm\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Дополнительные материалы ##\n",
"\n",
"- Выбираем самый удобный редактор кода Python (https://habr.com/ru/company/skillfactory/blog/521838/)\n",
"\n",
"[Далее...](Начинаем%20программировать.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,543 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Начинаем программировать #\n",
"\n",
"Теперь, когда Python 3 установлен в систему и я надеюсь вы выбрали редактор или среду разработки, в которых будете писать код на Python'е, мы можем начать программировать."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Первое на что мы посмотрим - это интерактивный интерпретатор Python. Чтобы запустить интерактивный интерпретатор, нам нужно в терминале набрать команду `python3`. Обратите внимание, что в зависимости от вашей системы и способа, которым вы устанавливали интерпретатор python'а 3 в систему, запуск, имя команды может отличаться и, например, быть просто `python`. В моем случае это `python3`. Обратите внимание, что когда я запускаю, версия интерпретатора указана вот здесь, в начале приветственной строки и нам нужна версия >=3.6.0.\n",
"\n",
"![Python](python.png \"Python\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Интерактивный интерпретатор, по большому счету, - это программа, которая считывает ввод пользователя, интерпретирует его и выдает на экран результат. Интерпретатор - это то, что делает Python таким доступным. В любой момент вы можете открыть интерактивную оболочку и проверить какую-то свою гипотезу, поэкспериментировать или просто воспользоваться ей как калькулятором."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Давайте это и сделаем. Мы можем сложить два числа, либо поделить два числа."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 + 1"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"0.5"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 / 2"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Давайте попробуем напечатать на экран какую-нибудь строку, используя для этого встроенную функцию `print`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hello\n"
]
}
],
"source": [
"print(\"hello\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"У нас получилось. В любой момент, находясь в интерактивном интерпретаторе, мы можем воспользоваться встроенной функцией help. Встроенная функция help позволяет получить справку по любому объекту, который доступен в области видимости. Посмотрим справку по объекту help. Это функция, и мы видим, что справка вывелась на экран, описание, что такое функция `print`, какие аргументы она принимает."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Help on built-in function print in module builtins:\n",
"\n",
"print(...)\n",
" print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
" \n",
" Prints the values to a stream, or to sys.stdout by default.\n",
" Optional keyword arguments:\n",
" file: a file-like object (stream); defaults to the current sys.stdout.\n",
" sep: string inserted between values, default a space.\n",
" end: string appended after the last value, default a newline.\n",
" flush: whether to forcibly flush the stream.\n",
"\n"
]
}
],
"source": [
"help(print)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Для того чтобы из справки выйти, мы можем нажать клавишу `q`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Сейчас мы ненадолго расстанемся с интерактивным интерпретатором. Чтобы выйти из него, можно нажать `Ctrl + D`, либо использовать доступную в интерпретаторе функцию `exit`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"А сейчас давайте поговорим о том, как в реальных проектах пишут код на Python'е. В реальных проектах код на Python'е пишут конечно же не в интерактивном интерпретаторе, а в файлах. А в больших проектах - в большом количестве файлов. Скоро мы вас научим как структурировать код на Python'е по модулям и пакетам, а сейчас нашей целью будет создать простейший файл, который будет содержать код на Python'е и запустить его интерпретатором Python. Давайте это сделаем. Я буду использовать текстовый редактор `vim`, для того чтобы создать файл. Обратите внимание, что я назвал файлик `example.py` и использовал расширение `py`. Это то расширение, которое принято давать файлам, которые содержат код на Python. Внутри файлика напишем простейшую инструкцию `print(\"hello\")`, сохраним его. Теперь, чтобы запустить, мы можем воспользоваться командой `python3`. В качестве аргумента передать ей имя нашего файла, который только что создали, и программа исполнится."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hello\n"
]
}
],
"source": [
"! python example.py"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Продолжим. Сейчас мы вернемся в интерактивный интерпретатор и поговорим с вами о переменных. Переменная - это то, что позволяет сохранить результат выполнения выражения для того, чтобы использовать его в дальнейшем. Наверняка вы встречались с переменными в других языках программирования. В Python'е для того, чтобы объявить переменную нужно написать следующее, например, `num = 1`. Мы используем знак `=`. Это оператор присваивания. В данном случае мы связываем имя переменной `num` с целочисленным объектом со значением `1`. В любой момент мы можем переприсвоить имени `num` значение другое - `num = 2`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"num = 1"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"num = 2"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Также в любой момент мы можем присвоить переменной `num` объект другого типа. В данном случае это строковый объект."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"num = \"hello\""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Поговорим о названиях переменных в Python. В Python имена переменных могут содержать буквы, цифры и символ нижнего подчеркивания. При этом начинаться переменная должна либо с буквы, либо с символа нижнего подчеркивания, то есть вот это правильное название переменной в Python, а вот, например, вот такое название уже будет неверным."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"hello_world = \"Hello, world!\""
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"ename": "SyntaxError",
"evalue": "invalid decimal literal (<ipython-input-21-c463ffa3a285>, line 1)",
"output_type": "error",
"traceback": [
"\u001b[1;36m File \u001b[1;32m\"<ipython-input-21-c463ffa3a285>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m 42_answer = 42\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid decimal literal\n"
]
}
],
"source": [
"42_answer = 42"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В данном случае происходит ошибка, выбрасывается исключение, это синтаксическая ошибка. Мы видим `SyntaxError`. В дальнейших лекциях нашего курса я научу вас обрабатывать эти исключения. Что важно отметить еще это то, что если переменная состоит из нескольких слов, то есть это длинное название переменной, то ее принято называть так называемым snake_case'ом. То есть слова начинаются с маленькой буквы, и отдельные слова разделены символом нижнего подчеркивания. В других языках вы могли видеть, что переменные называются в так называемом camelCase'ом. В Python'е так делать не принято.\n",
"\n",
"Свод рекомендаций в оформлению кода представлен в PEP8 (https://www.python.org/dev/peps/pep-0008/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Поговорим еще об одной особенности синтаксиса Python, а именно о том, как отделять блоки кода. В других языках вы могли видеть, что блоки кода отделяются фигурными скобочками, в Python'е не так. В Python'е блоки кода нужно отделять с помощью отступов.\n",
"\n",
"Что это значит? Давайте напишем небольшую программку, которая будет печатать на экран числа от 0 до 3. Обратите внимание, что блок кода внутри конструкции for, о которой мы еще с вами будем говорить в дальнейшем, я отделил с помощью четырех пробелов."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n"
]
}
],
"source": [
"for item in range(4):\n",
" print(item)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Это то, как принято отделять блоки кода в Python. Вам, конечно, не придется каждый раз нажимать клавишу Пробел четыре раза. Зачастую это будет делать за вас редактор или IDE, которое вы используете. Последнее на что мы посмотрим, это то как комментировать код. Давайте вернемся в файл `example.py`, который создавали незадолго до этого и попробуем написать комментарий."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"# комментарий"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В Python'е комментарий пишется с использованием специального символа \"решетка\", после которого идет текст комментария. Также комментарий можно писать на строках с выражениями. Это так называемые `inline` комментарии."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"'''\n",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n",
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n",
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n",
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n",
"'''\n",
"\n",
"num = 42 # Answer to the Ultimate Question of Life, The Universe, and Everything"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Также вы можете увидеть, что время от времени в программах встречаются многострочные комментарии. Однако этот комментарий не игнорируется интерпретатором Python. Это строковый литерал, который зачастую вы будете видеть, как часть документации функции, либо класса. Этот строковый литерал становится частью объекта, к которому можно достучаться, используя специальное свойство `doc` у объекта."
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
"\n",
"Prints the values to a stream, or to sys.stdout by default.\n",
"Optional keyword arguments:\n",
"file: a file-like object (stream); defaults to the current sys.stdout.\n",
"sep: string inserted between values, default a space.\n",
"end: string appended after the last value, default a newline.\n",
"flush: whether to forcibly flush the stream.\n"
]
}
],
"source": [
"print(\n",
" print.__doc__\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Давайте на это посмотрим. Например, функция print, которую мы использовали содержит атрибут doc, в котором содержится строка документирования, которая определена у этой функции."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В этой лекции мы с вами познакомились с интерактивным интерпретатором Python, посмотрели на особенности синтаксиса Python, поговорили о том, как отделять блоки кода в Python'е, как называть переменные и написали первую небольшую программу в отдельном файле. В дальнейшем нас ждет знакомство с основными типами, которые есть в языке."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Дополнительные материалы к лекции ##\n",
"\n",
"- REPL — Википедия (https://ru.wikipedia.org/wiki/REPL)\n",
"- Как запустить скрипт на Python (https://mkdev.me/posts/kak-zapustit-skript-na-python)\n",
"- Комментирование Python кода (https://dev-gang.ru/article/kommentirovanie-python-koda-auf6lgv2vv/)\n",
"- Правильный выбор имен переменных в Python (http://pythonlearn.ru/python-dlya-nachinayushhix/imena-dlya-peremennyx-v-python/)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,189 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# О языке #\n",
"\n",
"Давайте сначала немножко поговорим об истории языка."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В конце 80-х годов прошлого века сотрудник голландского центра математики и информатики Гвидо ван Россум решил создать свой собственный язык."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![guido.jpg](guido.jpg)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Основной целью он ставил создать язык простой и выразительный, на котором было бы просто писать код.\n",
"\n",
"В 1991 году он опубликовал исходники языка, который получил название Python. \n",
"\n",
"Не в честь известного всем вида пресмыкающихся, а в честь популярного телешоу 70-х годов прошлого века \"Летающий цирк Монти Пайтона\".\n",
"\n",
"Однако это не помешало змее стать маскотом, символом языка, а также присутствовать на логотипах языка и связанных с ним проектах."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"![logo.png](logo.svg)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Сейчас, спустя много лет, мы видим, что у Гвидо получилось.\n",
"\n",
"У него получилось создать интерпретируемый язык с динамической типизацией и автоматической сборкой мусора, на котором действительно приятно писать код."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Читая код \"на питоне\", мы практически читаем книгу на английском языке.\n",
"\n",
"Конечно это преувеличение, однако Python действительно выделяется в этом отношении.\n",
"\n",
"Также за годы существования языка вокруг него сложилось огромное сообщество, и была написана масса готовых библиотек на все случаи жизни.\n",
"\n",
"Я ставлю цель сделать вас частью этого сообщества по завершению этого курса."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Разработчики языка, выпустили третью версию, обратно несовместимую со второй. Это было сделано специально для того, чтобы решить некоторые архитектурные недостатки второй версии языка. Это получилось сделать, однако обратная несовместимость привела к тому, что до сих пор многие продакшн-системы используют Python версии 2. Но в 2020 году настал дедлайн и официальная поддержка Python 2 прекратилась, поэтому новые проекты стоит начинать именно на Python'е 3. Тем более, если вдруг вам потребуется узнать отличие Python'а 2 от Python'а 3, вы сможете это сделать достаточно быстро за 12 дня. Всё это есть в Интернете. На курсе я буду рассказывать про Python 3, а именно Python версии 3.6."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Python — это название спецификации языка, основная его реализация написана на языке C, называется CPython. Есть и другие реализации спецификации языка Python такие, как IronPython для .NET, либо PyPy, который добавляет JIT-компиляцию коду. Однако в курсе, когда я буду говорить слово Python, я имею в виду именно реализацию на C — CPython. Исходный код CPython открыт и доступен по ссылке на github https://github.com/python/cpython."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"У Python великолепная документация с большим количеством примеров, которые ставят в пример другим языкам, она также доступна в Интернете по ссылке https://docs.python.org/3.6/."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Развитие языка Python происходит согласно чётко регламентированному процессу создания, обсуждения, отбора и реализации документов PEP.\n",
"\n",
"PEP - Python Enhancement Proposal - это предложения по развитию питона https://www.python.org/dev/peps/.\n",
"\n",
"Процесс PEP является основным механизмом для предложения новых возможностей и для документирования проектных решений, которые прошли в Python."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Теперь, прежде чем начать программировать, нам осталось решить два вопроса.\n",
"\n",
"Первый — это как установить Python 3 в систему, и второй — в каком редакторе писать код на Python'е.\n",
"\n",
"Давайте разберемся с этими вопросами. [Далее...](Установка%20Python%203.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 1,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,72 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Полезные ссылки #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python язык с огромной экосистемой. Чтобы помочь вам в этой экосистеме ориентироваться, в этом документе я постараюсь дать некоторые полезные советы и ссылки для начинающих программировать на Python.\n",
"\n",
"Начнем с основной ссылки документации Python 3. Документация Python чрезвычайно подробна и наполнена большим количеством примеров ее часто ставят в пример другим языкам (https://docs.python.org/3/).\n",
"\n",
"Если вы знаете язык программирования C, то в любой непонятной ситуации вы всегда можете заглянуть в исходный код CPython, который доступен на Github (https://github.com/python/cpython).\n",
"\n",
"Красивый код это одна из мантр языка Python. Все Python-программисты стараются следовать советам по стилю кода, описанным в документе PEP 8 (https://www.python.org/dev/peps/pep-0008/).\n",
"\n",
"Есть утилита autopep8, которая позволяет автоматически приводить код к виду, соответствующему PEP 8 (https://pypi.python.org/pypi/autopep8).\n",
"\n",
"Библиотеки, написанные сообществом, находятся на ресурсе PyPI (Python Package Index) (https://pypi.python.org/pypi).\n",
"\n",
"Именно с этого ресурса будут устанавливаться внешние пакеты, когда вы будете устанавливать их с помощью утилиты pip. Лучший способ найти библиотеку для решения той или иной задачи постараться загуглить ее часто поиск Google выдает наиболее релевантный вариант. На GitHub есть коллекция хороших библиотек для решения всевозможных задач (https://github.com/vinta/awesome-python).\n",
"\n",
"Большая база вопросов и ответов по Python сосредоточена на ресурсе Stack Overflow (https://stackoverflow.com/) вы будете часто натыкаться на него, когда будете искать решение в непонятных ситуациях.\n",
"\n",
"Большинство из материалов по ссылкам выше на английском языке. Однако и в русском сегменте интернета информации по Python достаточно. Обратите внимание, что многое из того, что вы найдете может относиться к Python второй версии обращайте на это внимание, мы с вами изучаем Python 3. Хорошие ресурсы для новичков в Python на русском языке:\n",
"\n",
"https://pythonworld.ru/samouchitel-python\n",
"\n",
"https://metanit.com/python/tutorial/\n",
"\n",
"Также есть русскоязычная версия портала stackoverflow, где есть множество вопросов/ответов с тегом \"python\" (https://ru.stackoverflow.com/questions/tagged/python).\n",
"\n",
"Таблица cоответствия консольных команд Windows и Linux (https://white55.ru/cmd-sh.html).\n",
"\n",
"Не забывайте и про книги - их про Python очень много, в том числе и на русском языке. Опять же, обращайте внимание на версию языка, которая в них описана.\n",
"\n",
"Тем, кто любит книги, советуем обратить внимание на следующие издания:\n",
"\n",
"- Марк Саммерфилд - Программирование на Python 3. Подробное руководство\n",
"- Марк Лутц - Изучаем Python, 5-е издание\n",
"\n",
"Эти книги, особенно вторая, могут показаться тяжелыми для чтения, так как являются очень подробными справочниками по языку Python. Однако в них собраны практически все сведения о языке."
]
}
],
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,70 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Работа в терминале #"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"В лекциях мы будем много времени проводить работая в терминале. Я буду использовать терминалы Unix-подобных операционных систем. Обратите внимание, что если вы пользуетесь командной строкой Windows то команды, доступные там, будут немного отличаться от тех, что используются в лекциях. Это не должно вас смущать это не имеет прямого отношения к программированию на языке Python, и для всех команд есть аналоги (соответствие команд можно найти на этой странице - https://white55.ru/cmd-sh.html)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Работа в терминале это неотъемлемая часть профессии программиста, мы проводим в терминале очень много времени, выполняя в нем очень много полезных действий при разработке проектов. Однако, чтобы писать код на Python вам совсем не обязательно пользоваться командной строкой. Достаточно установить PyCharm Community Edition, создать в нем новый проект и можно начинать творить. Я расскажу про выбор IDE как установить PyCharm, создать в нем проект и запустить код."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Также на лекциях я буду использовать текстовый редактор vim, который запускается непосредственно в терминале. Если вы никогда ранее не пользовались этим редактором или у вас Windows создавайте и редактируйте файлы с помощью любого другого текстового редактора. Я перечислю ряд специализированных редакторов для удобного программирования на Python в следующей теме. [Далее...](Выбор%20среды%20разработки.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,268 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Установка Python 3 #\n",
"\n",
"В этой части я помогу вам с установкой Python 3.\n",
"\n",
"Во множестве операционных систем Python установлен по умолчанию, однако на данный момент это чаще всего Python версии 2.7, или не самая последняя версия Python 3. Наша цель установить Python 3.6+."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Windows ##"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Перейдите на сайт https://www.python.org/downloads/ и скачайте установщик последней доступной версии Python 3 для Windows."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Запустите установщик. На первом экране обязательно отметьте галочкой опцию `Add Python 3.6 to PATH` это сделает Python 3 доступным в командной строке. Далее следуйте инструкциям, в процессе установки не снимайте галочки у предлагаемых для установки компонентов.\n",
"\n",
"После установки Python 3 и программа IDLE будут доступны в меню Start (Пуск)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"В лекциях я буду часто показывать примеры работы в терминале. На курсе я использую Unix-подобные системы, однако на Windows также есть командная строка."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Для использования Python из командной строки Windows необходимо установить должным образом переменную `PATH`. Мы прописали Python 3.6 в переменную `PATH` во время установки, поставив галочку напротив соответствующей опции.\n",
"\n",
"Давайте откроем командную строку и убедимся, что python3 доступен."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Чтобы открыть терминал в Windows:\n",
"\n",
"Если у вас Windows 8 и выше нажмите кнопку «Поиск» (значок лупы рядом с кнопкой «Пуск»), в панели поиска наберите «Выполнить» и запустите найденную программу.\n",
"Если у вас Windows версии ниже 8 нажмите кнопку «Пуск» и выберите «Выполнить».\n",
"Окно программы \"Выполнить\" можно также вызвать с помощью сочетания клавиш `Win+R`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"В появившемся диалоговом окне программы \"Выполнить\" наберите слово `cmd` и нажмите Enter. Запустится терминал.\n",
"\n",
"Затем наберите `python` - должен запуститься интерактивный интерпретатор. Если команда `python` не запустила интерпретатор попробуйте `python3`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Вы должны увидеть что-то подобное:\n",
"\n",
"![Python](python.png \"Python\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## MacOS ##"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Я опишу 2 возможных способа установить Python 3 на MacOS."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### Способ 1. ###\n",
"\n",
"Перейдите по ссылке http://python.org/download/ и скачайте установщик последней доступной версии Python 3 для MacOS.\n",
"\n",
"Запустите установщик, запустите его. Запустите Python.mpkg в открывшемся окне.\n",
"\n",
"Во время установки вам нужно будет ввести административный пароль.\n",
"\n",
"После установки в папке /Applications появится IDLE."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### Способ 2. ###\n",
"\n",
"С помощью утилиты brew: https://brew.sh/index_ru.html\n",
"\n",
"Вам нужно установить brew, а затем набрать в терминале:\n",
"```\n",
"# brew install python3\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"После установки попробуйте запустить приложение Терминал (установлено по умолчанию) и наберите `python3` должен запуститься интерактивный интерпретатор. Если команда `python3` не запустила интерпретатор попробуйте просто `python`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Linux ##"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"В различных дистрибутивах Linux способы установки Python 3 могут отличаться, вы можете собрать Python 3 из исходного кода.\n",
"\n",
"Например в дистрибутивах основанных на Debian, можно использовать следующую команду: `$ sudo apt-get install python3`, а на RHEL: `$ sudo yum install python3`"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Или собрать самостоятельно:\n",
"\n",
"```\n",
"# wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tgz\n",
"# tar xzvf Python-3.6.1.tgz\n",
"# cd Python-3.6.1\n",
"# ./configure --with-ensurepip=install\n",
"# make\n",
"# make install\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Замечания относительно устанавливаемой версии Python.\n",
"\n",
"Рекомендованная версия - 3.6. Но вы можете установить себе на компьютер любую версию Python начиная с 3.6. [Далее...](Работа%20в%20терминале.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 81 KiB

@ -0,0 +1,23 @@
from random import randint
number = randint(0, 101)
while True:
answer = input("Введите число: ")
if not answer or answer == "exit":
break
if not answer.isdigit():
print("Введите правильное число!")
continue
user_answer = int(answer)
if user_answer > number:
print("Загаданное число меньше")
elif user_answer < number:
print("Загаданное число больше")
else:
print("Совершенно верно!")
break

@ -0,0 +1,651 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Конструкции управления потоком #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Когда вы пишете программу, у вас постоянно возникает необходимость выполнить то или иное действие, тот или иной блок кода в зависимости от выполнения условия. Также может возникнуть необходимость какой-то блок кода выполнить несколько раз. Для этого существуют конструкции управления потоком. Об этих конструкциях мы и будем говорить в этой лекции."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Первое, на что мы посмотрим — это оператор `if`. Оператор `if` используется для выполнения каких-то действий при выполнении условия. Давайте посмотрим на пример."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Условие выполнено!\n"
]
}
],
"source": [
"company = \"vniitf.ru\"\n",
"\n",
"if \"vniitf\" in company:\n",
" print(\"Условие выполнено!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В данном примере мы определяем переменную `company` со значением `vniitf.ru` и далее используем оператор `if` и проверяем наличие подстроки `vniitf` в нашей строке. Если условие выполняется, а условие выполняется в том случае, если оно интерпретируется как `True`, то блок кода под `if` выполняется. В данном случае подстрока `vniitf` есть в строке `vniitf.ru` и наше условие выполняется.\n",
"\n",
"Иногда может быть так, что условие у вас сложное. Никаких проблем, вы можете записывать сложные условия в блоке оператора `if`, то есть вы можете выполнять различные bool'евские выражения, разделенные `or`, `and` и так далее. На втором примере как раз это показано."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Условие выполнено!\n"
]
}
],
"source": [
"company = \"chelaxe.ru\"\n",
"\n",
"if \"vniitf\" in company or company.endswith(\".ru\"):\n",
" print(\"Условие выполнено!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В некоторых случаях нужно выполнить какие-либо действия, когда условие не выполняется. Для этого существует оператор `else`. Оператор `else` в блоке `if…else` позволяет выполнить какой-то код, если условие не выполнилось. Посмотрим на примере."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"company = \"google.com\"\n",
"\n",
"if \"vniitf\" in company:\n",
" print(\"Условие выполнено!\")\n",
"else:\n",
" print(\"Условие не выполнено!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"У нас в данном случае строка `google.com`, мы проверяем условие — наличие подстроки `vniitf` в строке `google.com` Оно, конечно же, не выполняется, но благодаря оператору `else` мы можем это отловить и выполнить код, который печатает нам на экран то, что условие не выполнено. Также есть возможность использовать выражение `if…elif…else`, в котором добавился оператор `elif`. Он используется тогда, когда нужно проверить друг за другом какие-то условия, не связанные друг с другом. Опять же на примере посмотрим."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Подстрока google найдена\n"
]
}
],
"source": [
"company = \"google.com\"\n",
"\n",
"if \"vniitf\" in company:\n",
" print(\"Подстрока vniitf найдена\")\n",
"elif \"google\" in company:\n",
" print(\"Подстрока google найдена\")\n",
"else:\n",
" print(\"Подстрока не найдена\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы определяем переменную со значением `google.com` строковый, и дальше у нас в данном случае срабатывает блок `elif`, который выполняется тогда, когда подстрока `google` была найдена в нашей строке.\n",
"\n",
"Также во многих языках программирования вы могли встречать тернарный оператор. В Python'е есть его аналог, и он записывается вот таким вот образом, который вы видите на примере."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Argentina\n"
]
}
],
"source": [
"score_1 = 5\n",
"score_2 = 0\n",
"\n",
"winner = \"Argentina\" if score_1 > score_2 else \"Jamaica\"\n",
"\n",
"print(winner)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"То есть переменной будет присвоена строка `Argentina` в том случае, если условие выполнено, иначе будет присвоена строка `Jamaica`. Все это записывается в одну строчку, как вы можете видеть. В некоторых случаях это удобно.\n",
"\n",
"Перейдем к другому оператору, оператору `while`. `While` позволяет выполнять блок до тех пор, пока выполняется какое-то условие. Опять же давайте посмотрим на пример."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100\n"
]
}
],
"source": [
"i = 0\n",
"while i < 100:\n",
" i += 1\n",
" \n",
"print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы определяем переменную, `i = 0`, и будем прибавлять к переменной `i` единичку до тех пор, пока она меньше 100. Достаточно всё просто, мы пишем `while i < 100`, прибавляем единичку."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"i = 3\n",
"\n",
"while i >= 0:\n",
" i -= 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сколько раз выполнится блок кода внутри цикла while?\n",
"\n",
"Дальше.\n",
"\n",
"Иногда вам нужно проитерироваться по какой-то последовательности. Для этого в Python существует выражение `for…in`. Мы на данный момент знаем две последовательности, о которых уже рассказывали — это строка и это байтовая строка В примере на слайде мы итерируемся по строке. Строка содержит значение Alex, и мы итерируемся с помощью выражения `for…in`, `for letter in name`, мы принтим буковку. Каждый юникодный символ друг за другом попадает в тело нашего for-цикла и с помощью функции `print` попадает в дальнейшем на экран."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A\n",
"l\n",
"e\n",
"x\n"
]
}
],
"source": [
"name = \"Alex\"\n",
"\n",
"for letter in name:\n",
" print(letter)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Встроенный объект `range` позволяет итерироваться по целым числам. Что это значит? Если мы напишем `for i in range(3)`, то в теле блока `for` мы сначала получим ноль, потом один, потом два, но не три. Когда мы определяем объект `range`, он не включает в себя последнее значение."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n"
]
}
],
"source": [
"for i in range(3):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте посмотрим на чуть более сложный пример."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5050\n"
]
}
],
"source": [
"result = 0\n",
"\n",
"for i in range(101):\n",
" result += i\n",
"\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Это задачка. Может быть, многие слышали историю про Фридриха Гаусса, который решил ее на уроке школьной математики очень быстро. Уверен, что на Python'е мы решим ее не менее быстро и суть ее в том, чтобы получить сумму чисел от 0 до 100. С помощью цикла `for` мы очень просто можем это сделать.\n",
"\n",
"Объект `range` также может инициализироваться с двумя аргументами, для того чтобы проитерироваться по последовательности чисел, начиная с какого-то числа и заканчивая каким-то числом. Но опять же последнее число не будет включено в последовательность."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n",
"6\n",
"7\n"
]
}
],
"source": [
"for i in range(5, 8):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также есть возможность проинициализировать `range` с тремя аргументами, и последним аргументом будет шаг. В данном случае это проще всего понять на примере. Мы итерируемся по числам от одного до десяти с шагом два, и на экран выводятся только нечетные числа, потому что мы перескакиваем по два."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"3\n",
"5\n",
"7\n",
"9\n"
]
}
],
"source": [
"for i in range(1, 10, 2):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также есть возможность итерироваться по числам в обратном порядке. Для этого в качестве шага объекту range нужно передать 1. На примере видно, что мы от десяти до пяти, опять же пять не включительно, в обратном порядке прогулялись."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10\n",
"9\n",
"8\n",
"7\n",
"6\n"
]
}
],
"source": [
"for i in range(10, 5, -1):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Перейдем к оператору `pass`. Оператор `pass` — это оператор, определяющий пустой блок, который ничего не делает. В основном на практике вы будете использовать этот оператор в качестве заглушки на время, пока вам не нужно писать код в каком-то блоке, но потом вы к нему вернетесь и замените оператор `pass` на что-то более полезное. Однако иногда для создания простых классов-наследников оператор `pass` может играть и полезную роль. Об этом вам ещё будет рассказываться в следующих лекциях. На примере мы можем посмотреть, `for i in range(100)`, `pass`, то есть мы итерируемся по числам и не делаем ничего в блоке цикла."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"for i in range(100):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Следующий оператор, на который мы посмотрим — это оператор `break`. Оператор `break` позволяет выйти из цикла досрочно. Что это значит? Давайте посмотрим на простом примере.\n",
"\n",
"У нас есть цикл `while True`, это бесконечный цикл, потому что условие всегда `True`, и он работал бы бесконечно, потребляя ресурсы процессора, если бы не наш оператор `break`, который мы вставили в код. Он отработает тогда, когда переменная `result` становится больше 100. Это позволяет выйти из цикла `while`, и выполнение программы достигает выражения `print(result)` и мы видим результат выполнения. "
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100\n"
]
}
],
"source": [
"result = 0\n",
"\n",
"while True:\n",
" result += 1\n",
" if result >= 100:\n",
" break\n",
"\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Оператор `break` также работает и в циклах `for`, то есть если мы используем `break` внутри `for`, мы выходим из `for`."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n"
]
}
],
"source": [
"for i in range(10):\n",
" if i == 5:\n",
" break\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также давайте посмотрим на оператор `continue`. Оператор `continue` позволяет перейти к следующей итерации в блоке цикла, не выполняя оставшиеся инструкции. Что это значит? Например, у нас есть задача сложить все числа от нуля до девяти, и при этом сложить только четные числа. С помощью `continue` мы можем реализовать это вот так, как вы видите на примере, то есть тогда, когда переменная `i` является нечетным числом, мы пишем `continue` и переходим к следующей итерации цикла `for`. Тем самым в `result` суммируются только четные числа, и мы получаем правильный ответ."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"20\n"
]
}
],
"source": [
"result = 0\n",
"\n",
"for i in range(10):\n",
" if i % 2 != 0:\n",
" continue\n",
" result += i\n",
"\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Выберите варианты, в которых цикл будет работать бесконечно:\n",
"\n",
"- [x] Вариант первый:\n",
"\n",
"```python\n",
"while True:\n",
" continue\n",
"```\n",
"\n",
"- [x] Вариант второй:\n",
"\n",
"```python\n",
"while True:\n",
" pass\n",
"```\n",
"\n",
"- [ ] Вариант третий:\n",
"\n",
"```python\n",
"while True:\n",
" break\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Так же для `for` и `while` есть `else` который срабатывает по окончанию, но не сработает если был использован `break`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"while\n",
"else\n"
]
}
],
"source": [
"flag = True\n",
"\n",
"while flag:\n",
" print(\"while\")\n",
" flag = False\n",
" \n",
"else:\n",
" print(\"else\")"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"while\n"
]
}
],
"source": [
"flag = True\n",
"\n",
"while flag:\n",
" print(\"while\")\n",
" break\n",
" \n",
"else:\n",
" print(\"else\")"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"for\n",
"else\n"
]
}
],
"source": [
"for i in range(1):\n",
" print(\"for\")\n",
" \n",
"else:\n",
" print(\"else\")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"for\n"
]
}
],
"source": [
"for i in range(1):\n",
" print(\"for\")\n",
" break\n",
" \n",
"else:\n",
" print(\"else\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,533 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Базовые типы: логический тип #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На этой лекции мы поговорим о ещё одном базовом типе, который есть в Python, а именно о логическом типе, он же тип `bool`.\n",
"\n",
"В Python'е тип `bool` представлен двумя значениями, как и в других языках — это `True` и это `False`."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"True"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что важно отметить, что в Python тип `bool` является подтипом целого числа, и, по большому счету `True` — это `True` соответствует единице, а `False` соответствует нулю. Если мы присвоим переменной `result` `True`, то, посмотрев на тип переменной, мы увидим, что это как раз тип `bool`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'bool'>\n"
]
}
],
"source": [
"result = True\n",
"print(type(result))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Когда нужны логические типы? По большому счету они нужны тогда, когда нужно проверить на истинность какое-то выражение — является выражение истинным либо является выражение ложью.\n",
"\n",
"Например, в Python'е мы можем проверить, что значения двух объектов равны. Это делается с помощью оператора «равно». Это два знака равно, идущие друг за другом."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"13 == 13"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также есть оператор «не равно», который позволяет сделать противоположное действие, то есть сравнить то, что значения не равны. Обратите внимание, что результатом выполнения обоих выражений являются логические типы — либо `True`, либо `False`."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 != 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Операторы сравнения ##\n",
" - `>` больше;\n",
" - `>=` больше или равно;\n",
" - `<` меньше;\n",
" - `<=` меньше или равно."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n",
"True\n",
"True\n",
"False\n"
]
}
],
"source": [
"print(3 > 3)\n",
"print(3 >= 3)\n",
"print(6 < 7)\n",
"print(6 <= 5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"И также интересной особенностью является наличие в Python'е множественного сравнения."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"x = 2\n",
"print(1 < x < 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы объявляем переменную `x`, которая равна целому числу 2. Дальше записываем множественное сравнение, которое в итоге выдает результат `True`, потому что `x > 1`, и в то же время `x < 3`. Это очень удобно бывает на практике."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Конвертация типов ##\n",
"\n",
"Если мы попробуем преобразовать целое число к типу `bool`, то есть к логическому типу, то если число не равняется нулю, то получим в результате `True`, если 0, то `False`. То же самое верно для вещественных чисел."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(12)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(0)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(-4)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(.5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Поговорим о логических выражениях.\n",
"\n",
"Логические выражения — это выражения, которые содержат в себе один или больше логических операторов. Какие мы знаем логические операторы? Это может быть логическое «и», логическое «или» или, например, логическое отрицание.\n",
"\n",
"В Python'е представлены все эти операторы и, например, логическое «и» записывается с помощью слова `and`.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n"
]
}
],
"source": [
"x, y = True, False\n",
"print(x and y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также есть логическое «или» — это оператор `or`. И есть логическое отрицание — это оператор `not`."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"x, y = True, False\n",
"print(x or y)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"y = False\n",
"print(not y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы можем составлять сложные логические выражения, в которые будут входить несколько логических операторов. При этом в этом логическом выражении у операторов есть свой приоритет, порядок исполнения, как и в математических операциях. Стоит отметить, что этот порядок мы также можем задавать, используя круглые скобки, как и в случае с математическим выражением."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"x, y, z = True, False, True\n",
"result = x and y or z\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Следующая особенность Python — это то, что логические выражения ленивые. Давайте подробно разберем это на примере. У нас есть переменная `x`, которая равняется 12 и переменная `y`, которая равняется `False`. И есть логическое выражение `x or y`. В результате работы этого выражения мы видим, что на экране не `True`, как мы могли бы ожидать, а число 12."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"12\n"
]
}
],
"source": [
"x = 12\n",
"y = False\n",
"\n",
"print(x or y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что здесь происходит? Python начинает интерпретировать логическое выражение, видит, что `x` является истинным, и понимает, что ему не нужно выполнять оставшуюся часть логического выражения. Поэтому он y уже не будет проверять, `x` — это истина, оператор `or` стоит, значит нас устраивает то, что мы можем остановиться в этот момент и результатом выполнения выражения будет как раз значение `x`. На другом примере у нас есть переменная `x`, которая равна 12, и переменная `z`, которая равна строке `boom`. В результате работы логического выражения `x and z` мы получаем как результат работы логического выражения строку `boom`."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"boom\n"
]
}
],
"source": [
"x = 12\n",
"z = \"boom\"\n",
"\n",
"print(x and z)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Происходит все то же самое. Python выполняет логическое выражение, до тех пор, пока оно имеет смысл, и результатом является последнее значение.\n",
"\n",
"Посмотрим небольшой пример — задачу, определить високосный год или нет. Существует такое правило, которое позволяет понять, является ли год високосным или нет. Год является високосным, если он кратен 4, но при этом не кратен 100, либо кратен 400. Звучит достаточно запутанно, однако решение всего в три строчки и как раз с помощью логических выражений. Мы объявляем переменную, которая содержит в себе год и далее составляем сложное логическое выражение, включающее в себя несколько логических операторов. При этом задаем порядок выполнения операторов скобками, обратите внимание. И что стоит отметить — то, что это то, о чем я говорил. Если год не кратен четырем, то есть первое выражение не является истинным, то логическое выражение не будет выполняться дальше, Python в этот момент остановится. Если же оно истинное, то выполнение логического выражения продолжится. Ну и мы получаем ответ. В данном случае 2017 год не является високосным."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n"
]
}
],
"source": [
"year = 2017\n",
"\n",
"is_leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n",
"\n",
"print(is_leap)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что стоит отметить — это то, что эту задачку мы могли бы решить ещё короче, всего в две строчки, используя модуль стандартной библиотеки `calendar`. Как принято говорить, Python — это «батарейки включены», то есть это язык, который предоставляет очень много возможностей в своей стандартной библиотеке. Так что библиотеку языка ставят в пример многим другим языкам программирования. Соответственно, есть модуль «календарь», который мы можем импортировать, и внутри модуля календарь есть функция `isleap`, которая делает как раз то, что нам нужно. Мы, например, можем убедиться, что 1980-й год был високосным. Про импорты мы ещё с вами будем говорить в дальнейшем."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"from calendar import isleap\n",
"\n",
"print(isleap(1980))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На этой лекции мы поговорили про логический тип в Python, тип `bool`, рассмотрели логические операторы, а также посмотрели на составные логические выражения."
]
}
],
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,178 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Базовые типы: объект None #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Еще один важный объект, который стоит рассмотреть, это объект `None`.\n",
"\n",
"Объект `None` по большому счету является единственным значением специального типа `NoneType`, который используется для того, чтобы подчеркнуть отсутствие значения. Если вы знаете язык С, то вы можете рассматривать `None` как эквивалент нулевому указателю.\n",
"\n",
"В программах на Python'е вы будете часто видеть `None` как в чужом коде, так и будете использовать его в своем. Для того чтобы присвоить переменной `None`, мы можем написать просто `answer = None`. Ничего нового для вас в этом нет. Если мы посмотрим на тип `None`, то мы увидим, что это как раз класс `NoneType`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'NoneType'>\n"
]
}
],
"source": [
"answer = None\n",
"print(type(None))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Однако обратите внимание, что с классом `NoneType` вам работать не придется, по большому счету вы работаете только с единственным значением, которое представляет собой объект `None`. Важно отметить, что при преобразовании `None` к типу `bool` всегда получается `False`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(None)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Поэтому если переменная равна `None` и мы используем условный оператор, `if not answer` всегда будет выполняться, потому что `if not answer` будет всегда `True`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ответ не получен\n"
]
}
],
"source": [
"answer = None\n",
"\n",
"if not answer:\n",
" print(\"Ответ не получен\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте посмотрим на примере, когда `None` может потребоваться в коде. Предположим, у нас есть программа, в которой мы рассчитываем какой-то доход, доход от продаж чего-либо, неважно чего. Предположим, у нас доход, который лежит в переменной `income`, равен нулю. Тогда в коде мы могли бы написать `if not income` и напечатать на экран \"Ничего не заработали\". Если у нас доход ноль, то мы ничего не заработали."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ничего не заработали\n"
]
}
],
"source": [
"income = 0\n",
"\n",
"if not income:\n",
" print(\"Ничего не заработали\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Однако предположим, что нам нужно подчеркнуть такую ситуацию, что мы еще даже не начинали продавать. Конечно же, до начала продаж доход всегда будет равен нулю. В этот момент может пригодится `None`. Мы присваиваем переменной `income` значение `None`, и после этого в условиях сравнения мы можем проверять, что если `income` является объектом `None`, то тогда печатаем на экран что мы еще даже не начинали продавать. Иначе — `elif not income`, — иначе если у нас в переменной `income` ноль, то мы и печатаем доход ноль, мы ничего не заработали. В данном случае важно отметить, что мы проверяем равенство переменной `income` объекту `None` с помощью оператора `is`. Это идиоматичный способ проверки соответствия переменной объекту `None` в Python'е. Мы могли бы, конечно, написать `if income == None`, но это неидиоматично в Python, потому что в Python'е оператор `==` можно переопределить с помощью магических методов, и мы это увидим в дальнейшем, что такое магические методы у классов."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Еще не начинали продавать\n"
]
}
],
"source": [
"income = None\n",
"\n",
"if income is None:\n",
" print(\"Еще не начинали продавать\")\n",
"elif not income:\n",
" print(\"Ничего не заработали\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что еще можно сделать с `None`? `None` будет часто использоваться как значение именованного аргумента в функции, для того чтобы подчеркнуть, что аргумент функции является опциональным. Также значение `None` присваивается при инициализации классом всевозможным атрибутам, которые в дальнейшем могут быть перезаписаны каким-то полезным значением. Также `None` по умолчанию возвращается из функции, если вы самостоятельно явным образом ничего из функции не вернули с помощью конструкции `return`. Все эти слова про функции и про классы могут звучать для вас по-новому, но мы про все это расскажем в следующих лекциях нашего курса. Ну а пока вы познакомились с объектом `None`, который теперь вас не испугает, когда вы его увидите."
]
}
],
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,371 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Пример на управление потоком #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На этой лекции мы напишем небольшую программу как пример на конструкции управления потоком.\n",
"\n",
"Программа будет представлять собой простую игру, в которой пользователю нужно угадать загаданное число. Я уже написал такую программу. Давайте посмотрим на неё в действии. Запустим."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"^C\n"
]
}
],
"source": [
"! python main.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Программа предлагает ввести число. Когда мы вводим, на экран выводится подсказка о том, загаданное число больше либо меньше того, что мы ввели. Если мы число угадываем, то на экран печатается \"Совершенно верно\", и программа завершает свою работу. Давайте напишем аналогичную программу."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Писать код мы будем в среде разработки `PyCharm Community Edition`. Давайте запустим `PyCharm`. И пока он запускается, стоит отметить, что подробно процесс установки `PyCharm` системы мы описали в сопроводительных документах. Когда `PyCharm` загрузился, нам нужно нажать кнопочку `New Project (Новый Проект).`. Далее `PyCharm` предлагает заполнить два текстовых поля. В первом нужно указать путь на жёстком диске до проекта, который мы создаём. Давайте назовём папочку `game`. Второе текстовое поле — это путь до интерпретатора Python 3 в системе. В данном случае `PyCharm` автоматически определил правильный путь до интерпретатора, который установлен у меня, поэтому всё, что осталось нажать — кнопочку `Create` которая создаёт новый проект. Как только проект открылся, нам нужно открыть в нём Python'овский файл `main.py`. И откроется окно редактора, в котором мы уже можем писать код, удалив код по умолчанию."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте начнём. Первое, что мы сделаем, это создадим переменную `number`, которой присвоим число 42. И это то число, которое нужно отгадать."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"number = 42"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Далее мы воспользуемся бесконечным циклом `while True`, который будет выполняться до тех пор, пока мы из него явным образом не выйдем. Внутри этого цикла на каждой итерации нам нужно получать ввод пользователя из терминала. Мы можем это сделать с помощью встроенной функции `input`. Внутри функции `input` мы будем просить пользователя ввести число. Если пользователь ввёл пустую строку, то есть просто нажал Enter, либо напечатал слово `exit`, мы должны выйти из программы. То есть если строка пустая, `if not answer`, или строка равна строке `exit`, мы выходим. Чтобы выйти из бесконечного цикла, воспользуемся оператором `break`. При этом программа завершится."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: exit\n"
]
}
],
"source": [
"while True:\n",
" answer = input(\"Введите число: \")\n",
" \n",
" if not answer or answer == \"exit\":\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Далее, у нас есть строковый ввод пользователя. Давайте проверим на то, что этот ввод преобразовывается к целому числу. Для этого воспользуемся методом строки `isdigit`. Если ввод пользователя не преобразовывается к целому числу, то напечатаем на экран: \"Введите правильное число\". И воспользуемся оператором `continue` для того, чтобы перейти к новой итерации цикла `while`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: bla bla bla\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Введите правильное число!\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: exit\n"
]
}
],
"source": [
"while True:\n",
" answer = input(\"Введите число: \")\n",
" \n",
" if not answer or answer == \"exit\":\n",
" break\n",
" \n",
" if not answer.isdigit():\n",
" print(\"Введите правильное число!\")\n",
" continue"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Далее, мы знаем теперь, что ввод пользователя преобразовывается к целому числу. Давайте преобразуем."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 8\n",
"Введите число: exit\n"
]
}
],
"source": [
"while True:\n",
" answer = input(\"Введите число: \")\n",
" \n",
" if not answer or answer == \"exit\":\n",
" break\n",
" \n",
" if not answer.isdigit():\n",
" print(\"Введите правильное число!\")\n",
" continue\n",
" \n",
" user_answer = int(answer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Теперь, когда переменная `user_answer` содержит целое число, мы можем сравнивать его с загаданным числом. Если `user_answer` больше, чем загаданное число, то печатаем на экран фразу \"Загаданное число меньше\". Если `user_answer` меньше, чем загаданное число, тогда печатаем на экран фразу \"Загаданное число больше\". Иначе, блок `else` будет выполнен тогда, когда пользователь угадал число. Мы можем его поздравить, написав \"Совершенно верно!\", и выйти из программы с помощью оператора `break`. Вот, собственно, и всё. Мы написали программу аналогичную той, что я показывал в начале видео."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 10\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Загаданное число больше\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 50\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Загаданное число меньше\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 42\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Совершенно верно!\n"
]
}
],
"source": [
"while True:\n",
" answer = input(\"Введите число: \")\n",
" \n",
" if not answer or answer == \"exit\":\n",
" break\n",
" \n",
" if not answer.isdigit():\n",
" print(\"Введите правильное число!\")\n",
" continue\n",
" \n",
" user_answer = int(answer)\n",
" \n",
" if user_answer > number:\n",
" print(\"Загаданное число меньше\")\n",
" elif user_answer < number:\n",
" print(\"Загаданное число больше\")\n",
" else:\n",
" print(\"Совершенно верно!\")\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Однако вы можете сказать, что это игра неинтересная, потому что ответ заранее известен. Давайте это исправим. Мы воспользуемся модулем стандартной библиотеки `random` для того, чтобы генерировать на старте программы случайное число в промежутке от 0 до 100. Про импорты мы ещё будем говорить в дальнейшем, ну а пока просто воспользуемся для нашего примера. Внутри модуля `random` стандартной библиотеки есть функция `randint`, которая возвращает число в каком-то промежутке, целое. Теперь в нашу программу интересно играть даже нам самим."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"from random import randint\n",
"\n",
"number = randint(0, 101)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 40\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Загаданное число больше\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 60\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Загаданное число меньше\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Введите число: 59\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Совершенно верно!\n"
]
}
],
"source": [
"while True:\n",
" answer = input(\"Введите число: \")\n",
" \n",
" if not answer or answer == \"exit\":\n",
" break\n",
" \n",
" if not answer.isdigit():\n",
" print(\"Введите правильное число!\")\n",
" continue\n",
" \n",
" user_answer = int(answer)\n",
" \n",
" if user_answer > number:\n",
" print(\"Загаданное число меньше\")\n",
" elif user_answer < number:\n",
" print(\"Загаданное число больше\")\n",
" else:\n",
" print(\"Совершенно верно!\")\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Чтобы запустить код, мы нажимаем правой кнопкой на белой области редактора и выбираем пункт меню `Run`. `PyCharm` запускает встроенный терминал, и, в принципе, мы можем пользоваться нашей программой как раз из этого терминала. Однако здесь написано, как `PyCharm` запускает программу, которую мы только что написали. Давайте запустим её самостоятельно из терминала. Введите число. Давайте попробуем для начала ввести что-то неправильное. Введите правильное число. А теперь попробуем отгадать число.\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.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,270 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Тест на типы и конструкции #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 1. 10 / 2 что получится в результате деления?\n",
"\n",
"- [ ] Вещественное число 5.0\n",
"- [ ] Целое число 5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 2. True или False?\n",
"\n",
"```python\n",
"bool(0.0)\n",
"```\n",
"\n",
"- [ ] True\n",
"- [ ] False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 3. Чему равно name?\n",
"\n",
"```python\n",
"x = 0\n",
"y = 12\n",
"name = x or y\n",
"```\n",
"\n",
"- [ ] 0\n",
"- [ ] False\n",
"- [ ] True\n",
"- [ ] 12"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 4. Что выведет?\n",
"\n",
"```python\n",
"print(r\"//\\\\\")\n",
"```\n",
"\n",
"- [ ] `//\\\\`\n",
"- [ ] `//\\`\n",
"- [ ] `/\\`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 5. Выберите правильные варианты объявления строки в Python?\n",
"\n",
"- [ ] \"строка\"\n",
"- [ ] f\"строка\"\n",
"- [ ] r\"строка\"\n",
"- [ ] 'строка'\n",
"- [ ] \"\"\"строка\"\"\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 6. Отметьте правдивые факты про строки\n",
"\n",
"- [ ] строки в Python неизменяемые\n",
"- [ ] строки - это последовательность юникодных символов\n",
"- [ ] строки в Python - это последовательность чисел от 0 до 255"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 7. Что получится в результате выполнения?\n",
"\n",
"```python\n",
"\"a\" * 3\n",
"```\n",
"\n",
"- [ ] Упадет исключение\n",
"- [ ] Строка \"aaa\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 8. Что получится в результате выполнения среза?\n",
"\n",
"```python\n",
"\"привет\"[::-1]\n",
"```\n",
"\n",
"- [ ] \"т\"\n",
"- [ ] \"тевирп\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 9. Срез строки\n",
"\n",
"```python\n",
"\"привет\"[:]\n",
"```\n",
"\n",
"- [ ] вернет пустую строку\n",
"- [ ] приведет к исключению SyntaxError\n",
"- [ ] вернет копию строки \"привет\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 10. `bool(\"\")`\n",
"\n",
"- [ ] False\n",
"- [ ] True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 11. Истинные выражения про байтовые строки\n",
"\n",
"- [ ] Это последовательность чисел от 0 до 255\n",
"- [ ] Это последовательность юникодных символов"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 12. Какой метод превратит строку в байтовую строку?\n",
"\n",
"- [ ] encode()\n",
"- [ ] decode()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 13. Какие из записей не приведут к исключению?\n",
"\n",
"- [ ] b\"test\"\n",
"- [ ] b\"тест\"\n",
"- [ ] b\"\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 14. Что выведет программа?\n",
"\n",
"```python\n",
"x = \"Москва\"\n",
"if \"ква\" not in x:\n",
" print(\"1\")\n",
"elif \"ва\" not in x:\n",
" print(\"2\")\n",
"else:\n",
" print(\"3\")\n",
"```\n",
"\n",
"- [ ] 3\n",
"- [ ] 1\n",
"- [ ] 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 15. Что выведет программа?\n",
"\n",
"```python\n",
"n = 0\n",
"while n < 3:\n",
" if n > 0:\n",
" continue\n",
" else:\n",
" break\n",
" n += 1\n",
"\n",
"print(n)\n",
"```\n",
"\n",
"- [ ] 2\n",
"- [ ] 0\n",
"- [ ] Это бесконечный цикл\n",
"- [ ] 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### 16. Что выведет программа?\n",
"\n",
"```python\n",
"s = \"\"\n",
"\n",
"for i in range(2, 10, 2):\n",
" s += str(i)\n",
"\n",
"print(s)\n",
"```\n",
"\n",
"- [ ] 2468\n",
"- [ ] 246810\n",
"- [ ] 23456789"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,262 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Тест на типы и конструкции #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. 10 / 2 что получится в результате деления?\n",
"\n",
"- [x] Вещественное число 5.0\n",
"- [ ] Целое число 5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. True или False?\n",
"\n",
"```python\n",
"bool(0.0)\n",
"```\n",
"\n",
"- [ ] True\n",
"- [x] False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. Чему равно name?\n",
"\n",
"```python\n",
"x = 0\n",
"y = 12\n",
"name = x or y\n",
"```\n",
"\n",
"- [ ] 0\n",
"- [ ] False\n",
"- [ ] True\n",
"- [x] 12"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4. Что выведет?\n",
"\n",
"```python\n",
"print(r\"//\\\\\")\n",
"```\n",
"\n",
"- [x] `//\\\\`\n",
"- [ ] `//\\`\n",
"- [ ] `/\\`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"5. Выберите правильные варианты объявления строки в Python?\n",
"\n",
"- [x] \"строка\"\n",
"- [x] f\"строка\"\n",
"- [x] r\"строка\"\n",
"- [x] 'строка'\n",
"- [x] \"\"\"строка\"\"\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"6. Отметьте правдивые факты про строки\n",
"\n",
"- [x] строки в Python неизменяемые\n",
"- [x] строки - это последовательность юникодных символов\n",
"- [ ] строки в Python - это последовательность чисел от 0 до 255"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"7. Что получится в результате выполнения?\n",
"\n",
"```python\n",
"\"a\" * 3\n",
"```\n",
"\n",
"- [ ] Упадет исключение\n",
"- [x] Строка \"aaa\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"8. Что получится в результате выполнения среза?\n",
"\n",
"```python\n",
"\"привет\"[::-1]\n",
"```\n",
"\n",
"- [ ] \"т\"\n",
"- [x] \"тевирп\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"9. Срез строки\n",
"\n",
"```python\n",
"\"привет\"[:]\n",
"```\n",
"\n",
"- [ ] вернет пустую строку\n",
"- [ ] приведет к исключению SyntaxError\n",
"- [x] вернет копию строки \"привет\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"10. `bool(\"\")`\n",
"\n",
"- [x] False\n",
"- [ ] True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"11. Истинные выражения про байтовые строки\n",
"\n",
"- [x] Это последовательность чисел от 0 до 255\n",
"- [ ] Это последовательность юникодных символов"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"12. Какой метод превратит строку в байтовую строку?\n",
"\n",
"- [x] encode()\n",
"- [ ] decode()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"13. Какие из записей не приведут к исключению?\n",
"\n",
"- [x] b\"test\"\n",
"- [ ] b\"тест\"\n",
"- [x] b\"\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"14. Что выведет программа?\n",
"\n",
"```python\n",
"x = \"Москва\"\n",
"if \"ква\" not in x:\n",
" print(\"1\")\n",
"elif \"ва\" not in x:\n",
" print(\"2\")\n",
"else:\n",
" print(\"3\")\n",
"```\n",
"\n",
"- [x] 3\n",
"- [ ] 1\n",
"- [ ] 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"15. Что выведет программа?\n",
"\n",
"```python\n",
"n = 0\n",
"while n < 3:\n",
" if n > 0:\n",
" continue\n",
" else:\n",
" break\n",
" n += 1\n",
"\n",
"print(n)\n",
"```\n",
"\n",
"- [ ] 2\n",
"- [x] 0\n",
"- [ ] Это бесконечный цикл\n",
"- [ ] 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"16. Что выведет программа?\n",
"\n",
"```python\n",
"s = \"\"\n",
"\n",
"for i in range(2, 10, 2):\n",
" s += str(i)\n",
"\n",
"print(s)\n",
"```\n",
"\n",
"- [x] 2468\n",
"- [ ] 246810\n",
"- [ ] 23456789"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,821 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Базовые типы: численные типы #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На этой лекции мы начинаем знакомство с основными базовыми типами, которые есть в Python.\n",
"\n",
"## Целые числа (int) ##\n",
"\n",
"Первым в нашем списке будет идти тип int, это целые числа. Чтобы объявить целое число достаточно писать `num = 13`, тем самым мы связываем имя переменной `num` с малочисленным объектом со значением 13, точно также можно присвоить переменной 0 либо отрицательно целое число."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"num = 13\n",
"num = 0\n",
"num = -1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Начиная с Python 3.6 появилась возможность разделять порядки символом нижнего подчеркивания в длинных числах, тем самым мы можем улучшить читаемость этих чисел в исходном коде."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"kpop = 10_000_000"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Раз уж мы заговорили о длинных числах, стоит отметить, что Python поддерживает длинную арифметику при работе с целыми числами, то есть поддерживаются числа неограниченной длины. Прежде чем продолжить, давайте разберем еще одну встроенную функцию, которая есть в языке, это функция `Type`. Функция `type` доступна в глобальном пространстве имен и она позволяет в рантайме, в процессе работы программы посмотреть на тип объекта, если мы объявим переменную `num` и свяжем ее с малочисленным объектом 13, а затем посмотрим какой у неё тип, то мы увидим, что это класс `int`, то это действительно тип `integer` и в дальнейшем мы видим, что встроенные типы Python они реализованы как классы, об этом я буду говорить в третьем блоке нашего курса. Ну а сейчас просто важно запомнить, что функция `Type` позволяет увидеть тип объекта."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"int"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(kpop)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Вещественные числа (float) ##\n",
"\n",
"Также Python умеет работать с вещественными числами, то есть с числами с плавающей точкой, это тип `float`. Чтобы объявить вещественное число, можно написать `pi = 13.4`, то есть точка используется для того чтобы разделить целую и дробную часть вещественного числа, то же самое для 0, и то же самое для отрицательного вещественного числа. По аналогии с типом `int` мы можем использовать символ нижнего подчёркивания, для того, чтобы разделять порядки в длинном вещественном числе. "
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"pi = 13.4\n",
"e = 2.718_281_828_459_045"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Также Python поддерживает экспоненциальную нотацию записи вещественного числа, а, как мы можем видеть `1.5Е2`, на самом деле это полтора, умножить на 10 в степени 2, то есть 150."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"150.0"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1.5E2"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"150.0"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1.5 * 10 ** 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Конвертация типов ##\n",
"\n",
"Теперь поговорим о ещё одном важном аспекте - о конвертации типов. Посмотрим на пример, предположим, у нас есть переменная `num`, которая связана с вещественным объектом со значением 150,2, и мы видим что это тип `float`. В любой момент мы можем воспользоваться встроенным классом `int`, чтобы переменную, которая связана с типом `float`, перевести в объект типа `integer` и затем мы можем преобразовать `integer` обратно к вещественному типу, используя класс `float`, тем самым в процессе работы программы мы можем из одного типа конвертировать переменные в другой тип."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"150.2"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num = 150.2\n",
"num"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"150"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num = int(num)\n",
"num"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"150.0"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"float(num)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Обратите внимание, что в процессе наших конвертаций на примере, мы потеряли дробную часть вещественного числа.\n",
"\n",
"## Комплексные числа (complex) ##\n",
"\n",
"Также Python умеет работать с комплексными числами, это тип комплекс, для того, чтобы определить комплексное число нужно для мнимой части использовать символ `J`, посмотрите на пример, мы определили комплексное число `14 + 1J` и можем убедиться, что его тип действительно комплекс, используя встроенную функцию `Type`."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(14+1j)"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num = 14 + 1J\n",
"num"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"complex"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(num)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"А также, чтобы достучаться до реальной и мнимой части комплексного числа, мы можем воспользоваться атрибутами `real` и `imag`."
]
},
{
"cell_type": "code",
"execution_count": 109,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"14.0"
]
},
"execution_count": 109,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num = 14 + 1J\n",
"num.real"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.0"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num.imag"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что стоит отметить? Что в Pyton-e есть еще два модуля, которые позволяют работать с числами, эти модули очень полезны, мы не будем рассматривать их подробно, но знать о них нужно. В первую очередь, это модуль `deccimal`, который позволяет работать с вещественными числами с фиксированной точностью, а второй модуль это модуль `fractions`, который позволяет работать с рациональными числами, проще говоря с дробями.\n",
"\n",
"## Основные операции с числами ##\n",
"\n",
"Теперь посмотрим на основные операции с числами, как я уже говорил, Python хорош в качестве калькулятора и, конечно же, он умеет работать с числами очень хорошо. Для того, чтобы сложить два числа, мы используем просто математический оператор плюс. 1+1=2, сумма двух целых чисел дает нам целое число, если мы складываем целое число и вещественное число, то результат у нас получается вещественный. То же самое с вычитанием, если мы вычитаем из целого целое, получается целое число, если мы вычитаем из вещественного целое, то результат вещественный. Для того, чтобы разделить числа, мы можем воспользоваться символом прямого слэша, и обратите внимание, что результат деления всегда вещественный. Делить на 0 нельзя. Если мы попытаемся разделить на 0, то мы получим исключение `ZeroDivisionError`. Про исключения, как я уже говорил, мы с вами будем говорить в дальнейшем, научим вас их отлавливать и обрабатывать. Для того чтобы умножить числа, мы можем использовать звездочку, а для того, чтобы возвести число в степень, мы можем использовать две звездочки, на примере мы возводим число 2 в 4 степень. Чтобы произвести целочисленное деление, мы используем два прямых слэша. Чтобы получить остаток от деления, используем процент. Важно подчеркнуть, что Python использует порядок операций в выражениях с числами такой, какой используется в правилах математики и,в если мы посмотрим на пример, то выражение 3 плюс 10 умножить на 3, сначала выполнится умножение, а потом сложение. Если же нам нужно подчеркнуть, что нужно сначала выполнить сложение, а потом результат сложения умножить на 3, то мы можем воспользоваться круглыми скобочками для этого."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сложение:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 + 1"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.5"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 + 1.5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Вычитание:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"6 - 2"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4.0"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"6 - 2.0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Деление:"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3.0"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"6 / 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Делить на 0 нельзя:"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"ename": "ZeroDivisionError",
"evalue": "division by zero",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mZeroDivisionError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-36-35ab704efce5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;36m6\u001b[0m \u001b[1;33m/\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
]
}
],
"source": [
"6 / 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Умножение:"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 * 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Возведение числа в степень:"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"16"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 ** 4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Целочисленное деление:"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"10 // 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Остаток от деления:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"10 % 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Порядок операций в выражениях с числами:"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"33"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3 + 10 * 3"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"39"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(3 + 10) * 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Побитовые операции ##\n",
"\n",
"Также Python поддерживает побитовые операции для чисел, среди них побитово или, побитово исключающие или, и так далее."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Побитовое или: 7\n",
"Побитовое исключающее или: 7\n",
"Побитовое и: 0\n",
"Битовый сдвиг влево: 32\n",
"Битовый сдвиг вправо: 2\n",
"Инверсия битов: -5\n"
]
}
],
"source": [
"x = 4\n",
"y = 3\n",
"\n",
"print(\"Побитовое или:\", x | y)\n",
"print(\"Побитовое исключающее или:\", x ^ y)\n",
"print(\"Побитовое и:\", x & y)\n",
"print(\"Битовый сдвиг влево:\", x << 3)\n",
"print(\"Битовый сдвиг вправо:\", x >> 1)\n",
"print(\"Инверсия битов:\", ~x)"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"## Задачка ##\n",
"\n",
"Теперь посмотрим на задачку. Задача найти расстояние между двумя точками в декартовых координатах, эта задача известна всем нам из школьного курса математики, и для того, чтобы ее решить по большому счёту, нам нужно найти гипотенузу прямоугольного треугольника. Давайте посмотрим на решение на Python.\n",
"\n",
"![График](graph.svg \"График\")"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.0\n"
]
}
],
"source": [
"x1, y1 = 0, 0\n",
"x2 = 3\n",
"y2 = 4\n",
"\n",
"distance = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** .5\n",
"print(distance)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Вначале мы объявляем переменные, это координаты точек. Обратите внимание на самую верхнюю запись `x1, y1 = 0, 0`. Это синтаксис, который поддерживается Python'ом, он позволяет через запятую перечислить названия переменных, а далее, через запятую перечислить те значения, которые этим переменным нужно присвоить. Это очень удобно. Для того, чтобы найти расстояние, мы воспользуемся формулой, корень из суммы квадратов катетов, и здесь мы видим, как мы вычисляем расстояние. Вместо того, чтобы брать корень, мы возводим выражение в скобках в степени 0,5, что эквивалентно взятию корня. И далее печатаем на экран ответ, мы видим, что ответ получился правильный."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"А что ещё хочется отметить про синтаксис Python, это мы видели в предыдущем примере то, что можно на одной строчке присвоить значения нескольким переменным. Однако, тот же самый трюк позволяет, например, поменять значения у 2 переменных без использования временной переменной, то есть мы определяем две переменные, дальше пишем `a, b = b, a`, и значение переменных меняется местами. Также очень удобно."
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2 1\n"
]
}
],
"source": [
"a = 1\n",
"b = 2\n",
"a, b = b, a\n",
"\n",
"print(a, b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что еще можно сделать было бы? Мы могли бы записать вместо `x, y = 0, 0`, `x = y = 0`, тем самым мы бы связали переменную `x` и переменную `y` с объектом, с зачисленным объектом 0. Однако нужно быть внимательным. первый объект это изменяемый объект, а второй - это неизменяемые, изменяемые объекты это объекты, которые после создания могут менять свое значение, а неизменяемые, соответственно, это объекты, которые после создания свое значение не меняют. Примитивные основные типы, которые мы разбираем на этой неделе, по большому счету, не изменяемы, поэтому для нашей задачи запись `x = y = 0` была бы оправдана. В данном случае, если бы мы поменяли только `х`, например, добавили бы к `х` впоследствии единичку, у нас `х` стал бы равен 1, а `у` остался бы нулем."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 0\n"
]
}
],
"source": [
"a = b = 0\n",
"a += 1\n",
"\n",
"print(a, b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Но если бы мы написали `х = у = пустой список`, а пустой список - это объект-контейнер, который мы будем разбирать на дальнейших неделях, он позволяет хранить последовательность объектов в себе, а этот объект уже изменяемый, поэтому, когда мы присваиваем `х = у = пустой список`, а затем в список `х` добавляем два элемента, то, если мы выведем на экран `х` и `у`, то мы увидим, что изменились значения обеих переменных. С этим нужно быть внимательным, это нюанс, который необходимо понимать и необходимо понимать, что в Python-е есть неизменяемые объекты и изменяемые объекты."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2] [1, 2]\n"
]
}
],
"source": [
"a = b = list()\n",
"\n",
"a.append(1)\n",
"a.append(2)\n",
"\n",
"print(a, b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На этой лекции мы с вами поговорили об основных численных типах, которые есть в языке, рассмотрели математические операции с ними, узнали о конвертации типов, а также затронули тему изменяемых и не изменяемых объектов в Python. В дальнейших лекциях, мы продолжим знакомиться с основными типами в языке."
]
}
],
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,13 @@
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
c = int(sys.argv[3])
d = b ** 2 - 4 * a * c
if d > 0:
print((-b + (b ** 2 - 4 * a * c) ** .5) / (2 * a))
print((-b - (b ** 2 - 4 * a * c) ** .5) / (2 * a))
elif not d:
print(-b / (2 * a))

@ -0,0 +1,7 @@
import requests
def get_location_info():
return requests.get("http://ip-api.com/json/").json()
if __name__ == "__main__":
print(get_location_info())

@ -0,0 +1,4 @@
from mypackage.utils import *
if __name__ == "__main__":
print(multiply(2, 3))

@ -0,0 +1,10 @@
import sys
digit_string = sys.argv[1]
summa = 0
for i in digit_string:
summa += int(i)
print(summa)

@ -0,0 +1,6 @@
import sys
num_steps = int(sys.argv[1])
for i in range(num_steps):
print((num_steps - i + 1) * " " + "#" * (i + 1))

@ -0,0 +1,219 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "08735fdb",
"metadata": {},
"source": [
"# Байткод #"
]
},
{
"cell_type": "markdown",
"id": "764f3e24",
"metadata": {},
"source": [
"А сейчас давайте поговорим про байт-код.\n",
"\n",
"Помните, когда мы рассматривали организацию кода на Python, мы импортировали функции из модулей и пакетов, и Python автоматически создавал файлики с расширением.pyc. Я сказал, что это файлики, в которых содержится байт-код, то есть скомпилированное представление вашего кода в форме, удобной Python для интерпретирования.\n",
"\n",
"Давайте сейчас подробнее посмотрим, что это значит. Для этого мы создадим модуль, назовем его `mymodule`.\n",
"\n",
"Мы с вами это уже умеем делать, и внутри определим функцию `multiply`, точно такую же, которую мы с вами уже писали. Она принимает два аргумента и возвращает их произведение.\n",
"\n",
"```python\n",
"def multiply(a, b):\n",
" return a * b\n",
"```\n",
"\n",
"Функция определена, теперь мы можем ее импортировать. Давайте воспользуемся конструкцией `from`. `from mymodule import multiply`. `multiply` — это функция."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "03f059a0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n"
]
}
],
"source": [
"from mymodule import multiply"
]
},
{
"cell_type": "markdown",
"id": "0c3e8648",
"metadata": {},
"source": [
"Как я уже сказал, практически всё в Python — это объект, не исключение и функция. Если мы посмотрим с помощью функции `dir` методы, которые содержит функция, мы увидим, что их много, в том числе у функции есть атрибут `__code__` с двумя подчеркиваниями, это `code object`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1374e3bc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']\n"
]
}
],
"source": [
"print(dir(multiply))"
]
},
{
"cell_type": "markdown",
"id": "43989f9d",
"metadata": {},
"source": [
"По сути, это объект, который оборачивает тот код, который вы пишете. У этого объекта есть метод `co_code`, который содержит байт-код. Это байт-код, который Python сгенерировал для этой функции."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "a3373ab6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"b'|\\x00|\\x01\\x14\\x00S\\x00'\n"
]
}
],
"source": [
"print(multiply.__code__.co_code)"
]
},
{
"cell_type": "markdown",
"id": "eb706b6d",
"metadata": {},
"source": [
"Что это значит? Это значит, что наша функция была преобразована в байт-код, который представляет собой набор инструкций. В данном случае это байтовая строка, которая содержит инструкцию одну за другой, которые подаются на вход интерпретатору Python.\n",
"\n",
"Интерпретатор Python, по большому счету, это большая конструкция `switch` кода на C, которая, в зависимости от входящей инструкции байт-кода, предпринимает то или иное действие."
]
},
{
"cell_type": "markdown",
"id": "33872151",
"metadata": {},
"source": [
"Как мы видим, байт-код нечеловекочитаемый Мы можем привести его в человекочитаемый вид, используя модуль `dis` стандартной библиотеки Python.\n",
"\n",
"Давайте это сделаем. Заимпортируем модуль `dis` и с помощью метода `dis` модуля `dis` дисассемблируем нашу функцию в человекочитаемый вид. Что мы видим?"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "e2ccab2f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 LOAD_FAST 0 (0)\n",
" 2 LOAD_FAST 1 (1)\n",
" 4 BINARY_MULTIPLY\n",
" 6 RETURN_VALUE\n"
]
}
],
"source": [
"from dis import dis\n",
"\n",
"dis(multiply.__code__.co_code)"
]
},
{
"cell_type": "markdown",
"id": "42c24b47",
"metadata": {},
"source": [
"Мы видим, что наша функция на самом деле преобразовывается в байт-код, который состоит из четырех инструкций. Каждая из этих инструкций делает какое-то действие. Давайте посмотрим на каждую инструкцию подробно.\n",
"\n",
"Первая — это `LOAD_FAST`, это инструкция, которая загружает содержимое переменной `a` в память и помещает его на стек. Стек — это структура данных, последним пришёл - первым вышел (LIFO), которая определена на уровне C и используется Python для того, чтобы манипулировать ходом выполнения программы.\n",
"\n",
"Как только переменная `a` помещена на стек, мы переходим ко второй инструкции, которая также `LOAD_FAST`, она делает то же самое со значением переменной `b`, помещает значение переменной `b` на стек. Далее.\n",
"\n",
"Следующая инструкция — это `BINARY_MULTIPLY`. `BINARY_MULTIPLY` говорит интерпретатору Python, что нужно взять два самых верхних значения на стеке и произвести их умножение. Заметьте, что мы могли передать в функцию значение любого типа. Могли передать целые числа, могли вещественные. Могли бы передать даже строки, например. Но в результате преобразования нашей функции в байт-код байт-код получился тем же самым. Типизация начинает по большому счету работать тогда, когда Python выполняет ту или иную инструкцию.\n",
"\n",
"В данном случае, когда выполняется инструкция `BINARY_MULTIPLY`, Python посмотрит на тип объектов, и, например, если это два числа, произведет умножение двух чисел. Результат умножения он поместит на вершину стека.\n",
"\n",
"И последняя инструкция — это `RETURN_VALUE`. `RETURN_VALUE` берет вершину стека, это результат умножения наших значений `a` и `b`, и возвращает его. По большому счету, это то, как работает Python, как устроен байт-код, и любой код на Python, который вы будете писать, будет превращаться, компилироваться в байт-код. Этот байт-код будет выглядеть посложнее, чем мы с вами видели, потому что функции, которые вы будете писать, могут быть сложнее, содержать всевозможные циклы, условия и так далее. И будут использоваться соответствующие инструкции.\n",
"\n",
"Все эти инструкции задокументированы, их можно посмотреть как в исходном коде Python, так и в документации. Ну, и можно посмотреть на них сейчас, есть модуль в стандартной библиотеке `opcode`, который мы можем заимпортировать и напечатать, например, на экран `opmap`."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b4d07e58",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'POP_TOP': 1, 'ROT_TWO': 2, 'ROT_THREE': 3, 'DUP_TOP': 4, 'DUP_TOP_TWO': 5, 'ROT_FOUR': 6, 'NOP': 9, 'UNARY_POSITIVE': 10, 'UNARY_NEGATIVE': 11, 'UNARY_NOT': 12, 'UNARY_INVERT': 15, 'BINARY_MATRIX_MULTIPLY': 16, 'INPLACE_MATRIX_MULTIPLY': 17, 'BINARY_POWER': 19, 'BINARY_MULTIPLY': 20, 'BINARY_MODULO': 22, 'BINARY_ADD': 23, 'BINARY_SUBTRACT': 24, 'BINARY_SUBSCR': 25, 'BINARY_FLOOR_DIVIDE': 26, 'BINARY_TRUE_DIVIDE': 27, 'INPLACE_FLOOR_DIVIDE': 28, 'INPLACE_TRUE_DIVIDE': 29, 'GET_AITER': 50, 'GET_ANEXT': 51, 'BEFORE_ASYNC_WITH': 52, 'BEGIN_FINALLY': 53, 'END_ASYNC_FOR': 54, 'INPLACE_ADD': 55, 'INPLACE_SUBTRACT': 56, 'INPLACE_MULTIPLY': 57, 'INPLACE_MODULO': 59, 'STORE_SUBSCR': 60, 'DELETE_SUBSCR': 61, 'BINARY_LSHIFT': 62, 'BINARY_RSHIFT': 63, 'BINARY_AND': 64, 'BINARY_XOR': 65, 'BINARY_OR': 66, 'INPLACE_POWER': 67, 'GET_ITER': 68, 'GET_YIELD_FROM_ITER': 69, 'PRINT_EXPR': 70, 'LOAD_BUILD_CLASS': 71, 'YIELD_FROM': 72, 'GET_AWAITABLE': 73, 'INPLACE_LSHIFT': 75, 'INPLACE_RSHIFT': 76, 'INPLACE_AND': 77, 'INPLACE_XOR': 78, 'INPLACE_OR': 79, 'WITH_CLEANUP_START': 81, 'WITH_CLEANUP_FINISH': 82, 'RETURN_VALUE': 83, 'IMPORT_STAR': 84, 'SETUP_ANNOTATIONS': 85, 'YIELD_VALUE': 86, 'POP_BLOCK': 87, 'END_FINALLY': 88, 'POP_EXCEPT': 89, 'STORE_NAME': 90, 'DELETE_NAME': 91, 'UNPACK_SEQUENCE': 92, 'FOR_ITER': 93, 'UNPACK_EX': 94, 'STORE_ATTR': 95, 'DELETE_ATTR': 96, 'STORE_GLOBAL': 97, 'DELETE_GLOBAL': 98, 'LOAD_CONST': 100, 'LOAD_NAME': 101, 'BUILD_TUPLE': 102, 'BUILD_LIST': 103, 'BUILD_SET': 104, 'BUILD_MAP': 105, 'LOAD_ATTR': 106, 'COMPARE_OP': 107, 'IMPORT_NAME': 108, 'IMPORT_FROM': 109, 'JUMP_FORWARD': 110, 'JUMP_IF_FALSE_OR_POP': 111, 'JUMP_IF_TRUE_OR_POP': 112, 'JUMP_ABSOLUTE': 113, 'POP_JUMP_IF_FALSE': 114, 'POP_JUMP_IF_TRUE': 115, 'LOAD_GLOBAL': 116, 'SETUP_FINALLY': 122, 'LOAD_FAST': 124, 'STORE_FAST': 125, 'DELETE_FAST': 126, 'RAISE_VARARGS': 130, 'CALL_FUNCTION': 131, 'MAKE_FUNCTION': 132, 'BUILD_SLICE': 133, 'LOAD_CLOSURE': 135, 'LOAD_DEREF': 136, 'STORE_DEREF': 137, 'DELETE_DEREF': 138, 'CALL_FUNCTION_KW': 141, 'CALL_FUNCTION_EX': 142, 'SETUP_WITH': 143, 'LIST_APPEND': 145, 'SET_ADD': 146, 'MAP_ADD': 147, 'LOAD_CLASSDEREF': 148, 'EXTENDED_ARG': 144, 'BUILD_LIST_UNPACK': 149, 'BUILD_MAP_UNPACK': 150, 'BUILD_MAP_UNPACK_WITH_CALL': 151, 'BUILD_TUPLE_UNPACK': 152, 'BUILD_SET_UNPACK': 153, 'SETUP_ASYNC_WITH': 154, 'FORMAT_VALUE': 155, 'BUILD_CONST_KEY_MAP': 156, 'BUILD_STRING': 157, 'BUILD_TUPLE_UNPACK_WITH_CALL': 158, 'LOAD_METHOD': 160, 'CALL_METHOD': 161, 'CALL_FINALLY': 162, 'POP_FINALLY': 163}\n"
]
}
],
"source": [
"from opcode import opmap\n",
"\n",
"print(opmap)"
]
},
{
"cell_type": "markdown",
"id": "a86677b4",
"metadata": {},
"source": [
"Мы видим, что на самом деле инструкций существует очень много. И, так или иначе, они в определенный момент будут задействованы. \n",
"\n",
"Что хотелось бы отметить? Знание о том, что ваш код компилируется в байт-код, может оказаться в определенный момент очень полезным. И, как минимум, вы не будете бояться того, что Python автоматически создает скомпилированные байт-код файлики в ваших директориях.\n",
"\n",
"Мы посмотрели на то, как Python преобразовывает код в байт-код, и теперь вы знаете, как это устроено."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,61 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "83ee0fe9",
"metadata": {},
"source": [
"# Виртуальное окружение на Windows #\n",
"\n",
"Работа с виртуальным окружением на **Windows** немного отличается от того, что мы видели в **Unix**-подобных систем. Чтобы создать виртуальное окружение на **Windows** запустите в терминале (мы рассказывали как открыть терминал в документе про установку **Python 3**):\n",
"\n",
"```\n",
"python3 -m venv c:\\path\\to\\myenv\n",
"```\n",
"\n",
"Где вместо `c:\\path\\to\\myenv` укажите путь до папки с виртуальным окружением, которую вы хотите создать.\n",
"\n",
"После того как скрипт отработает, вы можете активировать виртуальное окружение с помощью:\n",
"\n",
"```\n",
"c:\\path\\to\\myenv\\Scripts\\activate.bat\n",
"```\n",
"\n",
"Чтобы деактивировать виртуальное окружение:\n",
"\n",
"```\n",
"c:\\path\\to\\myenv\\Scripts\\deactivate.bat\n",
"```\n",
"\n",
"После активации виртуального окружения вы можете устанавливать в него дополнительные сторонние библиотеки точно так же, как мы показывали в видео, например:\n",
"\n",
"```\n",
"pip install requests\n",
"```\n",
"\n",
"Обратите внимание, что в то время как на **Unix** системах интерпретатор **python** находится в директории `bin/` внутри виртуального окружения на **Windows** интерпретатор будет находиться внутри директории `Scripts/` созданного виртуального окружения."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,334 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9ccc4a00",
"metadata": {},
"source": [
"# Виртуальное окружение #"
]
},
{
"cell_type": "markdown",
"id": "cdd20ab8",
"metadata": {},
"source": [
"На этой лекции мы с вами посмотрим, как работать со сторонними библиотеками с ресурсов `pypi.org`, мы установим нашу систему клиент-сервер серверное приложение `Jupiter` ноутбук, которые я активно применяю для создания слайдов в наших уроках. Однако это не единственное применение `Jupiter` Ноутбук, это очень распространённое приложение, которое применяется повсеместно разработчиками на Python, а также учеными для презентации своей работы, для того чтобы показывать сниппеты кода на Python, алгоритмы, результаты проведенной работы и показывать это интерактивно."
]
},
{
"cell_type": "markdown",
"id": "50ad6169",
"metadata": {},
"source": [
"Для того чтобы начать мы должны поговорить о том как поставить сторонний пакет на нашу операционную систему, для этого в Python существует замечательная утилита, которая называется `pip`. наберём `pip install` и название сторонних библиотек, которые мы хотим поставить, в данном случае написал `requests`, это замечательная библиотека для работы с `http` запросами. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "4d11a3c7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: requests in c:\\programdata\\anaconda3\\lib\\site-packages (2.25.1)\n",
"Requirement already satisfied: idna<3,>=2.5 in c:\\programdata\\anaconda3\\lib\\site-packages (from requests) (2.10)\n",
"Requirement already satisfied: chardet<5,>=3.0.2 in c:\\programdata\\anaconda3\\lib\\site-packages (from requests) (4.0.0)\n",
"Requirement already satisfied: certifi>=2017.4.17 in c:\\programdata\\anaconda3\\lib\\site-packages (from requests) (2020.12.5)\n",
"Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\\programdata\\anaconda3\\lib\\site-packages (from requests) (1.26.4)\n"
]
}
],
"source": [
"! pip install requests"
]
},
{
"cell_type": "markdown",
"id": "5c66f0b0",
"metadata": {},
"source": [
"`pip install request` ставит request в нашу систему, мы сейчас видим что библиотека `requests` уже установлена в глобальный директорию Python, это не очень удобно, мы не хотим трогать эту установленную версию библиотеки, поэтому следующим шагом нам нужно будет создать виртуальное окружение."
]
},
{
"cell_type": "markdown",
"id": "fc9717e7",
"metadata": {},
"source": [
"Виртуальное окружение в Python это окружение, которое позволяет изолировать зависимости для определенного проекта. Например, предположим вы разрабатываете на машине два проекта, причем в этих проектах используется одна и та же библиотека, но разных версий. В одном проекте чуть более старая версия этой библиотеки, в другом чуть более новая. Виртуальное окружение позволяет вам изолировать эти две зависимости друг от друга, так что вы можете работать над двумя проектами параллельно на одном компьютере. Это очень удобно. Тем самым, обновляя библиотеку в одном проекте, вы не сломаете код другого проекта."
]
},
{
"cell_type": "markdown",
"id": "57a1610b",
"metadata": {},
"source": [
"Чтобы создать виртуальное окружение мы воспользуемся модулем, который идет в поставке Рython3 из коробки он называется `venv`."
]
},
{
"cell_type": "markdown",
"id": "393b562d",
"metadata": {},
"source": [
"Чтобы создать виртуальное окружение мы пишем `python3 -m venv` и дальше название директории, в которой будет создано наше виртуальное окружение."
]
},
{
"cell_type": "markdown",
"id": "cde9b0a1",
"metadata": {},
"source": [
"Однако перед этим давайте опять создадим директорию, в которой мы будем работать и перейдём в нее. А вот теперь мы можем применить ту команду, о которой я говорил `python3 -m venv .env` запустить ее."
]
},
{
"cell_type": "markdown",
"id": "6e3d4421",
"metadata": {},
"source": [
"В этот момент создается виртуальное окружение и если мы посмотрим на структуру директорий, мы видим что создалось папочка `env`, да, как мы ее и назвали. Давайте посмотрим что внутри этой папочки, ограничимся уровнем вложенности три и мы видим что внутри папочки `env` создалось несколько директорий, одна из них `bin`, другая `include`, а также `lib`.\n",
"\n",
"```shell\n",
"tree -L 3\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "52a2b541",
"metadata": {},
"outputs": [],
"source": [
"! python -m venv .env"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "dc02a475",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"‘вагЄвга  Ї Ї®Є\n",
"‘ҐаЁ©­л© ­®¬Ґа ⮬ : 044F0438 581B:1E7B\n",
"C:\\USERS\\MIKHAYLOVAF\\PYTHON\\1. ‚‚…„…Ќ€… PYTHON\\4. ЋђѓЂЌ€‡Ђ–€џ ЉЋ„Ђ € ЋЉђ“†…Ќ€…\\.ENV\n",
"ГДДДInclude\n",
"ГДДДLib\n",
"і АДДДsite-packages\n",
"і ГДДДpip\n",
"і і ГДДД_internal\n",
"і і і ГДДДcli\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДcommands\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДdistributions\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДindex\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДmodels\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДnetwork\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДoperations\n",
"і і і і ГДДДbuild\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДinstall\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДreq\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДresolution\n",
"і і і і ГДДДlegacy\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДresolvelib\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДutils\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДvcs\n",
"і і і і АДДД__pycache__\n",
"і і і АДДД__pycache__\n",
"і і ГДДД_vendor\n",
"і і і ГДДДcachecontrol\n",
"і і і і ГДДДcaches\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДcertifi\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДchardet\n",
"і і і і ГДДДcli\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДcolorama\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДdistlib\n",
"і і і і ГДДД_backport\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДhtml5lib\n",
"і і і і ГДДДfilters\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДtreeadapters\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДtreebuilders\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДtreewalkers\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДД_trie\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДidna\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДmsgpack\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДpackaging\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДpep517\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДpkg_resources\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДprogress\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДrequests\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДresolvelib\n",
"і і і і ГДДДcompat\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДtoml\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДurllib3\n",
"і і і і ГДДДcontrib\n",
"і і і і і ГДДД_securetransport\n",
"і і і і і і АДДД__pycache__\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДpackages\n",
"і і і і і ГДДДbackports\n",
"і і і і і і АДДД__pycache__\n",
"і і і і і ГДДДssl_match_hostname\n",
"і і і і і і АДДД__pycache__\n",
"і і і і і АДДД__pycache__\n",
"і і і і ГДДДutil\n",
"і і і і і АДДД__pycache__\n",
"і і і і АДДД__pycache__\n",
"і і і ГДДДwebencodings\n",
"і і і і АДДД__pycache__\n",
"і і і АДДД__pycache__\n",
"і і АДДД__pycache__\n",
"і ГДДДpip-20.2.3.dist-info\n",
"і ГДДДpkg_resources\n",
"і і ГДДДextern\n",
"і і і АДДД__pycache__\n",
"і і ГДДД_vendor\n",
"і і і ГДДДpackaging\n",
"і і і і АДДД__pycache__\n",
"і і і АДДД__pycache__\n",
"і і АДДД__pycache__\n",
"і ГДДДsetuptools\n",
"і і ГДДДcommand\n",
"і і і АДДД__pycache__\n",
"і і ГДДДextern\n",
"і і і АДДД__pycache__\n",
"і і ГДДД_distutils\n",
"і і і ГДДДcommand\n",
"і і і і АДДД__pycache__\n",
"і і і АДДД__pycache__\n",
"і і ГДДД_vendor\n",
"і і і ГДДДpackaging\n",
"і і і і АДДД__pycache__\n",
"і і і АДДД__pycache__\n",
"і і АДДД__pycache__\n",
"і ГДДДsetuptools-49.2.1.dist-info\n",
"і АДДД__pycache__\n",
"АДДДScripts\n"
]
}
],
"source": [
"! tree .env"
]
},
{
"cell_type": "markdown",
"id": "f0562d32",
"metadata": {},
"source": [
"Директория `bin` содержит исполняемый файл python который на самом деле ссылается на наш глобально установленный python, также внутри `bin` директория Virtualenv есть утилита `pip`, а также другие вещи важные из которых скрипт `activate`, этот скрипт позволит нам активировать наше виртуальное окружение. Также обратите внимание на папочку `lib`, именно в эту директорию будут ставится сторонние зависимости когда мы будем работать в виртуальном окружении. Итак, давайте активируем виртуальное окружение. Обратите внимание, что все примеры, которые я здесь показываю для работы с виртуальным окружением в данном случае я работаю на Linux, а вот для Windows виртуальное окружение, структура его директории будут немного отличаться. Все остальные концепции, о которых я буду рассказывать справедливы для всех систем.\n",
"\n",
"- [Виртуальное окружение на Windows](Виртуальное%20окружение%20на%20Windows.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "643ab7b8",
"metadata": {},
"source": [
"Итак мы должны активировать виртуальное окружение, это делается с помощью команды `source` на unix-подобных системах и мы должны вызвать `source env/bin/activate`, чтобы деактивировать виртуальное окружение, мы набираем `deactivite`."
]
},
{
"cell_type": "markdown",
"id": "13a3e80c",
"metadata": {},
"source": [
"Однако нам всё же нужно работать в виртуальном окружении поэтому давайте его активируем опять и теперь установим в него сторонний модуль. Установим библиотечку `requests`, с которой мы уже начинали, она быстро устанавливается и теперь, когда мы пишем `python`, мы уже можем писать не `python3`, а `python` потому что, в данном случае мы находится в виртуальном окружении и `python` ссылается на `python3`.\n",
"\n",
"```shell\n",
"pip install requests\n",
"```\n",
"\n",
"```python\n",
"import requests\n",
"```\n",
"\n",
"Мы можем делать импорт только что установленной библиотечки `request`.\n",
"\n",
"- [Работа offline](Работа%20offline.ipynb)\n",
"\n",
"Продолжим идти к нашей цели, напомню, это `Jupiter` ноутбук и установим его, делается это точно также `pip install jupiter` ноутбук содержится в пакете `jupiter`, запускаем. Как вы видите пошёл процесс установки, в этот момент `pip` утилита загружает все необходимые зависимости пакета `jupither` с ресурса по `pip.org` а также зависимости зависимостей, среди которых на самом деле достаточно много всевозможных пакетов, но тем не менее, всё это происходит автоматически и когда процесс заканчивается у вас всё должно быть успешно установлено.\n",
"\n",
"Вот мы видим на экране, что у нас всё получилось и прежде чем запустить `Jupiter` ноутбук хотелось бы обратить внимание на ещё на один полезный пакет, который установился вместе с зависимостями `Jupiter` это пакет `ipython`. `ipython` это расширенная версия интерактивный интерпретатор Python. Если вы любите экспериментировать в интерактивном интерпретаторе Python, то рассмотрите `ipython` в качестве альтернативы, помимо подсветки синтаксиса, которая присутствует в `ipython`, там есть ещё много классных особенностей, которые позволят вам упростить жизнь. Например, это автодополнение, хранение истории и всевозможные полезные макросы."
]
},
{
"cell_type": "markdown",
"id": "a59124a3",
"metadata": {},
"source": [
"Теперь мы можем попробовать всё-таки запустить наш `Jupiter` ноутбук, он установлен в виртуальное окружение, теперь его исполняемые файлы находятся в папочке `bin` виртуального окружения и командой `jupiter-notebook` мы можем его запустить. Как я говорил это клиент-серверное веб-приложение, которое запускается в веб-браузере, веб-браузер у меня открылся автоматически и мы внутри этого веб-приложения можем создавать наши ноутбуки. Нажимаю на кнопочку `New`, выбираем `Python3` и у нас открывается наш ноутбук, по сути он состоит из ячеек в которых мы можем набирать питоновский код, делать любые импорты пакетов, которые у нас установлены и по сути всё, что мы можем делать в интерактивном интерпретаторе Python, мы можем сделать здесь, однако все эти ноутбуки можно сохранить, можно вернуться к ним в дальнейшем и проглядеть заново, перезапустить ячейки, это очень удобно. Как вы можете видеть я слайдах использую Jupiter ноутбук в качестве инструмента для создания слайдов например.\n",
"\n",
"На этой лекции мы с вами познакомились с виртуальным окружением в Python, научились ставить пакеты в нашу операционную систему, также мы установили Jupiter ноутбук, который является полезным инструментом для всех программистов на Python."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,109 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4cd45516",
"metadata": {},
"source": [
"# Корни квадратного уравнения #"
]
},
{
"cell_type": "markdown",
"id": "2aa19998",
"metadata": {},
"source": [
"Python очень активно применяют ученые со всего мира в своих исследованиях. В этом задании мы с вами попробуем с помощью Python найти корни квадратного уравнения.\n",
"\n",
"Напомним, что квадратное уравнение выглядит следующим образом:\n",
"\n",
"$$ ax^2 + bx + c = 0 $$\n",
"\n",
"На вход программе мы подадим коэффициенты $a$, $b$ и $c$ - ваша цель напечатать корни квадратного уравнения (каждый с новой строки).\n",
"\n",
"Помним формулу дискриминанта и корней:\n",
"\n",
"$$ D = b^2 - 4ac $$\n",
"$$ x_{1,2} = \\frac {-b \\pm \\sqrt {b^2 - 4ac} } {2a} $$\n",
"\n",
"Коэффициенты a, b, c вы можете получить следующим образом:\n",
"\n",
"```\n",
"import sys\n",
"\n",
"a = int(sys.argv[1])\n",
"b = int(sys.argv[2])\n",
"c = int(sys.argv[3])\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "af54e415",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4.0\n",
"-1.0\n"
]
}
],
"source": [
"! python equation.py 1 -3 -4"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "dfaa7006",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-0.3333333333333333\n"
]
}
],
"source": [
"! python equation.py 9 6 1"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "93119b50",
"metadata": {},
"outputs": [],
"source": [
"! python equation.py 2 -3 4"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,759 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Модули и пакеты #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте поговорим об организации кода на Python.\n",
"\n",
"В реальных проектах, конечно, никто код в один файлик не пишет. Это очень быстро становится громоздким и неподдерживаемым. Представьте проекты, где тысячи или даже сотни тысяч строк кода. Конечно, код надо разделять по смыслу, по назначению. И в Python'е это разделение предоставляется механизмом модулей и пакетов.\n",
"\n",
"Что такое модуль в Python? Модуль в Python мы с вами уже создавали, когда экспериментировали с интерактивным интерпретатором. Модуль в Python - это, по сути, файлик с расширением.py, который содержит определения переменных, функций, классов, и который также может содержать импорты других модулей. Давайте мы с вами начнем знакомство с модулем и создадим наш новый модуль."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Перейдем в консоль, создадим директорию `playground`, в которой мы будем работать. Перейдем в нее.\n",
"\n",
"```shell\n",
"mkdir playground\n",
"cd playground\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте создадим модуль, назовем его `mymodule`.\n",
"\n",
"```shell\n",
"vim mymodule.py\n",
"```\n",
"\n",
"Опять же обратите внимание на расширение.py."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Внутри `mymodule` мы можем писать код. Как я говорил, модуль может содержать импорты. Давайте начнем знакомиться с импортами и посмотрим на импорт модуля из стандартной библиотеки. Это делается с помощью ключевого слова `import`. Мы сейчас заимпортируем модуль стандартной библиотеки `sys`.\n",
"\n",
"```python\n",
"import sys\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Модуль `sys` содержит всевозможные переменные, структуры, функции для того, чтобы посмотреть на что-то, связанное с интерпретатором Python. В данном случае мы посмотрим на то, где Python ищет модули по умолчанию. Эта информация находится внутри переменной `sys.path`, давайте напечатаем ее на экран.\n",
"\n",
"```python\n",
"print(sys.path)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Как мы с вами уже делали, мы можем запустить модуль напрямую с помощью `python3`. Мы видим на экране, что с режима переменной `sys.path` вывелось. Это список директорий, в которых Python по умолчанию ищет модули. Обратите внимание, что на первом месте написана директория, в которой мы сейчас находимся.\n",
"\n",
"```shell\n",
"python3 mymodule.py\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['C:\\\\Users\\\\MikhaylovAF\\\\Python\\\\1. Введение в Python\\\\4. Организация кода и окружение', 'C:\\\\ProgramData\\\\Anaconda3\\\\python38.zip', 'C:\\\\ProgramData\\\\Anaconda3\\\\DLLs', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib', 'C:\\\\ProgramData\\\\Anaconda3', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\locket-0.2.1-py3.8.egg', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32\\\\lib', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\Pythonwin']\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте убедимся в этом. Утилита `pwd` показывает нашу текущую директорию. То есть Python будет искать модули в этой директории в первую очередь, поэтому ничто нам не мешает запустить интерактивный интерпретатор Python и попробовать заимпортировать наш модуль. Сделаем это."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['C:\\\\Users\\\\MikhaylovAF\\\\Python\\\\1. Введение в Python\\\\4. Организация кода и окружение', 'C:\\\\ProgramData\\\\Anaconda3\\\\python38.zip', 'C:\\\\ProgramData\\\\Anaconda3\\\\DLLs', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib', 'C:\\\\ProgramData\\\\Anaconda3', '', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\locket-0.2.1-py3.8.egg', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32\\\\lib', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\Pythonwin', 'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\IPython\\\\extensions', 'C:\\\\Users\\\\MikhaylovAF\\\\.ipython']\n"
]
}
],
"source": [
"import mymodule"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`import mymodule`, и видим, что у нас получилось. Что важно отметить, это то, что если мы попробуем сделать `import mymodule` еще раз, то ничего на экран не выведется. Это связано с тем, что Python импортирует модуль только один раз. Он импортирует имя модуля в локальное пространство имен, и в дальнейшем, если мы делаем точно такой же импорт, импорта не происходит, потому что Python видит, что мы уже ранее этот модуль импортировали, импорт закеширован."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Когда происходит импорт, что вообще происходит? Python импортирует модуль и выполняет все инструкции, которые в этом модуле на верхнем уровне определены. Собственно он наши инструкции, которые мы написали в файлике `mymodule.py` и выполнил.\n",
"\n",
"Давайте пойдем дальше. Иногда модулей недостаточно. Иногда код нужно объединять по смыслу в более крупные объединения, такие как пакеты."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Пакеты в Python, по сути, это директория, которая содержит один или больше модулей. Внутри пакетов могут содержаться другие пакеты. Давайте посмотрим на примере и создадим пакет. Пакет, по сути, является тем же самым модулем. Но давайте создадим пакет, назовем его `mypackage`. Это директория, и внутри директории `mypackage` давайте создадим пустой файлик `__init__.py`. Обратите внимание на название этого файла. Оно начинается с двух символов подчеркивания, заканчивается ими и с расширением `py`.\n",
"\n",
"```shell\n",
"mkdir mypackage\n",
"touch mypackage/__init__.py\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Это специальный файл, который должен содержать пакет для того, чтобы интерпретироваться Python'ом как пакет. На самом деле начиная с Python'a 3.3 есть так называемый `mainspace packages`, который не требует наличия `__init__.py` файлика в них. Но мы будем с вами рассматривать обычные `regular packages`, которые вы обычно будете видеть на практике."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Что это за файлик `__init__.py`? Это файл, который будет выполняться каждый раз, когда мы импортируем наш пакет. Давайте посмотрим на примере. Откроем наш модуль, который мы писали, `mymodule.py` и заменим импорты. Теперь мы будем импортировать не модуль стандартной библиотеки, а пакет, который мы только что создали. И давайте напечатаем, что это за объект такой `mypackage`, который мы заимпортировали.\n",
"\n",
"```python\n",
"import mypackage\n",
"\n",
"print(mypackage)\n",
"```\n",
"\n",
"Сохраняем и запускаем с помощью `python3`.\n",
"\n",
"```shell\n",
"python3 mymodule.py\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<module 'mypackage' from 'C:\\\\Users\\\\MikhaylovAF\\\\Python\\\\1. Введение в Python\\\\4. Организация кода и окружение\\\\mypackage\\\\__init__.py'>\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы видим, что у нас импорт прошел успешно, мы не получили никаких ошибок, и видим, что mypackage на самом деле представляет собой модуль. Как я и говорил, пакеты - это тоже модули. Давайте теперь внутри файлика `__init__.py` в модуле `mypackage` напишем немножко кода.\n",
"\n",
"```shell\n",
"vim mypackage/__init__.py\n",
"```\n",
"\n",
"Мы напишем здесь `print(\"importing mypackage\")`. Целью нашей является удостовериться, что файлик `__init__.py` вызывается каждый раз, когда мы импортируем пакет `mypackage` из нашего модуля `mymodule`. Вот мы видим, что на экран напечаталась строка, которую мы написали. Это значит, что файлик `__init__.py` был выполнен при импорте."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n",
"<module 'mypackage' from 'C:\\\\Users\\\\MikhaylovAF\\\\Python\\\\1. Введение в Python\\\\4. Организация кода и окружение\\\\mypackage\\\\__init__.py'>\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте посмотрим на директорию, которая у нас получилась. Мы видим, что мы создали `mymodule.py`, а также пакет, который содержит файлик `__init__.py`.\n",
"\n",
"```shell\n",
"tree\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Однако обратите внимание, что помимо тех директорий и файлов, которые мы создавали сами, Python автоматически создал директорию, которая называется `__pycache__` с двумя подчеркиваниями в начале и в конце, а также файлик `_init_.pyc`. Что это такое? Директория `__pycache__` и файлик с расширением.pyc содержат скомпилированное представление нашего кода. \n",
"\n",
"Виртуальная машина Python не выполняет напрямую код, который мы пишем сами, она прежде всего его преобразовывает в оптимизированное внутреннее представление, которое называется байткодом. Соответственно, директории `__pycache__` и файлы с расширением.pyc содержат как раз такое соптимизированное представление нашего кода, то есть байткод. Про то, что такое байткод и зачем он нужен, мы с вами будем говорить в отдельной лекции, а сейчас просто не пугайтесь, когда вы увидите вот такие автоматически созданные Python'ом файлы."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте пойдем дальше и еще поговорим об импортах и организации кода на Python'е. Откроем наш модуль. Очень часто на практике вы будете видеть вот такую конструкцию.\n",
"\n",
"```python\n",
"if __name__ == \"__main__\":\n",
" print(\"Hello\")\n",
"```\n",
"\n",
"Что это значит? Каждый модуль содержит в своем пространстве имен переменную `__name__` с двумя подчеркиваниями, которая определяет название модуля, в котором выполняется код. Это позволяет разделить те моменты, когда наш модуль используется напрямую интерпретатором Python, либо он был импортирован из другого модуля. Что это значит - лучше посмотреть на примере.\n",
"\n",
"Давайте попробуем выполнить наш модуль напрямую интерпретатором Python. Модуль выполнился, и мы видим, что строка \"hello\" напечаталась на экран."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n",
"Hello\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Однако если мы сейчас зайдем в интерактивный интерпретатор и попробуем заимпортировать наш модуль, мы видим, что строка \"hello\" не напечаталась."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n"
]
}
],
"source": [
"import mymodule"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Это как раз благодаря той проверке на то, как вызывается наш модуль, `if __name__ == \"_main__\"`. Мы можем таким образом контролировать, какие кусочки кода в каких условиях у нас вызываются. Далее.\n",
"\n",
"Следующее, что мы посмотрим, это как внутри пакета создавать свои отдельные модули, как я уже сказал, пакет является объединением одного и более модулей, и как оттуда импортировать код. Синтаксис импортов в Python достаточно богат, поэтому мы сейчас это осветим. Давайте создадим внутри директории `mypackage` новый модуль, который будет называться `utils.py`.\n",
"\n",
"```shell\n",
"vim mypackage/utils.py\n",
"```\n",
"\n",
"В этом модуле мы объявим функцию. С функциями вы пока не знакомы, и у нас в следующих блоках будут отдельная лекция, посвященная им. Однако давайте сейчас напишем самую простую функцию, которая будет называться `multiply`, и она будет перемножать два значения, которые ей передали в качестве аргументов, и возвращать результат этого умножения. Функция в Python записывается с помощью ключевого слова `def`, ну вы это еще увидите в дальнейшем.\n",
"\n",
"```python\n",
"def multiply(a, b):\n",
" return a * b\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Пока наша задача - посмотреть, как эту функцию можно заимпортировать, находясь внутри `mymodule`, который мы с вами написали. Давайте посмотрим на структуру, которая у нас получилась. Вот файлик `utils.py`, который мы только что создали, он внутри пакета `mypackage`. Как нам получить функцию `multiply` внутри `mymodule`? На самом деле, все, что нам нужно сделать - это сделать вот такой импорт, `import mypackage.utils`. Теперь мы можем обращаться к функции `multiply` вот таким вот образом - `mypackage.utils.multiply`. Давайте умножим 2 и 3.\n",
"\n",
"```shell\n",
"vim mymodule.py\n",
"```\n",
"\n",
"Сохраним и вызовем `python3`. У нас получилось."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n",
"6\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы видим, что в результате выполнения нашей программы, получилось число 6. Однако это достаточно длинная запись. В Python'е есть конструкция `from…import`. Что это значит? Что мы можем переписать наш импорт вот таким образом, используя `from mypackage.utils import`, и дальше название нашей функции, которую мы прописали в модуле `utils`. Тогда в коде мы можем обращаться к этой функции просто по ее имени, потому что она при импорте была добавлена в локальное пространство имен.\n",
"\n",
"```python\n",
"from mypackage.utils import multiply\n",
"\n",
"if __name__ == \"__main__\":\n",
" print(multiply(2, 3))\n",
"```\n",
"\n",
"Опять же запустим - то же самое."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n",
"6\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Есть еще возможность. Есть такой синтаксис, как `import` звездочка. То есть он записывается вот так, `import *`. Это позволяет из модуля `utils` заимпортировать все объявления, которые там содержатся.\n",
"\n",
"```python\n",
"from mypackage.utils import *\n",
"\n",
"multiply(2, 3)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"importing mypackage\n",
"6\n"
]
}
],
"source": [
"! python mymodule.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В данном случае у нас там содержится только функция `multiply`. Она будет заимпортирована, и мы можем ее использовать. Запустим - то же самое. Однако такой импорт со звездочкой не рекомендуется к использованию на практике, потому что он неявный. И в большинстве случаев его используют при экспериментах в интерактивном интерпретаторе, возможно при тестировании кода, но не советуют использовать при написании кода приложений и при импортах из каких-то библиотек.\n",
"\n",
"Последнее, что нам осталось посмотреть в работе модулей — это то, как посмотреть, где, например, находится модуль, который мы заимпортировали. Давайте попробуем это сделать. Под словами \"где он находится\" я имею в виду, где на диске. В Python есть такой замечательный модуль, который называется `this`."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The Zen of Python, by Tim Peters\n",
"\n",
"Beautiful is better than ugly.\n",
"Explicit is better than implicit.\n",
"Simple is better than complex.\n",
"Complex is better than complicated.\n",
"Flat is better than nested.\n",
"Sparse is better than dense.\n",
"Readability counts.\n",
"Special cases aren't special enough to break the rules.\n",
"Although practicality beats purity.\n",
"Errors should never pass silently.\n",
"Unless explicitly silenced.\n",
"In the face of ambiguity, refuse the temptation to guess.\n",
"There should be one-- and preferably only one --obvious way to do it.\n",
"Although that way may not be obvious at first unless you're Dutch.\n",
"Now is better than never.\n",
"Although never is often better than *right* now.\n",
"If the implementation is hard to explain, it's a bad idea.\n",
"If the implementation is easy to explain, it may be a good idea.\n",
"Namespaces are one honking great idea -- let's do more of those!\n"
]
}
],
"source": [
"import this"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Давайте сделаем `import this`, и видим, что на экран вывелся набор утверждений, которым должен следовать каждый Python-программист. Давайте посмотрим на первые два. `Beautiful is better than ugly` - красивое лучше, чем некрасивое. Логично. Второе - `Explicit is better than implicit` — явное лучше неявного. Это очень важный принцип. Посмотрите на все эти утверждения. Они забавные, интересные и в целом справедливые. Однако напоминаю, что наша цель — посмотреть, откуда же он был заимпортирован. В Python существуют богатые средства интроспекции, которые предоставляет модуль стандартной библиотеки `inspect`. Мы его импортируем и дальше воспользуемся функцией `getfile` из этого модуля, чтобы посмотреть, где находится модуль `this`. Передаем название модуля `this` и видим путь на жестком диске, на котором хранится наш модуль `this.py`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\this.py'"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import inspect\n",
"\n",
"inspect.getfile(this)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"На вашей операционной системе результат будет, скорее всего, другим. Нас в данном случае интересует директория. А какие же еще модули содержатся в этой директории, помимо `this`? Давайте посмотрим. Сделаем импорт из модуля `os` — это модуль стандартной библиотеки, который позволяет работать с операционной системой. И в модуле `os` есть функция `listdir`, которая позволяет получить содержимое той или иной директории. Мы передаем ей наш путь, который мы только что скопировали, и получаем список файлов, которые находятся в этой директории. Посмотрите на этот огромный список."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['abc.py',\n",
" 'aifc.py',\n",
" 'antigravity.py',\n",
" 'argparse.py',\n",
" 'ast.py',\n",
" 'asynchat.py',\n",
" 'asyncio',\n",
" 'asyncore.py',\n",
" 'base64.py',\n",
" 'bdb.py',\n",
" 'binhex.py',\n",
" 'bisect.py',\n",
" 'bz2.py',\n",
" 'calendar.py',\n",
" 'cgi.py',\n",
" 'cgitb.py',\n",
" 'chunk.py',\n",
" 'cmd.py',\n",
" 'code.py',\n",
" 'codecs.py',\n",
" 'codeop.py',\n",
" 'collections',\n",
" 'colorsys.py',\n",
" 'compileall.py',\n",
" 'concurrent',\n",
" 'configparser.py',\n",
" 'contextlib.py',\n",
" 'contextvars.py',\n",
" 'copy.py',\n",
" 'copyreg.py',\n",
" 'cProfile.py',\n",
" 'crypt.py',\n",
" 'csv.py',\n",
" 'ctypes',\n",
" 'curses',\n",
" 'dataclasses.py',\n",
" 'datetime.py',\n",
" 'dbm',\n",
" 'decimal.py',\n",
" 'difflib.py',\n",
" 'dis.py',\n",
" 'distutils',\n",
" 'doctest.py',\n",
" 'dummy_threading.py',\n",
" 'email',\n",
" 'encodings',\n",
" 'ensurepip',\n",
" 'enum.py',\n",
" 'filecmp.py',\n",
" 'fileinput.py',\n",
" 'fnmatch.py',\n",
" 'formatter.py',\n",
" 'fractions.py',\n",
" 'ftplib.py',\n",
" 'functools.py',\n",
" 'genericpath.py',\n",
" 'getopt.py',\n",
" 'getpass.py',\n",
" 'gettext.py',\n",
" 'glob.py',\n",
" 'gzip.py',\n",
" 'hashlib.py',\n",
" 'heapq.py',\n",
" 'hmac.py',\n",
" 'html',\n",
" 'http',\n",
" 'idlelib',\n",
" 'imaplib.py',\n",
" 'imghdr.py',\n",
" 'imp.py',\n",
" 'importlib',\n",
" 'inspect.py',\n",
" 'io.py',\n",
" 'ipaddress.py',\n",
" 'json',\n",
" 'keyword.py',\n",
" 'lib2to3',\n",
" 'libLIEF.dll',\n",
" 'libLIEF.lib',\n",
" 'linecache.py',\n",
" 'locale.py',\n",
" 'logging',\n",
" 'lzma.py',\n",
" 'mailbox.py',\n",
" 'mailcap.py',\n",
" 'mimetypes.py',\n",
" 'modulefinder.py',\n",
" 'msilib',\n",
" 'multiprocessing',\n",
" 'netrc.py',\n",
" 'nntplib.py',\n",
" 'ntpath.py',\n",
" 'nturl2path.py',\n",
" 'numbers.py',\n",
" 'opcode.py',\n",
" 'operator.py',\n",
" 'optparse.py',\n",
" 'os.py',\n",
" 'pathlib.py',\n",
" 'pdb.py',\n",
" 'pickle.py',\n",
" 'pickletools.py',\n",
" 'pipes.py',\n",
" 'pkgutil.py',\n",
" 'platform.py',\n",
" 'plistlib.py',\n",
" 'poplib.py',\n",
" 'posixpath.py',\n",
" 'pprint.py',\n",
" 'profile.py',\n",
" 'pstats.py',\n",
" 'pty.py',\n",
" 'pyclbr.py',\n",
" 'pydoc.py',\n",
" 'pydoc_data',\n",
" 'py_compile.py',\n",
" 'queue.py',\n",
" 'quopri.py',\n",
" 'random.py',\n",
" 're.py',\n",
" 'reprlib.py',\n",
" 'rlcompleter.py',\n",
" 'runpy.py',\n",
" 'sched.py',\n",
" 'secrets.py',\n",
" 'selectors.py',\n",
" 'shelve.py',\n",
" 'shlex.py',\n",
" 'shutil.py',\n",
" 'signal.py',\n",
" 'site-packages',\n",
" 'site.py',\n",
" 'smtpd.py',\n",
" 'smtplib.py',\n",
" 'sndhdr.py',\n",
" 'socket.py',\n",
" 'socketserver.py',\n",
" 'sqlite3',\n",
" 'sre_compile.py',\n",
" 'sre_constants.py',\n",
" 'sre_parse.py',\n",
" 'ssl.py',\n",
" 'stat.py',\n",
" 'statistics.py',\n",
" 'string.py',\n",
" 'stringprep.py',\n",
" 'struct.py',\n",
" 'subprocess.py',\n",
" 'sunau.py',\n",
" 'symbol.py',\n",
" 'symtable.py',\n",
" 'sysconfig.py',\n",
" 'tabnanny.py',\n",
" 'tarfile.py',\n",
" 'telnetlib.py',\n",
" 'tempfile.py',\n",
" 'test',\n",
" 'textwrap.py',\n",
" 'this.py',\n",
" 'threading.py',\n",
" 'timeit.py',\n",
" 'tkinter',\n",
" 'token.py',\n",
" 'tokenize.py',\n",
" 'trace.py',\n",
" 'traceback.py',\n",
" 'tracemalloc.py',\n",
" 'tty.py',\n",
" 'turtle.py',\n",
" 'turtledemo',\n",
" 'types.py',\n",
" 'typing.py',\n",
" 'unittest',\n",
" 'urllib',\n",
" 'uu.py',\n",
" 'uuid.py',\n",
" 'venv',\n",
" 'warnings.py',\n",
" 'wave.py',\n",
" 'weakref.py',\n",
" 'webbrowser.py',\n",
" 'wsgiref',\n",
" 'xdrlib.py',\n",
" 'xml',\n",
" 'xmlrpc',\n",
" 'zipapp.py',\n",
" 'zipfile.py',\n",
" 'zipimport.py',\n",
" '_bootlocale.py',\n",
" '_collections_abc.py',\n",
" '_compat_pickle.py',\n",
" '_compression.py',\n",
" '_dummy_thread.py',\n",
" '_markupbase.py',\n",
" '_nsis.py',\n",
" '_osx_support.py',\n",
" '_pydecimal.py',\n",
" '_pyio.py',\n",
" '_py_abc.py',\n",
" '_sitebuiltins.py',\n",
" '_strptime.py',\n",
" '_system_path.py',\n",
" '_threading_local.py',\n",
" '_weakrefset.py',\n",
" '__future__.py',\n",
" '__phello__.foo.py',\n",
" '__pycache__']"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import os\n",
"\n",
"os.listdir(\"C:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Это все модули стандартной библиотеки в Python, коих просто тонны. Там есть модули на все случаи жизни. И с какой-то частью из этих модулей мы вас познакомим в рамках нашего курса в дальнейшем. Однако может так получиться, что даже этой богатой функциональности, которая есть в стандартной библиотеке Python, вам может не хватить — у вас какая-то специфичная задача. Не отчаивайтесь, Python — это язык с огромным сообществом, и существует масса библиотек, написанных этим сообществом на все случаи жизни, которые вы можете установить в свою систему. Эти библиотеки находятся на ресурсе `pypi.org`, и вы в любой момент можете зайти на этот ресурс, посмотреть, какая библиотека вам нужна, и установить ее в вашу систему. А установка пакета стороннего в систему производиться с помощью утилиты `pip`, и мы с вами на следующей лекции посмотрим, как стороннюю библиотеку в вашу систему можно поставить.\n",
"\n",
"На этой лекции мы познакомились с организацией кода на Python, посмотрели, что такое модули и что такое пакеты, и как их организовывать, как импортировать код из модулей и пакетов. Также с некоторыми особенностями, которые с модулями и пакетами связаны. Нас ждет в дальнейшем еще немало работы с модулями стандартной библиотеки. Теперь вы умеете ими пользоваться."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,199 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "381820da",
"metadata": {},
"source": [
"# Объектная структура в Python #"
]
},
{
"cell_type": "markdown",
"id": "405c46bd",
"metadata": {},
"source": [
"Сейчас давайте поговорим про объектную структуру в Python.\n",
"\n",
"На протяжении всей недели я подчеркивал, что когда мы создаем переменную, мы не просто присваиваем переменной значение, а мы создаем связь имени переменной с объектом.\n",
"\n",
"Давайте посмотрим, что это значит. Посмотрим на примере целочисленного объекта. Давайте объявим переменную `num` и свяжем ее с целочисленным объектом 13."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "38ec2a88",
"metadata": {},
"outputs": [],
"source": [
"num = 13"
]
},
{
"cell_type": "markdown",
"id": "479473c5",
"metadata": {},
"source": [
"Если посмотреть внимательней, то окажется, что у переменной `num` есть метод `__add__` c двумя подчеркиваниями, который добавляет к числу другое число."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "626c1f15",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"15"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num.__add__(2)"
]
},
{
"cell_type": "markdown",
"id": "4585f708",
"metadata": {},
"source": [
"Как же так? Что это за метод? В других языках программирования вы могли видеть, что когда вы создаете переменную, эта переменная на самом деле ссылается непосредственно на адрес памяти, в котором хранится, например, число. В Python не так.\n",
"\n",
"В Python переменная ссылается на объект, и если мы посмотрим внимательней на наш объект и используем функцию `dir` для того, чтобы посмотреть все методы атрибута, то мы увидим, что на самом деле их еще больше, чем мы могли бы подумать. Их действительно очень много."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "dd2ae309",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']\n"
]
}
],
"source": [
"print(dir(num))"
]
},
{
"cell_type": "markdown",
"id": "77fa3764",
"metadata": {},
"source": [
"Если мы то же самое сделаем со строкой, то мы увидим, что и для строки выполняется то же самое."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "05b3e0c9",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']\n"
]
}
],
"source": [
"print(dir(\"строка\"))"
]
},
{
"cell_type": "markdown",
"id": "c9225a3e",
"metadata": {},
"source": [
"Дело в том, что в Python всё есть объект. Целые числа, строки — это все представлено, как объект внутри. На C уровне определена структура, которая называется `PyObject`.\n",
"\n",
"```c\n",
"typedef struct _object {\n",
" _PyObject_HEAD_EXTRA\n",
" Py_ssize_t ob_refcnt; // Счетчик ссылок\n",
" struct _typeobject *ob_type; // Указатель на тип объекта\n",
"} PyObject;\n",
"```\n",
"\n",
"Эта структура содержит два важных поля, первое из которых это `ob_refcnt` — это счетчик ссылок. Что это такое?\n",
"Когда мы создаем новую связь имени переменной с объектом, этот счетчик ссылок автоматически увеличивается на единицу. Как только создается еще одна связь — еще на единицу, и так далее. Когда же связь становится не нужна, например, функция закончила выполнение, и Python понимает, что какая-то переменная больше не нужна, счетчик ссылок, на который она ссылается, на единицу уменьшается, и когда он достигает нуля, счетчик ссылок может удалить объект из памяти. Таким образом, в Python автоматическая сборка мусора со счетчиком ссылок.\n",
"\n",
"Второе важное поле — это указатель на тип объекта. Зачем это нужно? Как мы уже говорили, Python — это язык с динамической типизацией, и указатель на тип объекта, который есть у каждого объекта в Python, позволяет Python'у в процессе работы в runtime'е определять тип того или иного объекта, тем самым производя правильные операции, применяя правильные действия к этому объекту.\n",
"\n",
"Также есть структура `PyVarObject`, которая расширяет структуры `PyObject` и добавляет одно важное поле.\n",
"\n",
"```c\n",
"typedef struct {\n",
" PyObject ob_base;\n",
" Py_ssize_t ob_size; // Кол-во элементов в переменной части\n",
"} PyVarObject;\n",
"```\n",
"\n",
"Это поле `ob_size`. Поле `ob_size` содержит количество элементов переменной части объекта. Что это значит? Это значит, что это поле, например, может использоваться как контейнер для длины строки. Для того чтобы каждый раз не рассчитывать длину строки, не итерироваться по всем символам, длину строки можно сохранять в это поле.\n",
"\n",
"Это поле используется в различных типах по-разному для строки, как мы видим, для хранения длины.\n",
"\n",
"Так же на C уровне определены два макроса, которые соответствуют `PyVarObject` и `PyObject`, и эти макросы используются для того, чтобы создавать новые типы в Python, новые объекты.\n",
"\n",
"```c\n",
"#define PyObject_HEAD PyObject ob_base;\n",
"#define PyObject_VAR_HEAD PyVarObject ob_base;\n",
"\n",
"typedef struct PyMyObject {\n",
" PyObject_HEAD\n",
" ...\n",
"}\n",
"```\n",
"\n",
"или\n",
"\n",
"```c\n",
"typedef struct PyMyObject {\n",
" PyObject_VAR_HEAD\n",
" ...\n",
"}\n",
"```\n",
"\n",
"По сути так реализованы все встроенные типы в Python. Они расширяют структуры `PyObject`, либо `PyVarObject`. Тем самым, у каждого объекта есть счетчик ссылок, у каждого объекта есть указатель на тип.\n",
"\n",
"Что стоит отметить еще, что так реализованы не только какие-то простые типы, такие как `int`, `float`, `boolean` типы, но и другие вещи, которые вы встречаете в Python'е. Например, модули, либо функции, либо классы. Все это является под капотом объектом со своими методами и атрибутами.\n",
"\n",
"Теперь, я думаю, вас не испугает огромное количество методов, которые есть у питоновских объектов. Вы знаете, что на самом деле под капотом все это является расширением структуры `PyObject`, и, что самое главное, из этого следует, то что в Python'е любой объект можно присвоить переменной, можно передать как аргумент в функцию. На самом деле знать это необязательно для того, чтобы программировать на Python'е. Однако это информация, которая поможет вам в дальнейшем стать более продуктивным, все-таки знать, как инструмент устроен. Это очень важно."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,91 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c8270781",
"metadata": {},
"source": [
"# Пример. Пишем программу #"
]
},
{
"cell_type": "markdown",
"id": "055a2af7",
"metadata": {},
"source": [
"Сейчас давайте попробуем обобщить все, что мы прошли и напишем небольшую программку.\n",
"\n",
"Суть ее сводится к тому, что по нашему IP-адресу мы хотим получить информацию о нашем местоположении. При этом мы пройдем все шаги с начала, используя виртуальное окружение и давайте займемся этим.\n",
"\n",
"В Интернете есть замечательный ресурс - `ip-api.com`, который по нашему IP-адресу позволяет посмотреть информацию о нашем местоположении. Доступна эта информация по адресу `ip-api.com/json/`. Мы эту информацию сейчас получим с помощью Python и напечатаем на экран.\n",
"\n",
"```json\n",
"{\n",
" \"query\": \"192.168.0.1\",\n",
" \"status\": \"success\",\n",
" \"countryCode\": \"RU\",\n",
" \"country\": \"Russia\",\n",
" \"region\": \"CHE\",\n",
" \"regionName\": \"Chelyabinsk Oblast\",\n",
" \"city\": \"Snezhinsk\",\n",
" \"zip\": \"456770\",\n",
" \"lat\": 56.0855106,\n",
" \"lon\": 60.7314472,\n",
" \"timezone\": \"Asia/Yekaterinburg\"\n",
"}\n",
"```\n",
"\n",
"Обратите внимание, что здесь есть как раз информация о нашей стране. В зависимости от вашего IP-адреса здесь также может присутствовать и другая информация, вплоть до вашего города, например. Она будет содержаться в поле `city`.\n",
"\n",
"Давайте начнем. Первое что мы сделаем - это создадим директорию, в которой будем работать. Назовем ее также как всегда - `playground`, перейдем в нее и теперь мы можем создать в ней виртуальное окружение `python3 -m venv` и название директории, в которой будет создаваться наше виртуальное окружение. Как только виртуальное окружение создано, мы можем его активировать.\n",
"\n",
"```shell\n",
". env/bin/activate\n",
"```\n",
"\n",
"Обратите внимание, что точка - это замена команды `source`, которую я показывал ранее. Виртуальное окружение создано, и мы можем установить пакет. Мы установим пакет `requests`. Это библиотека, как я уже говорил, для работы с `http` запросами, причем очень удобная библиотека, которой пользуются практически все питонисты для работы с `http`.\n",
"\n",
"Все успешно установлено. Давайте начнем программировать. Назовем наш файлик `location.py`. Первое, что мы сделаем, это сделаем `import import requests`, который мы будем использовать.\n",
"\n",
"Далее мы хотим чтобы наша программка работала только тогда, когда мы ее запускаем интерпретатором Python, то есть нам нужно написать конструкцию `if __name__ == \"__main__\":`. Внутри этой конструкции мы напечатаем на экран результат выполнения нашей функции, которая будет называться `get_location_info`.\n",
"\n",
"Конечно же нам нужно объявить саму эту функцию. Напомню, что про функции мы вам будем рассказывать более подробно в дальнейшем. В одну строчку мы получим данные с сайта `ip-api.com`, используя библиотеку `requests`. У `requests` есть метод `get` для того, чтобы сделать `get http` запрос, то есть то как мы открывали эту страничку в браузере.\n",
"\n",
"Давайте скопируем адрес, вставим его. Также есть метод `json`, который позволит текст, который возвращается, когда мы запрашиваем этот адрес в формате `json` преобразовать во внутреннее представление в Python. В нашем случае этот `json` будет преобразован в словарь. Словарь - это структура данных, которая встроена в язык и про нее мы также будем рассказывать в дальнейшем. В принципе, все готово.\n",
"\n",
"```python\n",
"import requests\n",
"\n",
"def get_location_info():\n",
" return requests.get(\"http://ip-api.com/json/\").json()\n",
"\n",
"if __name__ == \"__main__\":\n",
" print(get_location_info())\n",
"```\n",
"\n",
"Теперь мы можем запустить нашу программу - `python location.py`. Мы получили эти данные. Все очень быстро, все очень удобно, в одну строчку практически решение. Однако, давайте посмотрим на код. Что здесь не хватает? Конечно же, у нас может отсутствовать Интернет соединение, либо сайт `ip-api.com` может быть недоступен. В таком случае упадет ошибка. Ошибки мы в этой программе не обрабатываем. Однако, на практике, чтобы писать хороший код, вы должны обрабатывать все возможные исключения, которые могут произойти. Этому я вас буду учить в следующих лекциях."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,49 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "06c03dfd",
"metadata": {},
"source": [
"# Проверка установки Python #"
]
},
{
"cell_type": "markdown",
"id": "a44cc24d",
"metadata": {},
"source": [
"Я хочу удостовериться, что Python 3 верно установлен в систему и вы научились пользоваться виртуальным окружением и ставить внешние модули с помощью утилиты pip.\n",
"\n",
"Для выполнения задания вам нужно:\n",
"\n",
"1. Создать, активировать и деактевировать виртуальное окружение;\n",
"2. Запустить интерпретатор Python 3, и убедиться в его версия >= 3.6;\n",
"3. Импортировать модуль стандартной библиотеки Python;\n",
"4. Установить сторонний модуль, например, `requests`;\n",
"5. Импортировать модуль, который не входит в стандартную библиотеку Python, например, `requests`."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,105 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d6326720",
"metadata": {},
"source": [
"# Работа offline #"
]
},
{
"cell_type": "markdown",
"id": "30e75439",
"metadata": {},
"source": [
"Отсутствие интернета не беда!\n",
"\n",
"В pypi репозитории 300 тысяч пакетов, 2,5 милионов релизов и порядка 4,5 милионов файлов что занимает более 2Тб. - это пакеты в исходном коде, колеса (whl), пакеты под разные платформы, версии Python и разные версии самого пакета.\n",
"\n",
"Для синхронизации репозитория pypi можно использовать пакет `bandersnatch`. Для развертывания локального pypi репозитория есть пакет `pypiserver`. Использовать его легко:\n",
"\n",
"```shell\n",
"pypi-server --port 8080 --interface 127.0.0.1 --root ~/path/packages --disable-fallback\n",
"```\n",
"\n",
"где в директории ~/path/packages находятся пакеты Python. Чтобы использовать данный pypi репозиторий необходимо настроить свой pip:\n",
"\n",
"```ini\n",
"; Linux: /etc/pip.conf\n",
"; Windows: C:\\Users\\User\\AppData\\Roaming\\pip\\pip.ini\n",
"\n",
"[global]\n",
"index = http://127.0.0.1:8080\n",
"index-url = http://127.0.0.1:8080/simple\n",
"trusted-host = 127.0.0.1\n",
"```\n",
"\n",
"Скачать определенный пакет с его зависимостями можно командой:\n",
"\n",
"```shell\n",
"pip download -d ~/path/packages <PACKAGE1> [<PACKAGE2> <PACKAGE3>]\n",
"```\n",
"\n",
"Установить пакет из директории можно вот так:\n",
"\n",
"```shell\n",
"pip install --no-index --find-links ~/path/packages <PACKAGE1> [<PACKAGE2> <PACKAGE3>]\n",
"```\n",
"\n",
"Для обновления самого pip используют команду:\n",
"\n",
"```shell\n",
"pip install --upgrade pip\n",
"```\n",
"\n",
"или (что в Windows чаще приходится делать):\n",
"```shell\n",
"python -m pip install --upgrade pip\n",
"```\n",
"\n",
"Сам pip можно получить разным способом (если конечно его нет в системе с самим Python):\n",
"\n",
"- установить из репозитория в Linux:\n",
"\n",
"```shell\n",
"yum install python3-pip\n",
"```\n",
"\n",
"- установить с помощью модуля Python:\n",
"\n",
"```shell\n",
"python3 -m ensurepip\n",
"```\n",
"\n",
"- скачать с сайта и установить.\n",
"\n",
"```shell\n",
"wget https://bootstrap.pypa.io/get-pip.py\n",
"python get-pip.py\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,82 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "caa5f142",
"metadata": {},
"source": [
"# Рисуем лестницу #"
]
},
{
"cell_type": "markdown",
"id": "cf46f7d9",
"metadata": {},
"source": [
"Эта задачка чуть сложней предыдущей и потребует от вас размышлений. Мы будем рисовать лестницу.\n",
"\n",
"На вход ваша программа будет получать количество ступенек.\n",
"\n",
"```python\n",
"import sys\n",
"\n",
"num_steps = int(sys.argv[1])\n",
"```\n",
"\n",
"Ваша цель напечатать на экран лесенку используя символы пробела \" \" и решетки \"#\". Например, для входного параметра (количества ступенек) 4 лесенка должна выглядеть следующим образом:\n",
"\n",
"```\n",
" #\n",
" ##\n",
" ###\n",
"####\n",
"```\n",
"\n",
"Конечно, мы будем подавать на вход вашей программе разное количество ступенек."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "9d3cc6ed",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" #\n",
" ##\n",
" ###\n",
" ####\n",
" #####\n"
]
}
],
"source": [
"! python stairs.py 5"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,83 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "749960d7",
"metadata": {},
"source": [
"# Сумма цифр в строке #"
]
},
{
"cell_type": "markdown",
"id": "e18f4190",
"metadata": {},
"source": [
"Давайте начнем с несложной задачки - ваша цель найти сумму цифр, из которых состоит строка.\n",
"\n",
"Вы должны создать Python модуль `solution.py`. Этот файл запускаем с аргументом командной строки строкой, состоящей из цифр. Например, вот такой:\n",
"\n",
"\"873\"\n",
"\n",
"Чтобы получить ввод, вы в начале программы можете считать его, используя модуль стандартной библиотеки sys:\n",
"\n",
"```python\n",
"import sys\n",
"\n",
"digit_string = sys.argv[1]\n",
"```\n",
"\n",
"В переменной `digit_string` будет содержаться строка \"873\" (ну или какая-то другая строка, в том числе другой длины). В строке, подаваемой на вход, будут только символы, соответствующие цифрам от 0 до 9.\n",
"\n",
"В результате ваша программа должна напечатать на экран сумму цифр (для строки \"873\" сумма будет 18).\n",
"\n",
"То, что полученная программа ведет себя должным образом можно проверить локально, запустив ее следующим образом:\n",
"\n",
"```python\n",
"python3 solution.py 873\n",
"```\n",
"\n",
"В списке `sys.argv` будут лежать аргументы командной строки, `sys.argv[0]` - имя запущенного файла, `sys.argv[1]` - строка, сумму цифр которой необходимо посчитать и вывести на экран."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "38669063",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"18\n"
]
}
],
"source": [
"! python solution.py 873"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,230 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "386445d7",
"metadata": {},
"source": [
"# Тест по блоку #"
]
},
{
"cell_type": "markdown",
"id": "75a76c51",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "67365d05",
"metadata": {},
"source": [
"##### 1. Совместимы ли Python 2 и Python 3?\n",
"\n",
"- [ ] Несовместимы\n",
"- [ ] Совместимы полностью"
]
},
{
"cell_type": "markdown",
"id": "e9415f9e",
"metadata": {},
"source": [
"##### 2. На каком языке программирования написана основная реализация спецификации Python?\n",
"\n",
"- [ ] Python\n",
"- [ ] C\n",
"- [ ] Java"
]
},
{
"cell_type": "markdown",
"id": "02e8631d",
"metadata": {},
"source": [
"##### 3. Какое расширение обычно дают файлам с кодом на Python?\n",
"\n",
"- [ ] .python\n",
"- [ ] .pyc\n",
"- [ ] .py"
]
},
{
"cell_type": "markdown",
"id": "c62d7c53",
"metadata": {},
"source": [
"##### 4. Как пишутся комментарии в Python?\n",
"\n",
"- [ ] `/* это комментарий */`\n",
"- [ ] `# это комментарий`\n",
"- [ ] `// это комментарий`"
]
},
{
"cell_type": "markdown",
"id": "ba1df828",
"metadata": {},
"source": [
"##### 5. Какие имена переменных правильные?\n",
"\n",
"- [ ] name\n",
"- [ ] _name\n",
"- [ ] !name\n",
"- [ ] 1name"
]
},
{
"cell_type": "markdown",
"id": "0720dc5e",
"metadata": {},
"source": [
"##### 6. `bool(0.000001)` - `True` или `False`?\n",
"\n",
"- [ ] False\n",
"- [ ] True"
]
},
{
"cell_type": "markdown",
"id": "0db8f448",
"metadata": {},
"source": [
"##### 7. Что получится в результате выполнения среза `[0:1]` для строки \"привет\"?\n",
"\n",
"- [ ] \"п\"\n",
"- [ ] \"пр\"\n",
"- [ ] \"\""
]
},
{
"cell_type": "markdown",
"id": "e3137721",
"metadata": {},
"source": [
"##### 8. Какая функция позволяет считать ввод пользователя из терминала?\n",
"\n",
"- [ ] input()\n",
"- [ ] readline()\n",
"- [ ] enter()\n",
"- [ ] read()"
]
},
{
"cell_type": "markdown",
"id": "b42cb89d",
"metadata": {},
"source": [
"##### 9. Какой метод превратит байтовую строку в строку?\n",
"\n",
"- [ ] .decode()\n",
"- [ ] .encode()"
]
},
{
"cell_type": "markdown",
"id": "0085afce",
"metadata": {},
"source": [
"##### 10. Чему будет равен `pi_fmt`?\n",
"\n",
"```python\n",
"pi = 3.1415926\n",
"pi_fmt = f\"{pi:#0.2f}\"\n",
"```\n",
"\n",
"- [ ] Строке \"3.14\"\n",
"- [ ] Числу 3.14"
]
},
{
"cell_type": "markdown",
"id": "7ecf728d",
"metadata": {},
"source": [
"##### 11. Предположим, есть пакет **foo**, в котором находится модуль **bar.py**, внутри **bar.py** определена функция с именем **run**. Какая конструкция импорта является правильной?\n",
"\n",
"- [ ] `import run from foo.bar`\n",
"- [ ] `from foo.bar import run`\n",
"- [ ] `import foo.bar.run`"
]
},
{
"cell_type": "markdown",
"id": "e7cd2fd1",
"metadata": {},
"source": [
"##### 12. Предположим, есть код\n",
"\n",
"```python\n",
"import this\n",
"import this\n",
"```\n",
"\n",
"Сколько раз напечатаются идиомы Python?\n",
"\n",
"- [ ] Дважды\n",
"- [ ] Только единожды"
]
},
{
"cell_type": "markdown",
"id": "4ce5fcfb",
"metadata": {},
"source": [
"##### 13. Зачем нужен virtualenv (виртуальное окружение)?\n",
"\n",
"- [ ] Возможность увеличить скорость запуска скомпилированных в байткод Python-программ\n",
"- [ ] Возможность запускать несколько интерпретаторов Python одновременно\n",
"- [ ] Изоляция зависимостей"
]
},
{
"cell_type": "markdown",
"id": "6d0683f2",
"metadata": {},
"source": [
"##### 14. Какая утилита позволяет ставить внешние Python пакеты в систему?\n",
"\n",
"- [ ] pypi\n",
"- [ ] pip\n",
"- [ ] pep"
]
},
{
"cell_type": "markdown",
"id": "df78b46f",
"metadata": {},
"source": [
"##### 15. Что содержат файлы с расширением `.pyc`?\n",
"\n",
"- [ ] Код на Python, cкомпилированный в машинный код\n",
"- [ ] Код на Python, cкомпилированный в байткод"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,221 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "386445d7",
"metadata": {},
"source": [
"# Тест по блоку #"
]
},
{
"cell_type": "markdown",
"id": "67365d05",
"metadata": {},
"source": [
"1. Совместимы ли Python 2 и Python 3?\n",
"\n",
"- [x] Несовместимы\n",
"- [ ] Совместимы полностью"
]
},
{
"cell_type": "markdown",
"id": "e9415f9e",
"metadata": {},
"source": [
"2. На каком языке программирования написана основная реализация спецификации Python?\n",
"\n",
"- [ ] Python\n",
"- [x] C\n",
"- [ ] Java"
]
},
{
"cell_type": "markdown",
"id": "02e8631d",
"metadata": {},
"source": [
"3. Какое расширение обычно дают файлам с кодом на Python?\n",
"\n",
"- [ ] .python\n",
"- [ ] .pyc\n",
"- [x] .py"
]
},
{
"cell_type": "markdown",
"id": "c62d7c53",
"metadata": {},
"source": [
"4. Как пишутся комментарии в Python?\n",
"\n",
"- [ ] `/* это комментарий */`\n",
"- [x] `# это комментарий`\n",
"- [ ] `// это комментарий`"
]
},
{
"cell_type": "markdown",
"id": "ba1df828",
"metadata": {},
"source": [
"5. Какие имена переменных правильные?\n",
"\n",
"- [x] name\n",
"- [x] _name\n",
"- [ ] !name\n",
"- [ ] 1name"
]
},
{
"cell_type": "markdown",
"id": "0720dc5e",
"metadata": {},
"source": [
"6. `bool(0.000001)` - `True` или `False`?\n",
"\n",
"- [ ] False\n",
"- [x] True"
]
},
{
"cell_type": "markdown",
"id": "0db8f448",
"metadata": {},
"source": [
"7. Что получится в результате выполнения среза `[0:1]` для строки \"привет\"?\n",
"\n",
"- [x] \"п\"\n",
"- [ ] \"пр\"\n",
"- [ ] \"\""
]
},
{
"cell_type": "markdown",
"id": "e3137721",
"metadata": {},
"source": [
"8. Какая функция позволяет считать ввод пользователя из терминала?\n",
"\n",
"- [x] input()\n",
"- [ ] readline()\n",
"- [ ] enter()\n",
"- [ ] read()"
]
},
{
"cell_type": "markdown",
"id": "b42cb89d",
"metadata": {},
"source": [
"9. Какой метод превратит байтовую строку в строку?\n",
"\n",
"- [x] .decode()\n",
"- [ ] .encode()"
]
},
{
"cell_type": "markdown",
"id": "0085afce",
"metadata": {},
"source": [
"10. Чему будет равен `pi_fmt`?\n",
"\n",
"```python\n",
"pi = 3.1415926\n",
"pi_fmt = f\"{pi:#0.2f}\"\n",
"```\n",
"\n",
"- [x] Строке \"3.14\"\n",
"- [ ] Числу 3.14"
]
},
{
"cell_type": "markdown",
"id": "7ecf728d",
"metadata": {},
"source": [
"11. Предположим, есть пакет **foo**, в котором находится модуль **bar.py**, внутри **bar.py** определена функция с именем **run**. Какая конструкция импорта является правильной?\n",
"\n",
"- [ ] `import run from foo.bar`\n",
"- [x] `from foo.bar import run`\n",
"- [ ] `import foo.bar.run`"
]
},
{
"cell_type": "markdown",
"id": "e7cd2fd1",
"metadata": {},
"source": [
"12. Предположим, есть код\n",
"\n",
"```python\n",
"import this\n",
"import this\n",
"```\n",
"\n",
"Сколько раз напечатаются идиомы Python?\n",
"\n",
"- [ ] Дважды\n",
"- [x] Только единожды"
]
},
{
"cell_type": "markdown",
"id": "4ce5fcfb",
"metadata": {},
"source": [
"13. Зачем нужен virtualenv (виртуальное окружение)?\n",
"\n",
"- [ ] Возможность увеличить скорость запуска скомпилированных в байткод Python-программ\n",
"- [ ] Возможность запускать несколько интерпретаторов Python одновременно\n",
"- [x] Изоляция зависимостей"
]
},
{
"cell_type": "markdown",
"id": "6d0683f2",
"metadata": {},
"source": [
"14. Какая утилита позволяет ставить внешние Python пакеты в систему?\n",
"\n",
"- [ ] pypi\n",
"- [x] pip\n",
"- [ ] pep"
]
},
{
"cell_type": "markdown",
"id": "df78b46f",
"metadata": {},
"source": [
"15. Что содержат файлы с расширением `.pyc`?\n",
"\n",
"- [ ] Код на Python, cкомпилированный в машинный код\n",
"- [x] Код на Python, cкомпилированный в байткод"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,179 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Введение в Python #\n",
"\n",
"В первом блоке вы познакомитесь с языком, основными конструкциями и базовыми типами. Настроите окружение для работы и выберете среду разработки."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Задачи обучения ##\n",
"\n",
"- Развернуть окружение для программирования на Python.\n",
"- Узнать базовые сведения о языке.\n",
"- Освоить базовые типы и конструкции языка.\n",
"- Узнать об организации кода на Python.\n",
"- Получить начальные сведения о внутреннем устройстве интерпретатора Python."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Оглавление ##"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Знакомство с курсом ###\n",
"\n",
"- [Приветствие](1.%20Знакомство%20с%20курсом/Приветствие.ipynb)\n",
"- [Опрос](1.%20Знакомство%20с%20курсом/Опрос.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Первые шаги ###\n",
"\n",
"- [О языке](2.%20Первые%20шаги/О%20языке.ipynb)\n",
"- [Установка Python 3](2.%20Первые%20шаги/Установка%20Python%203.ipynb)\n",
"- [Работа в терминале](2.%20Первые%20шаги/Работа%20в%20терминале.ipynb)\n",
"- [Выбор среды разработки](2.%20Первые%20шаги/Выбор%20среды%20разработки.ipynb)\n",
"- [Начинаем программировать](2.%20Первые%20шаги/Начинаем%20программировать.ipynb)\n",
"- [Полезные ссылки](2.%20Первые%20шаги/Полезные%20ссылки.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Базовые типы и конструкции ###\n",
"\n",
"- [Базовые типы: численные типы](3.%20Базовые%20типы%20и%20конструкции/Численные%20типы.ipynb)\n",
"- [Базовые типы: логический тип](3.%20Базовые%20типы%20и%20конструкции/Логический%20тип.ipynb)\n",
"- [Базовые типы: строки и байтовые строки](3.%20Базовые%20типы%20и%20конструкции/Строки%20и%20байтовые%20строки.ipynb)\n",
"- [Базовые типы: объект None](3.%20Базовые%20типы%20и%20конструкции/Объект%20None.ipynb)\n",
"- [Конструкции управления потоком](3.%20Базовые%20типы%20и%20конструкции/Конструкции%20управления%20потоком.ipynb)\n",
"- [Пример на управление потоком](3.%20Базовые%20типы%20и%20конструкции/Пример%20на%20управление%20потоком.ipynb)\n",
"- [Тест на типы и конструкции](3.%20Базовые%20типы%20и%20конструкции/Тест%20на%20типы%20и%20конструкции.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Организация кода и окружение ###\n",
"\n",
"- [Модули и пакеты](4.%20Организация%20кода%20и%20окружение/Модули%20и%20пакеты.ipynb)\n",
"- [Виртуальное окружение](4.%20Организация%20кода%20и%20окружение/Виртуальное%20окружение.ipynb)\n",
"- [Пример. Пишем программу](4.%20Организация%20кода%20и%20окружение/Пример.%20Пишем%20программу.ipynb)\n",
"- [Тест по блоку](4.%20Организация%20кода%20и%20окружение/Тест%20по%20блоку.ipynb)\n",
"- [Проверка установки Python](4.%20Организация%20кода%20и%20окружение/Проверка%20установки%20Python.ipynb)\n",
"- [Сумма цифр в строке](4.%20Организация%20кода%20и%20окружение/Сумма%20цифр%20в%20строке.ipynb)\n",
"- [Рисуем лестницу](4.%20Организация%20кода%20и%20окружение/Рисуем%20лестницу.ipynb)\n",
"- [Корни квадратного уравнения](4.%20Организация%20кода%20и%20окружение/Корни%20квадратного%20уравнения.ipynb)\n",
"- [Объектная структура в Python](4.%20Организация%20кода%20и%20окружение/Объектная%20структура%20в%20Python.ipynb)\n",
"- [Байткод](4.%20Организация%20кода%20и%20окружение/Байткод.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Вот и подошел к концу первый блок нашего курса."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Мы с вами познакомились с языком программирования Python, посмотрели на интерактивный интерпретатор, а также познакомились с основными конструкциями и типами, которые есть в языке.\n",
"\n",
"Также мы взглянули на то, как организовывать код на Python-e.\n",
"\n",
"Теперь вы можете писать свои первые простые программы.\n",
"\n",
"Надеюсь вы заметили, насколько Python выразителен и прост."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"В дальнейшем вас ждет еще много всего увлекательного, в том числе в следующем блоке я буду знакомить вас с основными структурами данных, которые есть в языке, а также функциями. [Далее...](../2.%20Структуры%20данных%20и%20функции/Readme.ipynb)"
]
}
],
"metadata": {
"celltoolbar": "Слайд-шоу",
"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.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,45 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "be6131f1",
"metadata": {},
"source": [
"# Документация #"
]
},
{
"cell_type": "markdown",
"id": "88d93f85",
"metadata": {},
"source": [
"Здесь и далее будет много ссылок на стандартную документацию. Умение находить и читать материалы на английском языке является ключевым навыком программиста. Тем не менее, при желании вы всегда можете найти аналогичные статьи и видео на русском языке.\n",
"\n",
"- [Стандартные типы в Python](https://docs.python.org/3/library/stdtypes.html \"4. Built-in Types\")\n",
"- [Туториал по коллекциям из документации](https://docs.python.org/3/tutorial/datastructures.html \"5. Data Structures\")\n",
"- [Hash table](https://en.wikipedia.org/wiki/Hash_table \"Hash table\")"
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,113 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0a39e911",
"metadata": {},
"source": [
"# Множества. Пример программы #"
]
},
{
"cell_type": "markdown",
"id": "f399fcc2",
"metadata": {},
"source": [
"Итак, мы с вами разобрали множества, давайте попробуем решить задачу на их применение.\n",
"\n",
"В качестве примера попробуем выяснить, через сколько итераций функция `random.randint` выдаст повтор. Как вы уже знаете, `randint` возвращает какое-то значение случайное в интервале ему переданном. Давайте импортируем наш модуль `random`, который мы будем использовать, и определим наш `random_set`, в который мы будем складывать наши уникальные числа."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f34fe0ea",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"random_set = set()"
]
},
{
"cell_type": "markdown",
"id": "8367c37e",
"metadata": {},
"source": [
"Теперь воспользуемся циклом `while True` — бесконечным циклом, и в цикле попробуем класть элементы в наше множество. Итак, получим уникальный, какой-то случайный номер, `random.randint(1, 10)`. От одного до десяти. Теперь попробуем проверить, есть ли наш новый номер в множестве. Делаем мы это с помощью оператора `in`. И если этот номер уже содержится в множестве, нам нужно просто выйти, потому что очевидно, именно это мы и хотели выяснить. Если номера нет, мы добавляем наш новый номер в `random_set`. Делаем мы это с помощью знакомого вам метода `add`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "47385363",
"metadata": {},
"outputs": [],
"source": [
"while True:\n",
" new_number = random.randint(1, 10)\n",
" \n",
" if new_number in random_set:\n",
" break\n",
" \n",
" random_set.add(new_number)"
]
},
{
"cell_type": "markdown",
"id": "d81a9e34",
"metadata": {},
"source": [
"И что же теперь происходит? У нас произошло какое-то количество итераций, и в `random_set`'е содержится какой-то набор уникальных объектов. Нашим ответом будет число уникальных объектов в `random_set`'е. Число объектов в `random_set`'е можно получить с помощью знакомой вам встроенной функции `len`. И какая-то еще одна итерация, потому что мы успели выйти до того, как добавили номер повторный. Давайте выведем."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "bb40c926",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6\n"
]
}
],
"source": [
"print(len(random_set) + 1)"
]
},
{
"cell_type": "markdown",
"id": "cf5abb51",
"metadata": {},
"source": [
"Оказалось, что уже спустя четыре итерации у нас произошел повтор, то есть `random` не такой уж и рандомный. Попробуем запустить еще раз. Да, результат получше. 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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,349 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c8ab769e",
"metadata": {},
"source": [
"# Множества #"
]
},
{
"cell_type": "markdown",
"id": "448cade2",
"metadata": {},
"source": [
"Следующей структурой данных, о которой мы с вами поговорим, являются множества.\n",
"\n",
"Множества позволяют вам хранить в неупорядоченном виде набор уникальных объектов. Чтобы определить пустое множество, можно просто воспользоваться литералом `set`, определить `empty_set`, или использовать фигурные скобочки, чтобы определить `number_set`, в котором уже содержатся какие-то числа."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a6f9836a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{1, 2, 3, 4, 5}\n"
]
}
],
"source": [
"empty_set = set()\n",
"number_set = {1, 2, 3, 3, 4, 5}\n",
"\n",
"print(number_set)"
]
},
{
"cell_type": "markdown",
"id": "1f6f1403",
"metadata": {},
"source": [
"Обратите внимание, мы пытаемся с вами добавить в `number_set` две тройки и у нас ничего не получится, потому что Python гарантирует вам то, что в множестве содержатся уникальные элементы.\n",
"\n",
"Это достигается с помощью уже знакомой функции хеширования. Также очень легко и быстро проверить, содержится ли какое-то значение в нашем множестве. Делается это с помощью оператора `in`, и проверка тоже выполняется за константное время. \n",
"\n",
"Например, двойка, очевидно, содержится в нашем сете."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "77fec4ef",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 in number_set"
]
},
{
"cell_type": "markdown",
"id": "31bc07a9",
"metadata": {},
"source": [
"Давайте создадим два множества, `odd_set` и `even_set`, в котором будут содержаться значения, чётное и нечётное число от 0 до 10. Делаем мы это с помощью цикла, и в цикле добавляем элементы множества. Множества являются изменяемой структурой данных, поэтому мы можем это делать с помощью встроенного метода `add`. Мы проверяем чётное и нечётное число и добавляем это число в соответствующее множество."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d44dcf28",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{1, 3, 5, 7, 9}\n",
"{0, 2, 4, 6, 8}\n"
]
}
],
"source": [
"odd_set = set()\n",
"even_set = set()\n",
"\n",
"for number in range(10):\n",
" if number % 2:\n",
" odd_set.add(number)\n",
" else:\n",
" even_set.add(number)\n",
" \n",
"print(odd_set)\n",
"print(even_set)"
]
},
{
"cell_type": "markdown",
"id": "bc80a0b8",
"metadata": {},
"source": [
"Множества, как и предполагает их название, поддерживают математические операции над множествами. Например, мы можем использовать объединения, знакомые вам из курса математики, и получить все значения, которые содержатся как в чётном множестве, так и в нечётном множестве.\n",
"\n",
"Очевидно, это все значения, потому что все числа от 0 до 10 являются либо чётными, либо нечётными."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "fcf03b2b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
]
}
],
"source": [
"union_set = odd_set | even_set\n",
"union_set = odd_set.union(even_set)\n",
"\n",
"print(union_set)"
]
},
{
"cell_type": "markdown",
"id": "48e027f6",
"metadata": {},
"source": [
"Точно так же мы можем брать пересечение множеств. Очевидно, что пересечение чётных и нечётных чисел от 0 до 10, да и любых чётных и нечётных чисел - это пустое множество."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "afae36d1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"set()\n"
]
}
],
"source": [
"intersection_set = odd_set & even_set\n",
"intersection_set = odd_set.intersection(even_set)\n",
"\n",
"print(intersection_set)"
]
},
{
"cell_type": "markdown",
"id": "e62260b6",
"metadata": {},
"source": [
"Также мы можем использовать разность из теории множеств и получить все значения, которые содержатся в одном множестве, но не содержатся в другом множестве. Делается это с помощью оператора минус или встроенного метода `difference`. Очевидно, что в множестве `odd_set` содержатся все элементы, которые не содержатся в множестве `even_set`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "af062911",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{1, 3, 5, 7, 9}\n"
]
}
],
"source": [
"difference_set = odd_set - even_set\n",
"difference_set = odd_set.difference(even_set)\n",
"\n",
"print(difference_set)"
]
},
{
"cell_type": "markdown",
"id": "d8117277",
"metadata": {},
"source": [
"Существует также симметрическая разность, которую можно получить с помощью шапочки или с помощью метода `symmetric_difference`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f594748a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
]
}
],
"source": [
"symmetric_difference_set = odd_set ^ even_set\n",
"symmetric_difference_set = odd_set.symmetric_difference(even_set)\n",
"\n",
"print(symmetric_difference_set)"
]
},
{
"cell_type": "markdown",
"id": "ddfb3604",
"metadata": {},
"source": [
"Так как множества являются изменяемой структурой данных, мы можем не только добавлять туда элементы, но и удалять их. Делается это с помощью метода `remove`, которому мы передаем значение, которое мы хотим удалить из множества. Например, мы можем удалить 2."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "dec76d51",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0, 4, 6, 8}\n"
]
}
],
"source": [
"even_set.remove(2)\n",
"\n",
"print(even_set)"
]
},
{
"cell_type": "markdown",
"id": "d4514890",
"metadata": {},
"source": [
"Если мы хотим удалить какое-то случайное значение, можно использовать метод `pop`, который просто вернет какое-то значение из нашего множества. Мы точно не знаем какое."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "c1d74063",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"even_set.pop()"
]
},
{
"cell_type": "markdown",
"id": "35a57ceb",
"metadata": {},
"source": [
"Так как множества являются изменяемой структурой данных, очевидно, у них существует аналог, который неизменяем. Существует `frozenset`, который гарантирует вам уникальность элементов и ведет себя точно так же, как и множества, но мы не можем добавлять туда элементы или удалять их. Например, мы можем определить множество `frozen` и попытаться в него добавить `Olaf'а`. У нас ничего не выйдет, потому что `frozenset` неизменяемый, у него нет метода `add`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "d393d0ec",
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'frozenset' object has no attribute 'add'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-11-23adb5d17543>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mfrozen\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfrozenset\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"Anna\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"Elsa\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"Kristoff\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mfrozen\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Olaf\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m: 'frozenset' object has no attribute 'add'"
]
}
],
"source": [
"frozen = frozenset([\"Anna\", \"Elsa\", \"Kristoff\"])\n",
"\n",
"frozen.add(\"Olaf\")"
]
},
{
"cell_type": "markdown",
"id": "f074172b",
"metadata": {},
"source": [
"Итак, мы с вами обсудили множества, которые являются неупорядоченным набором уникальных объектов. А также множества можно изменять, то есть добавлять и удалять оттуда элементы. Мы легко можем проверить, существует ли какое-то значение в нашем множестве с помощью оператора `in`; также множества поддерживают операции над множествами, знакомые вам из курса математики. Давайте попробуем решить задачку на множества."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,334 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9c7c1cc4",
"metadata": {},
"source": [
"# Словари. Пример программы #"
]
},
{
"cell_type": "markdown",
"id": "e4b389fd",
"metadata": {},
"source": [
"Итак, мы с вами разобрали словари и то, как они работают, и давайте попробуем решить задачку на их применение.\n",
"\n",
"В качестве примера попробуем найти три самых часто встречающихся слова в Zen of Python. Как вы уже знаете, Zen of Python можно получить, импортировав `this`, и в нём содержится какой-то набор утверждений, которым должен следовать программист, который пишет на Python.\n",
"\n",
"Например, красивое лучше чем некрасивое."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e74604f2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The Zen of Python, by Tim Peters\n",
"\n",
"Beautiful is better than ugly.\n",
"Explicit is better than implicit.\n",
"Simple is better than complex.\n",
"Complex is better than complicated.\n",
"Flat is better than nested.\n",
"Sparse is better than dense.\n",
"Readability counts.\n",
"Special cases aren't special enough to break the rules.\n",
"Although practicality beats purity.\n",
"Errors should never pass silently.\n",
"Unless explicitly silenced.\n",
"In the face of ambiguity, refuse the temptation to guess.\n",
"There should be one-- and preferably only one --obvious way to do it.\n",
"Although that way may not be obvious at first unless you're Dutch.\n",
"Now is better than never.\n",
"Although never is often better than *right* now.\n",
"If the implementation is hard to explain, it's a bad idea.\n",
"If the implementation is easy to explain, it may be a good idea.\n",
"Namespaces are one honking great idea -- let's do more of those!\n"
]
}
],
"source": [
"import this"
]
},
{
"cell_type": "markdown",
"id": "77beacdd",
"metadata": {},
"source": [
"Давайте создадим нашу переменную `zen`, в которую положим эти замечательные утверждения. Мы должны использовать тройные кавычки, потому что здесь есть переносы строк. Скопируем все строки и поместим в `zen`. Отлично."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "f7b07b1f",
"metadata": {},
"outputs": [],
"source": [
"zen = \"\"\"Beautiful is better than ugly.\n",
"Explicit is better than implicit.\n",
"Simple is better than complex.\n",
"Complex is better than complicated.\n",
"Flat is better than nested.\n",
"Sparse is better than dense.\n",
"Readability counts.\n",
"Special cases aren't special enough to break the rules.\n",
"Although practicality beats purity.\n",
"Errors should never pass silently.\n",
"Unless explicitly silenced.\n",
"In the face of ambiguity, refuse the temptation to guess.\n",
"There should be one-- and preferably only one --obvious way to do it.\n",
"Although that way may not be obvious at first unless you're Dutch.\n",
"Now is better than never.\n",
"Although never is often better than *right* now.\n",
"If the implementation is hard to explain, it's a bad idea.\n",
"If the implementation is easy to explain, it may be a good idea.\n",
"Namespaces are one honking great idea -- let's do more of those!\"\"\""
]
},
{
"cell_type": "markdown",
"id": "34a73654",
"metadata": {},
"source": [
"Теперь нам нужно каким-то образом выяснить, какие слова используются чаще всего. Логично нам нужно для начала разбить нашу большую длинную строчку на, собственно, эти самые слова. \n",
"\n",
"Давайте используем для этого переменную `zen_map` какой-то `dict`, где мы будем хранить собственно количество слов, которое мы уже нашли, и будем итерироваться с помощью метода `split`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "23b04337",
"metadata": {},
"outputs": [],
"source": [
"zen_map = dict()"
]
},
{
"cell_type": "markdown",
"id": "36889d1e",
"metadata": {},
"source": [
"Метод `split` разобьёт нашу большую строку по пробельным символам. У нас получится какое-то слово при итерации. Теперь, что нам нужно сделать с этим словом? Нам нужно, во-первых, его очистить от каких-то точек, от каких-то восклицательных знаков и прочих спецсимволов. Давайте назовём переменную `cleaned_word` и будем очищать наше слово, используя метод строки `strip`.\n",
"\n",
"Очистим всё, что мы можем найти. Отлично. Теперь давайте приведём строку к нижнему регистру с помощью метода `lower`. И теперь нам нужно добавить нашу строку в наш `zen_map`.\n",
"\n",
"Если строка уже есть в `zen_map`'е, мы просто хотим инкрементировать `counter`, который говорит о том, сколько раз мы находили уже это слово. Если строки там нет, то мы туда должны её добавить, и давайте именно это и сделаем.\n",
"\n",
"Если `cleaned_word`'а ещё нету в `zen_map`'е, мы добавим наш `zen_map` в `cleaned_word`. В качестве значения у нас будет 0, потому что мы это слово ещё не встречали. Если мы его встречали или только что добавили, мы инкрементируем это значение.\n",
"\n",
"Таким образом, в нашем `zen_map`'e будет хранится отображение из слова в количество раз, в котором мы его встретили."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "4c71d106",
"metadata": {},
"outputs": [],
"source": [
"for word in zen.split():\n",
" cleaned_word = word.strip(\".,!-*\").lower()\n",
" \n",
" if cleaned_word not in zen_map:\n",
" zen_map[cleaned_word] = 0\n",
" \n",
" zen_map[cleaned_word] += 1"
]
},
{
"cell_type": "markdown",
"id": "16f875bd",
"metadata": {},
"source": [
"Давайте попробуем вывести наш `zen_map`. Отлично."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "41ca869f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'beautiful': 1, 'is': 10, 'better': 8, 'than': 8, 'ugly': 1, 'explicit': 1, 'implicit': 1, 'simple': 1, 'complex': 2, 'complicated': 1, 'flat': 1, 'nested': 1, 'sparse': 1, 'dense': 1, 'readability': 1, 'counts': 1, 'special': 2, 'cases': 1, \"aren't\": 1, 'enough': 1, 'to': 5, 'break': 1, 'the': 5, 'rules': 1, 'although': 3, 'practicality': 1, 'beats': 1, 'purity': 1, 'errors': 1, 'should': 2, 'never': 3, 'pass': 1, 'silently': 1, 'unless': 2, 'explicitly': 1, 'silenced': 1, 'in': 1, 'face': 1, 'of': 2, 'ambiguity': 1, 'refuse': 1, 'temptation': 1, 'guess': 1, 'there': 1, 'be': 3, 'one': 3, 'and': 1, 'preferably': 1, 'only': 1, 'obvious': 2, 'way': 2, 'do': 2, 'it': 2, 'that': 1, 'may': 2, 'not': 1, 'at': 1, 'first': 1, \"you're\": 1, 'dutch': 1, 'now': 2, 'often': 1, 'right': 1, 'if': 2, 'implementation': 2, 'hard': 1, 'explain': 2, \"it's\": 1, 'a': 2, 'bad': 1, 'idea': 3, 'easy': 1, 'good': 1, 'namespaces': 1, 'are': 1, 'honking': 1, 'great': 1, '': 1, \"let's\": 1, 'more': 1, 'those': 1}\n"
]
}
],
"source": [
"print(zen_map)"
]
},
{
"cell_type": "markdown",
"id": "2717da3c",
"metadata": {},
"source": [
"У нас хранится отображение из строки в количество раз, в котором мы её встретили. Например, `beautiful` у нас встретилось всего один раз, а `is` встретилось десять раз.\n",
" \n",
"Теперь нам нужно каким-то образом выяснить, какие слова встречаются чаще всего. Обратите внимание, как ключи, так и значения в словаре не упорядочены, поэтому нам нужно сделать так, чтобы они были упорядочены. Для этого можно воспользоваться, например, методом `items` и, например, в `zen_items` мы можем положить `zen_map.items`. Это будет уже список таплов. Обратите внимание, у нас в нашем `zen_items` содержится как ключ, так и значение, но уже в виде тапла, и эту вещь мы уже можем сортировать."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "981664ee",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"dict_items([('beautiful', 1), ('is', 10), ('better', 8), ('than', 8), ('ugly', 1), ('explicit', 1), ('implicit', 1), ('simple', 1), ('complex', 2), ('complicated', 1), ('flat', 1), ('nested', 1), ('sparse', 1), ('dense', 1), ('readability', 1), ('counts', 1), ('special', 2), ('cases', 1), (\"aren't\", 1), ('enough', 1), ('to', 5), ('break', 1), ('the', 5), ('rules', 1), ('although', 3), ('practicality', 1), ('beats', 1), ('purity', 1), ('errors', 1), ('should', 2), ('never', 3), ('pass', 1), ('silently', 1), ('unless', 2), ('explicitly', 1), ('silenced', 1), ('in', 1), ('face', 1), ('of', 2), ('ambiguity', 1), ('refuse', 1), ('temptation', 1), ('guess', 1), ('there', 1), ('be', 3), ('one', 3), ('and', 1), ('preferably', 1), ('only', 1), ('obvious', 2), ('way', 2), ('do', 2), ('it', 2), ('that', 1), ('may', 2), ('not', 1), ('at', 1), ('first', 1), (\"you're\", 1), ('dutch', 1), ('now', 2), ('often', 1), ('right', 1), ('if', 2), ('implementation', 2), ('hard', 1), ('explain', 2), (\"it's\", 1), ('a', 2), ('bad', 1), ('idea', 3), ('easy', 1), ('good', 1), ('namespaces', 1), ('are', 1), ('honking', 1), ('great', 1), ('', 1), (\"let's\", 1), ('more', 1), ('those', 1)])"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"zen_items = zen_map.items()\n",
"zen_items"
]
},
{
"cell_type": "markdown",
"id": "367d5fc2",
"metadata": {},
"source": [
"Как мы будем это делать? Если мы попробуем отсортировать `zen_items` прямо сейчас, то у нас сортировка произойдёт по первому значению, по `beautiful`.\n",
"\n",
"Однако, нам нужно сортировать по второму значению наших таплов, по количеству раз, в котором мы встречали это слово. Для этого нам поможет замечательный модуль `operator` и функция `sorted`. Воспользуемся функцией `sorted` и будем сортировать наши `zen_items`.\n",
"\n",
"Чтобы сортировать по второму значению в нашем тапле, мы можем в качестве аргумента `key` передать туда `operator.itemgetter` и написать единичку, потому что нас интересует именно первый индекс. `beautiful` — это нулевой индекс, единичка — это первый индекс.\n",
"\n",
"Так как нам нужны самые популярные слова, мы используем `reverse=True`."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d3b22620",
"metadata": {},
"outputs": [],
"source": [
"import operator\n",
"\n",
"word_count_items = sorted(\n",
" zen_items, key=operator.itemgetter(1), reverse=True\n",
")"
]
},
{
"cell_type": "markdown",
"id": "831b3bed",
"metadata": {},
"source": [
"Давайте попробуем вывести `word_count_items` и возьмём первые три элемента. Отлично, у нас получилось, что самые популярные слова, это `is`, `better`, `than`, что мы собственно и ожидали."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "80cdc92d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[('is', 10), ('better', 8), ('than', 8)]\n"
]
}
],
"source": [
"print(word_count_items[:3])"
]
},
{
"cell_type": "markdown",
"id": "dabaebe1",
"metadata": {},
"source": [
"Однако, как это часто бывает в Python'е, есть встроенный модуль, который вам поможет решить эту задачу намного быстрее. Мы можем импортировать из модуля `collections` `Counter`. В случае, если мы используем `Counter`, всё, что нам нужно — это очистить наши слова и передать эти слова в `Counter`.\n",
"\n",
"Давайте именно это и сделаем.\n",
"\n",
"Создадим `cleaned_list`, в который в цикле будем добавлять очищенные слова уже знакомым образом, используя метод `split`, который разбивает строку по пробельным символам, и будем добавлять в `cleaned_lis`t с помощью метода `append` очищенное слово.\n",
"\n",
"Очищенное слово будет получено с помощью метода `strip` и приведения к нижнему регистру с помощью метода `lower`.\n",
"\n",
"Итак, всё, что нам осталось сделать, это вызвать `counter` с `cleaned_list`'ом и обратиться к методу `most_common`, перадать ему троечку.\n",
"\n",
"Давайте попробуем, что у нас получится. Отлично, именно то, что мы ожидали, однако, это достигается с помощью трёх строк кода."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "0c407d54",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[('is', 10), ('better', 8), ('than', 8)]\n"
]
}
],
"source": [
"from collections import Counter\n",
"\n",
"cleaned_list = []\n",
"\n",
"for word in zen.split():\n",
" cleaned_list.append(word.strip(\".,-!\").lower())\n",
" \n",
"print(Counter(cleaned_list).most_common(3))"
]
},
{
"cell_type": "markdown",
"id": "89af8ccd",
"metadata": {},
"source": [
"Часто в Python'е есть встроенные методы и модули, которые вам позволяют делать какие-то вещи легко и удобно. Отлично, мы с вами познакомились со словарями и решили задачку на их применение."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,620 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "35e7641e",
"metadata": {},
"source": [
"# Словари #"
]
},
{
"cell_type": "markdown",
"id": "aa115439",
"metadata": {},
"source": [
"На этой лекции мы поговорим про словари.\n",
"\n",
"Словари являются важнейшей структурой данных в Python'е и они позволяют хранить данные в формате ключ-значение.\n",
"\n",
"Чтобы определить словарь, нужно использовать литерал фигурные скобки или просто вызвать `dict`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "647047ab",
"metadata": {},
"outputs": [],
"source": [
"empty_dict = {}\n",
"empty_dict = dict()"
]
},
{
"cell_type": "markdown",
"id": "b793bf48",
"metadata": {},
"source": [
"Если мы хотим определить словарь, в котором уже есть какие-то данные, мы просто пишем наши ключ-значение через двоеточие. Например, словарь collections_map содержит отображение и строк, и списки в листы."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ab12602e",
"metadata": {},
"outputs": [],
"source": [
"collections_map = {\n",
" \"mutable\": [\"list\", \"set\", \"dict\"],\n",
" \"immutable\": [\"tuple\", \"frozenset\"]\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "8f0a511f",
"metadata": {},
"source": [
"Чем хороши словари? Они позволяют получить значение по ключу за константное время, очень быстро. Это достигается с помощью алгоритма хеширования. Например, мы можем получить `immutable` лист."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ce41b9bf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['tuple', 'frozenset']\n"
]
}
],
"source": [
"print(collections_map[\"immutable\"])"
]
},
{
"cell_type": "markdown",
"id": "46286967",
"metadata": {},
"source": [
"Если мы попытаемся получить доступ по ключу, которого не существует, у нас Python выдаст нам ошибку KeyError, потому что такого ключа очевидно нет."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "bfbe0151",
"metadata": {},
"outputs": [
{
"ename": "KeyError",
"evalue": "'irresistible'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-4-c3adb9d5d5cd>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcollections_map\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"irresistible\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mKeyError\u001b[0m: 'irresistible'"
]
}
],
"source": [
"print(collections_map[\"irresistible\"])"
]
},
{
"cell_type": "markdown",
"id": "f9f60bb7",
"metadata": {},
"source": [
"Однако, часто бывает полезно попробовать взять значение по ключу и в случае неудачи вернуть какое-то дефолтное значение. Для этого есть встроенный метод `get` у словаря, который делает именно это. Например, мы попытались взять ключи `irresistible` и вернули `not found`."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "975284c8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"not found\n"
]
}
],
"source": [
"print(collections_map.get(\"irresistible\", \"not found\"))"
]
},
{
"cell_type": "markdown",
"id": "ead7601e",
"metadata": {},
"source": [
"Чтобы проверить, содержится ли ключ в словаре, мы используем уже знакомые вам операторы."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "faa26fae",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"mutable\" in collections_map"
]
},
{
"cell_type": "markdown",
"id": "b3a915d7",
"metadata": {},
"source": [
"Так как словарь является изменяемой структурой данных, мы можем добавлять и удалять элементы из него. Например, мы можем определить словарь `beatles_map`, который содержит знаменитых музыкантов и их инструменты, и добавить в него Ринго с ударными, просто используя доступ по ключу."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0f6226ae",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'Paul': 'Bass', 'John': 'Guitar', 'George': 'Guitar'}\n"
]
}
],
"source": [
"beatles_map = {\n",
" \"Paul\": \"Bass\",\n",
" \"John\": \"Guitar\",\n",
" \"George\": \"Guitar\",\n",
"}\n",
"\n",
"print(beatles_map)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "0a54daf3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'Paul': 'Bass', 'John': 'Guitar', 'George': 'Guitar', 'Ringo': 'Drums'}\n"
]
}
],
"source": [
"beatles_map[\"Ringo\"] = \"Drums\"\n",
"\n",
"print(beatles_map)"
]
},
{
"cell_type": "markdown",
"id": "6d010fb3",
"metadata": {},
"source": [
"Чтобы удалить ключ и значение из словаря, можно использовать уже знакомый вам оператор del, который удаляет в данном случае Джона из нашего словаря."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "230b9d49",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'Paul': 'Bass', 'George': 'Guitar', 'Ringo': 'Drums'}\n"
]
}
],
"source": [
"del beatles_map[\"John\"]\n",
"\n",
"print(beatles_map)"
]
},
{
"cell_type": "markdown",
"id": "5d2a6238",
"metadata": {},
"source": [
"Также, чтобы добавить какой-то ключ-значение в словарь, можно использовать встроенный метод `update`, который принимает словарь и апдейтит существующий словарь."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d2ca1637",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'Paul': 'Bass', 'George': 'Guitar', 'Ringo': 'Drums', 'John': 'Guitar'}\n"
]
}
],
"source": [
"beatles_map.update({\n",
" \"John\": \"Guitar\"\n",
"})\n",
"\n",
"print(beatles_map)"
]
},
{
"cell_type": "markdown",
"id": "ffbbe1b1",
"metadata": {},
"source": [
"Чтобы удалить ключ-значение из словаря и вернуть значение, можно использовать метод pop, и в данном случае мы удаляем Ринго, и нам возвращаются его ударные."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "c68f6183",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Drums\n",
"{'Paul': 'Bass', 'George': 'Guitar', 'John': 'Guitar'}\n"
]
}
],
"source": [
"print(beatles_map.pop(\"Ringo\"))\n",
"\n",
"print(beatles_map)"
]
},
{
"cell_type": "markdown",
"id": "64580aff",
"metadata": {},
"source": [
"Часто бывает необходимо не только попробовать проверить, существует ли ключ в словаре, но и в случае неудачи добавить эту новую пару ключ-значение. Для этого есть метод `setdefault`, и давайте посмотрим, как он работает. Мы объявляем `unknown_dict`, который абсолютно пуст, и используя метод `setdefault` пытаемся взять какой-то ключ `key` и в случае неудачи добавим туда `default`."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "403cdacb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"default\n"
]
}
],
"source": [
"unknown_dict = {}\n",
"\n",
"print(unknown_dict.setdefault(\"key\", \"default\"))"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "f8dbee49",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'key': 'default'}"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"unknown_dict"
]
},
{
"cell_type": "markdown",
"id": "8fc6bb60",
"metadata": {},
"source": [
"Происходит именно это, у нас в нашем пустом словаре получилось отображение `key`, `default` и `default` вернулся к нам в результате вызова метода `setdefault`.\n",
"\n",
"Если мы попытаемся вызвать `setdefault` и в качестве дефолтного значения передаём `new_default`, у нас вернётся значение, которое уже лежит в словаре, значение `default`."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "9b0fa584",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"default\n"
]
}
],
"source": [
"print(unknown_dict.setdefault(\"key\", \"new_default\"))"
]
},
{
"cell_type": "markdown",
"id": "3c5cf8c7",
"metadata": {},
"source": [
"Словари, как и все коллекции, поддерживают протокол итерации, поэтому мы можем итерироваться по словарю, например, с помощью метода `for`."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "5acefef3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'mutable': ['list', 'set', 'dict'], 'immutable': ['tuple', 'frozenset']}\n"
]
}
],
"source": [
"print(collections_map)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "b4c7bb66",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mutable\n",
"immutable\n"
]
}
],
"source": [
"for key in collections_map:\n",
" print(key)"
]
},
{
"cell_type": "markdown",
"id": "74c5246c",
"metadata": {},
"source": [
"Обратите внимание, мы итерируемся по ключам словаря. Можем вывести собственно ключи, которые содержатся в нашем словаре. \n",
"\n",
"Если нам нужно итерироваться не по ключам, как по умолчанию, а по ключам и значениям сразу можно использовать метод словаря `items`, который возвращает ключи и значения. Например, можно выводить в нашем `collections_map` как ключи, так и значения."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "342baa0c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mutable: ['list', 'set', 'dict']\n",
"immutable: ['tuple', 'frozenset']\n"
]
}
],
"source": [
"for key, value in collections_map.items():\n",
" print(f\"{key}: {value}\")"
]
},
{
"cell_type": "markdown",
"id": "87b8d55f",
"metadata": {},
"source": [
"Если нужно итерироваться по значениям, используйте логично метод `values`, который возвращает именно значения. Также существует симметричный метод `keys`, который возвращает оператор ключей."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "56344c1b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['list', 'set', 'dict']\n",
"['tuple', 'frozenset']\n"
]
}
],
"source": [
"for value in collections_map.values():\n",
" print(value)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "3299f648",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mutable\n",
"immutable\n"
]
}
],
"source": [
"for key in collections_map.keys():\n",
" print(key)"
]
},
{
"cell_type": "markdown",
"id": "d96b364e",
"metadata": {},
"source": [
"Важная особенность словарей в Python'е - они содержат ключи и значения в неупорядоченном виде, то есть вы не можете гарантировать, что ключи, например, отсортированы, или хранятся в том порядке, в каком вы их туда добавили. Однако, в Python'е существует `OrderedDict`, это тип, который содержится в модуле `collections`, который гарантирует вам, что ключи содержатся именно в том порядке, в каком вы их добавили в словарь. Например, мы можем вывести пару, число и строка, именно в том порядке, в каком мы их туда добавили."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "b7ce5f64",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n",
"5\n",
"6\n",
"7\n",
"8\n",
"9\n"
]
}
],
"source": [
"from collections import OrderedDict\n",
"\n",
"ordered = OrderedDict()\n",
"\n",
"for number in range(10):\n",
" ordered[number] = str(number)\n",
" \n",
"for key in ordered:\n",
" print(key)"
]
},
{
"cell_type": "markdown",
"id": "8e4e659e",
"metadata": {},
"source": [
"По секрету с версии Python 3.8 порядок гарантирован."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "b885b0f1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n",
"5\n",
"6\n",
"7\n",
"8\n",
"9\n"
]
}
],
"source": [
"ordered = dict()\n",
"\n",
"for number in range(10):\n",
" ordered[number] = str(number)\n",
" \n",
"for key in ordered:\n",
" print(key)"
]
},
{
"cell_type": "markdown",
"id": "fb29decb",
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,315 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3c0c7af7",
"metadata": {},
"source": [
"# Списки. Пример программы #"
]
},
{
"cell_type": "markdown",
"id": "7cd71aeb",
"metadata": {},
"source": [
"Итак, мы с вами разобрали, как работают списки. Давайте попробуем решить задачу на их применение.\n",
"\n",
"В качестве примера попробуем найти медиану случайного списка."
]
},
{
"cell_type": "markdown",
"id": "1384a6a8",
"metadata": {},
"source": [
"Медиана - это значение в отсортированном списке, которое лежит ровно посередине, таким образом, половина значений - слева от него, и половина значений - справа."
]
},
{
"cell_type": "markdown",
"id": "50a2cb56",
"metadata": {},
"source": [
"Чтобы получить медиану случайного списка, нам нужно случайный список создать."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8139fe20",
"metadata": {},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "markdown",
"id": "84628ca0",
"metadata": {},
"source": [
"Давайте воспользуемся для этого модулем `random` в стандартной библиотеке.\n",
"\n",
"Заведём наш список `numbers`. Давайте, у нас будет какое-то случайное количество элементов в этом списке, чтобы было интереснее.\n",
"\n",
"У нас будет `number_size`, который мы получим с помощью функции `randint`. Функция `randint` возвращает какое-то случайное значение в интервале, ей переданном.\n",
"\n",
"Таким образом, у нас `number_size` будет равняться от 10 до 15, какому-то числу."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "29199966",
"metadata": {},
"outputs": [],
"source": [
"numbers = []\n",
"numbers_size = random.randint(10, 15)"
]
},
{
"cell_type": "markdown",
"id": "c0c7e008",
"metadata": {},
"source": [
"Теперь нам нужно создать наш случайный список. Давайте воспользуемся циклом `for` и встроенной функцией `range`. Будем интерироваться ровно `number_size` раз. \n",
"\n",
"Обратите внимание, я использую переменную нижнее подчёркивание, которая говорит о том, что нам не интересно, в принципе, что в неё записывается. Мы не будем её использовать. Нам важно, чтобы итерация происходила ровно `number_size` раз.\n",
"\n",
"И будем добавлять в наши `numbers` с помощью знакомого вам метода `append` какое-то новое число. А случайное число мы будем получать с помощью той же самой функции `randint`, и пусть это число будет от 10 до 20."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "95d1de3b",
"metadata": {},
"outputs": [],
"source": [
"for _ in range(numbers_size):\n",
" numbers.append(random.randint(10, 20))"
]
},
{
"cell_type": "markdown",
"id": "1cd2eb85",
"metadata": {},
"source": [
"Давайте выведем наш список. Отлично."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "26e888a2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[15, 15, 16, 17, 10, 18, 13, 20, 20, 17]\n"
]
}
],
"source": [
"print(numbers)"
]
},
{
"cell_type": "markdown",
"id": "328d7ffd",
"metadata": {},
"source": [
"У нас получился список со случайными значениями, они не отсортированы, и, как видно, от 11 до 20, насколько я вижу.\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "701ba286",
"metadata": {},
"source": [
"Теперь, чтобы найти медиану, нам нужно список отсортировать. Давайте используем для этого встроенный в список метод `sort` и отсортируем его."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "c96230f4",
"metadata": {},
"outputs": [],
"source": [
"numbers.sort()"
]
},
{
"cell_type": "markdown",
"id": "b7e71ccc",
"metadata": {},
"source": [
"`sort` сортирует `in place`, поэтому наш список уже должен быть отсортирован, давайте выведем его."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5dc5b158",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[10, 13, 15, 15, 16, 17, 17, 18, 20, 20]\n"
]
}
],
"source": [
"print(numbers)"
]
},
{
"cell_type": "markdown",
"id": "f3a7b55e",
"metadata": {},
"source": [
"Отлично, 10 в начале, 20 в конце, значит, список отсортирован.\n",
"\n",
"Теперь нам нужно взять какое-то среднее значение. Как вы могли догадаться, случая может быть два. Если у нас количество элементов в списке нечётное, то всё просто. Мы просто берём средний элемент. Если количество элементов чётное, то по определению медианы нам нужно взять среднее арифметическое от двух средних элементов.\n",
"\n",
"Давайте заведем переменную `half_size`, в которую положим значение, равное половине длины списка. Для этого используем встроенную функцию `len` и поделим нашу длину пополам. И заведём переменную медиана, которая будет для начала равняться `None`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "49d11c5e",
"metadata": {},
"outputs": [],
"source": [
"half_size = len(numbers) // 2\n",
"median = None"
]
},
{
"cell_type": "markdown",
"id": "2b920f46",
"metadata": {},
"source": [
"Итак, в простейшем случае у нас нечётное количество элементов. Если у нас наш `number_size` нечётный, то есть при делении на два даёт остаток один, то мы просто берём в качестве медианы `numbers[half_size]`.\n",
" \n",
"Однако интересное происходит, когда у нас чётное количество элементов в нашем списке. В таком случае нам нужно взять среднее из двух чисел, которые лежат посередине. Воспользуемся знакомой вам функцией `sum` встроенной и срезом. Нам нужен срез от `half_size -1` до `half_size +1`.\n",
"\n",
"Обратите внимание, элементы нумеруются с нуля, поэтому именно так нам нужно поступать. И мы берём среднее арифметическое, поэтому делим на два."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "460fd334",
"metadata": {},
"outputs": [],
"source": [
"if numbers_size % 2 == 1:\n",
" median = numbers[half_size]\n",
"else:\n",
" median = sum(\n",
" numbers[half_size - 1:half_size + 1]\n",
" ) / 2"
]
},
{
"cell_type": "markdown",
"id": "18bd5ff2",
"metadata": {},
"source": [
"Давайте попробуем вывести нашу медиану."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "8fd0d3ae",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"16.5\n"
]
}
],
"source": [
"print(median)"
]
},
{
"cell_type": "markdown",
"id": "33912a15",
"metadata": {},
"source": [
"У нас получилось значение, и, на самом деле, это действительно медиана нашего списка.\n",
"\n",
"Чтобы это проверить, можно воспользоваться встроенным модулем `statistics`, который позволяет нам сделать то, что мы делали только что какое-то заметное количество времени, намного быстрее.\n",
"\n",
"Давайте импортируем модуль `statistics`, и у `statistics` есть метод `median`, который позволит нам найти медиану очень легко. Отлично."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "71dd213f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"16.5"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import statistics\n",
"\n",
"statistics.median(numbers)"
]
},
{
"cell_type": "markdown",
"id": "13664bbb",
"metadata": {},
"source": [
"Медиана действительно равна 16.5. Мы с вами не только разобрали списки, но и посмотрели задачу на их применение."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,106 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4244c488",
"metadata": {},
"source": [
"# Тест по коллекциям #"
]
},
{
"cell_type": "markdown",
"id": "7345eb54",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "302b4c7d",
"metadata": {},
"source": [
"##### 1. Выберите верные утверждения про списки:\n",
"\n",
"- [ ] списки могут содержать элементы различных типов\n",
"- [ ] списки изменяемые\n",
"- [ ] проверка на вхождение элемента в список происходит за линейное время\n",
"- [ ] списки неизменяемые\n",
"- [ ] проверка на вхождение элемента в список происходит за константное время"
]
},
{
"cell_type": "markdown",
"id": "1c801377",
"metadata": {},
"source": [
"##### 2. К чему приведет обращение к непустому списку по индексу \"-1\"?\n",
"\n",
"- [ ] Ошибка `IndexError`\n",
"- [ ] Ошибка `KeyError`\n",
"- [ ] Вернется первый элемент\n",
"- [ ] Вернется последний элемент"
]
},
{
"cell_type": "markdown",
"id": "80eb70ce",
"metadata": {},
"source": [
"##### 3. Выберите верные утверждения про словари:\n",
"\n",
"- [ ] поиск ключа в словаре происходит за константное время\n",
"- [ ] поиск ключа в словаре происходит за линейное время\n",
"- [ ] словари изменяемые\n",
"- [ ] словари неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "0143258b",
"metadata": {},
"source": [
"##### 4. Можно ли изменять список, находящийся внутри кортежа?\n",
"\n",
"- [ ] Да\n",
"- [ ] Нет"
]
},
{
"cell_type": "markdown",
"id": "cace4fb3",
"metadata": {},
"source": [
"##### 5. В чем отличие стандартного метода списка `sort` и встроенное функции `sorted`?\n",
"\n",
"- [ ] `Sorted` сортирует исходный список, а `sort` возвращает новый\n",
"- [ ] `Sort` сортирует исходный список, а `sorted` возвращает новый\n",
"- [ ] Отличий нет\n",
"- [ ] Функции `sorted` не существует"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,97 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4244c488",
"metadata": {},
"source": [
"# Тест по коллекциям #"
]
},
{
"cell_type": "markdown",
"id": "302b4c7d",
"metadata": {},
"source": [
"1. Выберите верные утверждения про списки:\n",
"\n",
"- [x] списки могут содержать элементы различных типов\n",
"- [x] списки изменяемые\n",
"- [x] проверка на вхождение элемента в список происходит за линейное время\n",
"- [ ] списки неизменяемые\n",
"- [ ] проверка на вхождение элемента в список происходит за константное время"
]
},
{
"cell_type": "markdown",
"id": "1c801377",
"metadata": {},
"source": [
"2. К чему приведет обращение к непустому списку по индексу \"-1\"?\n",
"\n",
"- [ ] Ошибка `IndexError`\n",
"- [ ] Ошибка `KeyError`\n",
"- [ ] Вернется первый элемент\n",
"- [x] Вернется последний элемент"
]
},
{
"cell_type": "markdown",
"id": "80eb70ce",
"metadata": {},
"source": [
"3. Выберите верные утверждения про словари:\n",
"\n",
"- [x] поиск ключа в словаре происходит за константное время\n",
"- [ ] поиск ключа в словаре происходит за линейное время\n",
"- [x] словари изменяемые\n",
"- [ ] словари неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "0143258b",
"metadata": {},
"source": [
"4. Можно ли изменять список, находящийся внутри кортежа?\n",
"\n",
"- [x] Да\n",
"- [ ] Нет"
]
},
{
"cell_type": "markdown",
"id": "cace4fb3",
"metadata": {},
"source": [
"5. В чем отличие стандартного метода списка `sort` и встроенное функции `sorted`?\n",
"\n",
"- [ ] `Sorted` сортирует исходный список, а `sort` возвращает новый\n",
"- [x] `Sort` сортирует исходный список, а `sorted` возвращает новый\n",
"- [ ] Отличий нет\n",
"- [ ] Функции `sorted` не существует"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,135 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6da43522",
"metadata": {},
"source": [
"# Aннотация типов #"
]
},
{
"cell_type": "markdown",
"id": "a086d5d9",
"metadata": {},
"source": [
"В Python'е последних версий появилась возможность аннотировать типы, и делается это с помощью двоеточия в случае параметров, и вот такой вот стрелочкой, если мы хотим указать, какого типа возвращаемое значение должно вернуться из функции."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "2ae84c77",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"21\n",
"One, two\n"
]
}
],
"source": [
"def add(x: int, y: int) -> int:\n",
" s: int = x + y\n",
" return s\n",
"\n",
"print(add(10, 11))\n",
"print(add(\"One, \", \"two\"))"
]
},
{
"cell_type": "markdown",
"id": "80d0226a",
"metadata": {},
"source": [
"Однако, что интересно, если мы передадим даже параметры других типов, как в данном случае, то у нас код все равно исполняется, потому что Python - это динамический язык, и аннотация типов призвана помочь программисту или его `IDE` отловить какие-то ошибки.\n",
"\n",
"Тем не менее код все равно исполняется. Если вы считаете это необходимым, можете использовать аннотацию типов, а так же изучить пакет `typing` и `mypy`.\n",
"\n",
"Рекомендую еще использовать аннотацию типов в кавычках, что может Вас огородить от ряда проблем с цеклическими импортами."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9c6913fa",
"metadata": {},
"outputs": [],
"source": [
"from typing import TYPE_CHECKING\n",
"\n",
"if TYPE_CHECKING:\n",
" from typing import List\n",
"\n",
"def to_list(x: \"int\", y: \"int\") -> \"List[int]\":\n",
" return [x, y]"
]
},
{
"cell_type": "markdown",
"id": "1cc049a2",
"metadata": {},
"source": [
"Aннотацию типов можно производить с помощью комментариев."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "d9133920",
"metadata": {},
"outputs": [],
"source": [
"from typing import TYPE_CHECKING\n",
"\n",
"if TYPE_CHECKING:\n",
" from typing import List\n",
"\n",
"def to_list(x, y):\n",
" # type: (int, int) -> List[int]\n",
" \n",
" result = [x, y] # type: List[int]\n",
" \n",
" return result"
]
},
{
"cell_type": "markdown",
"id": "a01ed930",
"metadata": {},
"source": [
"Aннотацию типов можно производить в отдельном файле с расширением `.pyi`\n",
"\n",
"```python\n",
"from typing import List\n",
"\n",
"def to_list(x: \"int\", y: \"int\") -> \"List[int]\": ...\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,73 @@
"""Реализация Key-value хранилища"""
from argparse import ArgumentParser
from json import dumps, loads
from os import path, remove
from tempfile import gettempdir
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Dict, List, Optional
STORAGE_PATH: "str" = path.join(gettempdir(), "storage.data")
def get_data() -> "Dict[str,List[str]]":
"""Получение всех данных из хранилища"""
result: "Dict[str,List[str]]" = {}
if path.exists(STORAGE_PATH):
with open(STORAGE_PATH, "r", encoding="utf-8") as des:
raw_data: "str" = des.read()
if raw_data:
result = loads(raw_data)
return result
def put(key: "str", value: "str") -> "None":
"""Запись значения по ключу"""
data: "Dict[str,List[str]]" = get_data()
if key in data:
data[key].append(value)
else:
data[key] = [
value,
]
with open(STORAGE_PATH, "w", encoding="utf-8") as des:
des.write(dumps(data))
def get(key) -> "Optional[str]":
"""Получение данных по ключу"""
data: "Dict[str,List[str]]" = get_data()
return None if not key in data else ", ".join(data.get(key))
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("--key", help="Key")
parser.add_argument("--val", help="Value")
parser.add_argument("--clear", action="store_true", help="Clear")
args = parser.parse_args()
if args.clear:
remove(STORAGE_PATH)
elif args.key and args.val:
put(args.key, args.val)
elif args.key:
print(get(args.key))
else:
print("Wrong command")

@ -0,0 +1,14 @@
"""Реализация декоратора to_json"""
from functools import wraps
from json import dumps
def to_json(func):
"""Декоратора to_json"""
@wraps(func)
def wrapped(*args, **kwargs):
return dumps(func(*args, **kwargs))
return wrapped

@ -0,0 +1,562 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "748ad676",
"metadata": {},
"source": [
"# Генераторы #"
]
},
{
"cell_type": "markdown",
"id": "81ac59b4",
"metadata": {},
"source": [
"На этой лекции мы с вами обсудим генераторы, которые являются очень важным механизмом, на котором построено многое в Python'е.\n",
"\n",
"Итак, простейший генератор — это функция, в которой есть оператор `yield`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "20b39e22",
"metadata": {},
"outputs": [],
"source": [
"def even_range(start, end):\n",
" current = start\n",
" while current < end:\n",
" yield current\n",
" current += 2"
]
},
{
"cell_type": "markdown",
"id": "8dd3b8b1",
"metadata": {},
"source": [
"Что же делает наш генератор? У нас генератор — это `even_range`, который работает по упрощенной схеме знакомого вам генератора `range`. Он принимает `(start, end)`, записывает текущий `start`, и пока у нас `current` меньше конечного значения, он `yield`'ит `current` и прибавляет двойку."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c4a176d4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"2\n",
"4\n",
"6\n",
"8\n"
]
}
],
"source": [
"for number in even_range(0, 10):\n",
" print(number)"
]
},
{
"cell_type": "markdown",
"id": "94a8da7f",
"metadata": {},
"source": [
"Что же делает этот оператор `yield`? `Yield` можно рассматривать как какой-то временный `return`, то есть у нас возвращается значение `current`. Однако выполнение функции не прекращается, это не обычный `return`, `return`, как вы видите, здесь вот в конце, у нас `return None` по умолчанию. `Yield` возвращает значение, но прерывает выполнение функции только на время, то есть мы можем вернуться к этой функции, к этому моменту.\n",
"\n",
"Важно знать, что мы можем итерироваться, например, по генератору, можем пробежаться и вывести все значения."
]
},
{
"cell_type": "markdown",
"id": "f2e7ddcd",
"metadata": {},
"source": [
"Каждый раз, когда у нас выполняется `yield`, у нас возвращается значение `current`, и каждый раз, когда мы просим следующий элемент, у нас выполнение функции возвращается в этот же момент, и мы идем дальше. Чтобы посмотреть, как это происходит на самом деле, можно воспользоваться функцией next, которая действительно применяется каждый раз при итерации. Таким образом, мы создаем наш итератор, наш генератор, и вызываем `next`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f652e038",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ranger = even_range(0, 4)\n",
"next(ranger)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "78b9cbcb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"next(ranger)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "30534428",
"metadata": {},
"outputs": [
{
"ename": "StopIteration",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-5-c186e1cb4ee7>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mranger\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mStopIteration\u001b[0m: "
]
}
],
"source": [
"next(ranger)"
]
},
{
"cell_type": "markdown",
"id": "5d8c18d1",
"metadata": {},
"source": [
"В начале у нас получается 0. Потом вызывается `next`, получается 2. И когда у нас наш генератор исчерпан, когда у нас условие, что текущее значение меньше 4 не выполняется, — у нас вызывается `StopIteration`, выбрасывается исключение и больше `yield`'ов нет, у нас происходит `return`.\n",
"\n",
"Именно так работают генераторы. Чтобы понять, что действительно происходит, можно, например, добавить сюда `print` после `yield`'а."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c7a44318",
"metadata": {},
"outputs": [],
"source": [
"def list_generator(list_obj):\n",
" for item in list_obj:\n",
" yield item\n",
" print(f\"After yielding {item}\")\n",
" \n",
"generator = list_generator([1, 2])"
]
},
{
"cell_type": "markdown",
"id": "3ee7ee21",
"metadata": {},
"source": [
"Давайте посмотрим, что происходит при вызове `next`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "441b6575",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"next(generator)"
]
},
{
"cell_type": "markdown",
"id": "b405a97b",
"metadata": {},
"source": [
"У нас есть наш `list_generator`, он принимает `list` какой-то `object` и просто `yield`'ит все элементы по порядку. Мы вызываем `next(generator)`, то есть берем следующий элемент из генератора, у нас возвращается единичка, первый элемент списка. Логично. Однако у нас почему-то не вывелось `print`. Не вывелось именно потому, что у нас выполнение функции генератора прервано. На этом моменте у нас вернулся элемент, и у нас запомнилось состояние функции в какой-то этот момент. "
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "870d8e66",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"After yielding 1\n"
]
},
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"next(generator)"
]
},
{
"cell_type": "markdown",
"id": "64be79b0",
"metadata": {},
"source": [
"И выводится `After yielding` у нас выводится, когда мы берем следующий элемент, то есть у нас выполнение продолжается с вот этого момента и идет до тех пор, пока нет следующего `yield`'а. Таким образом мы доходим до двойки и все."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "420efed7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"After yielding 2\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;32m<ipython-input-12-323ce5d717bb>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mgenerator\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mStopIteration\u001b[0m: "
]
}
],
"source": [
"next(generator)"
]
},
{
"cell_type": "markdown",
"id": "cab954f3",
"metadata": {},
"source": [
"Собственно, чем полезны эти генераторы? Казалось бы, концепт понятный, но не понятно, зачем их использовать. Оказывается, очень важной бывает именно вот эта вот особенность, которая позволяет хранить состояние функции и возвращаться к этому состоянию раз за разом. Таким образом, например, реализована функция `range`. Функция `range`, как вы знаете, позволяет вам получить генератор, какой-то итерабельный объект, по которому мы можем пробежаться и, например, выводить просто в цикле числа или делать что-нибудь более сложное. Функция `range` позволяет вам не загружать в память сразу огромный список чисел. Таким образом раньше в Python'е, например, была функция `range` безгенераторная и `xrange` генераторная. Функция `range` загружала в память огромное количество чисел, чтобы потом по ним итерироваться. Генератор позволяет вам делать очень просто. Он позволяет запомнить текущее положение и от него уже идти дальше."
]
},
{
"cell_type": "markdown",
"id": "f534d54f",
"metadata": {},
"source": [
"Классический пример — это числа Фибоначчи."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "ff283572",
"metadata": {},
"outputs": [],
"source": [
"def fibonacci(number):\n",
" a = b = 1\n",
" \n",
" for _ in range(number):\n",
" yield a\n",
" a, b = b, a + b"
]
},
{
"cell_type": "markdown",
"id": "d4edb25a",
"metadata": {},
"source": [
"Нам нужно получить числа Фибоначчи до какого-то момента. Опять же, обратите внимание, нам не нужно запоминать огромное количество чисел Фибоначчи, которые очень быстро растут. Нам нужно здесь только помнить два конкретных числа и возвращаться в момент, когда мы остановились."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "ca0e7f6b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"1\n",
"2\n",
"3\n",
"5\n",
"8\n",
"13\n",
"21\n",
"34\n",
"55\n"
]
}
],
"source": [
"for num in fibonacci(10):\n",
" print(num)"
]
},
{
"cell_type": "markdown",
"id": "8af9fb69",
"metadata": {},
"source": [
"Итак, очень важная особенность генераторов, что они позволяют хранить состояние и возвращаться к нему и использовать это для того, чтобы оптимизировать работу с памятью. "
]
},
{
"cell_type": "markdown",
"id": "c9054bfd",
"metadata": {},
"source": [
"Еще один очень важный момент — это возможность генераторам получать какие-то значения. Мы можем не только возвращаться к моменту, когда у нас генератор сделал `yield`, и продолжать исполнение с этого момента с каким-то контекстом, мы можем еще и передавать туда значения. Этот момент используется очень активно в асинхронном программировании, о котором мы с вами поговорим чуть позже."
]
},
{
"cell_type": "markdown",
"id": "f33f1bd3",
"metadata": {},
"source": [
"А пока давайте определим `accumulator`, нашу генераторную функцию, которая хранит какое-то общее количество данных и просто в бесконечном цикле получает с помощью оператора `yield` значение. Давайте посмотрим, что здесь происходит в простейшем виде."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "97d62f6d",
"metadata": {},
"outputs": [],
"source": [
"def accumulator():\n",
" total = 0\n",
" while True:\n",
" value = yield total\n",
" print(f\"Got: {value}\")\n",
" \n",
" if not value: break\n",
" \n",
" total += value\n",
" \n",
"generator = accumulator()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "161da2c5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"next(generator)"
]
},
{
"cell_type": "markdown",
"id": "55ec02a9",
"metadata": {},
"source": [
"Вначале мы инициализируем наш генератор, то есть мы его стартуем. У нас возвращается `yield total`, которая равна нулю по умолчанию в начале цикла. Дальше, мы можем послать данные в генератор с помощью метода генератора `send`."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "e359097d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Got: 1\n",
"Accumulated: 1\n"
]
}
],
"source": [
"print(f\"Accumulated: {generator.send(1)}\")"
]
},
{
"cell_type": "markdown",
"id": "c6c8b6c3",
"metadata": {},
"source": [
"Что же здесь мы делаем? Мы передаем в наш генератор единичку, мы посылаем туда значение, то есть у нас есть какая-то точка исполнения, у нас остановилось исполнение здесь. Мы можем послать в эту точку значение, и это значение запишется в `value`. Таким образом мы передали в `value` единицу. Мы можем вывести, что мы действительно получили единицу.\n",
"\n",
"Дальше, если у нас в `value` не было передано, мы хотим выйти. У нас есть `value`, поэтому мы прибавляем к нашему `total`'у `value`, которому мы передали. Собственно, accumulator аккумулирует значение, что логично. Итак, у нас накопилась единичка.\n",
"\n",
"Дальше, мы можем послать еще одну единичку и окажется, что мы действительно получили единичку, а накопили уже два, потому что наша функция хранит состояние, и у нас хранится контекст, в котором у нас `total` уже равен двум."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "9d27bdf5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Got: 1\n",
"Accumulated: 2\n"
]
}
],
"source": [
"print(f\"Accumulated: {generator.send(1)}\")"
]
},
{
"cell_type": "markdown",
"id": "0b4adf6b",
"metadata": {},
"source": [
"Мы можем передать еще одну единичку, и у нас очевидно `Accumulated` станет равен трем."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "8972b01d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Got: 1\n",
"Accumulated: 3\n"
]
}
],
"source": [
"print(f\"Accumulated: {generator.send(1)}\")"
]
},
{
"cell_type": "markdown",
"id": "2e9d9826",
"metadata": {},
"source": [
"Мы передали единичку, о чем и говорим. Если мы ничего не передадим, а просто пойдем дальше, то у нас по условию выхода из цикла, как вы помните, заканчивается наш генератор и выбрасывается исключение `StopIteration`."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "13dace76",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Got: None\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;32m<ipython-input-21-323ce5d717bb>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mgenerator\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mStopIteration\u001b[0m: "
]
}
],
"source": [
"next(generator)"
]
},
{
"cell_type": "markdown",
"id": "6e6b2588",
"metadata": {},
"source": [
"Видите, мы ничего не получили, мы получили `None`, поэтому мы закончили выполнение.\n",
" \n",
"Итак, генераторы являются очень важной концепцией, которая используется во многих особенностях языка Python, например, в асинхронном программировании. Они позволяют вам создавать функции, к которым можно возвращаться, которые хранят контекст выполнения и позволяют вам оптимально работать с памятью."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,787 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "13c4cdb4",
"metadata": {},
"source": [
"# Декораторы #"
]
},
{
"cell_type": "markdown",
"id": "2fd8abca",
"metadata": {},
"source": [
"На этой лекции мы с вами поговорим о декораторах, и это очень важная концепция, которую действительно нужно понять, потому что они используются повсеместно практически во всех приложениях на Python'е. \n",
"\n",
"Итак, декоратор - это функция, которая принимает функцию и возвращает функцию. Это очень важный момент, пожалуйста, запомните это."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "053894bf",
"metadata": {},
"outputs": [],
"source": [
"def decorator(func):\n",
" return func"
]
},
{
"cell_type": "markdown",
"id": "7818e41d",
"metadata": {},
"source": [
"Просто функция, принимающая одну функцию, возвращающая другую функцию. Как вы уже знаете, в Python'е функция - это объект первого класса, поэтому их можно возвращать, принимать и производить с ними разные операции.\n",
"\n",
"Итак, простейший декоратор просто возвращает функцию и возвращает её же, то есть такой `identity` декоратор, который ничего не делает. Однако, часто бывает полезно с функцией что-то делать, и мы с вами это сейчас как раз обсудим."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9c451b1e",
"metadata": {},
"outputs": [],
"source": [
"@decorator\n",
"def decorated():\n",
" print(\"Hello!\")"
]
},
{
"cell_type": "markdown",
"id": "497c0bb8",
"metadata": {},
"source": [
"Синтаксис применения декоратора в Python'е такой. На самом деле, это просто так называемый синтаксический сахар, и происходит здесь именно это. У нас вызывается декоратор, ему передаётся функция и записывается всё в функцию, которую мы декорируем."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "0c2285bb",
"metadata": {},
"outputs": [],
"source": [
"decorated = decorator(decorated)"
]
},
{
"cell_type": "markdown",
"id": "19dc0fd2",
"metadata": {},
"source": [
"Делается это обычно как раз с помощью `@`. Не нужно этого бояться, происходит здесь довольно простой момент вот такой. \n",
"\n",
"Итак, однако, как вы понимаете, довольно скучно просто возвращать саму же функцию, и нет в этом никакого смысла. Часто бывает необходимо вернуть не ту же самую функцию, а какую-то модифицированную функцию или вообще новую функцию совершенно другую.\n",
"\n",
"Например, ещё один простой декоратор, мы можем его определить, он принимает функцию, определяет внутри какую-то новую функцию и возвращает её. Таким образом, у нас при применении декоратора, если мы вызовем `decorated`, не вызовется `hello`, потому что вернётся, на самом деле, функция `new_func`, и мы будем вызывать уже функцию `new_func`. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "21fa4573",
"metadata": {},
"outputs": [],
"source": [
"def decorator(func):\n",
" def new_func():\n",
" pass\n",
" \n",
" return new_func\n",
"\n",
"@decorator\n",
"def decorated():\n",
" print(\"Hello!\")\n",
"\n",
"decorated()"
]
},
{
"cell_type": "markdown",
"id": "303da690",
"metadata": {},
"source": [
"Можем это проверить, если посмотрим на её имя. "
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "96066a0b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"new_func\n"
]
}
],
"source": [
"print(decorated.__name__)"
]
},
{
"cell_type": "markdown",
"id": "293001dc",
"metadata": {},
"source": [
"На самом деле, `decorated` — это `new_func`, потому что опять же у нас вот такой вот синтаксис. У нас декоратор вернул другую функцию, и в `decorated` записалась новая функция `new_func`."
]
},
{
"cell_type": "markdown",
"id": "aa2fd676",
"metadata": {},
"source": [
"Важно это понимать. Итак, давайте попробуем сразу перейти к практике, чтобы поподробнее разобраться с тем, как это работает. \n",
"\n",
"И напишем декоратор, который записывает в лог результаты выполнения функции. Вообще для чего обычно используются декораторы? Чаще всего они используются для того, чтобы модифицировать поведение каких-то функций. Часто бывает необходимо использовать один декоратор, для того чтобы какое-то семейство функций переопределить, модифицировать их поведение. \n",
"\n",
"Например, у нас может быть декоратор `login_required`, который мы можем применять к разным функциям и требовать, чтобы в момент исполнения этой функции человек был залогинен на сайте. Или мы можем опять же логировать какую-то информацию, можем считать какие-то метрики, и написав один декоратор, мы можем модифицировать поведение сразу многих функций. Это очень полезный и мощный концепт, который, собственно, мы с вами сейчас рассмотрим.\n",
"\n",
"Итак, у нас декоратор, который просто пишет что-то, что произошло в функции, в файл. Мы вот здесь вот определили наш декоратор, назовём его `logger`, и вот так вот он будет применяться. И теперь при вызове `summator`'а мы хотим, чтобы у нас результат выполнения `summator`'а записывался в лог-файл. Также мы можем этот декоратор применять к любой другой функции. У нас всегда результат выполнения будет записываться в файл вне зависимости от того, какая это функция."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "24f785fd",
"metadata": {},
"outputs": [],
"source": [
"def logger(func):\n",
" ...\n",
"\n",
"@logger\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"# summator([1, 2, 3, 4])"
]
},
{
"cell_type": "markdown",
"id": "7411ba1d",
"metadata": {},
"source": [
"Давайте попробуем это сделать. Итак, мы определили наш декоратор, и нам нужно из него очевидно вернуть какую-то новую функцию. Давайте назовём ее `wrapped`. Обычно функции, которые определены внутри декоратора, называются `wrapped`, `decorated`, `inner`, что-то в этом роде, чтобы понять, что это действительно то новое, чем мы переопределяем изначальную функцию.\n",
"\n",
у нас функция будут принимать `num_list` для начала, также как и наш `summator`, потому что мы заменим наш `summator` на этот `wrapped(num_list)`. Мы потом вернём его, перезаписав `summator`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "33aba2ae",
"metadata": {},
"outputs": [],
"source": [
"def logger(func):\n",
" def wrapped(num_list):\n",
" pass\n",
" \n",
" return wrapped"
]
},
{
"cell_type": "markdown",
"id": "71c217c5",
"metadata": {},
"source": [
"Что же здесь должно происходить? Воспользуемся знакомым вашим концептом замыкания и вызовем нашу функцию, передав ей `num_list`, и, например, откроем файл и запишем в него результат выполнения. Не забудем вернуть `result`, потому что мы хотим, чтобы у нас, на самом деле, всё работало."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "596da402",
"metadata": {},
"outputs": [],
"source": [
"def logger(func):\n",
" def wrapped(num_list):\n",
" result = func(num_list)\n",
" \n",
" with open(\"log.txt\", \"w\") as f:\n",
" f.write(str(result))\n",
" \n",
" return result\n",
" \n",
" return wrapped"
]
},
{
"cell_type": "markdown",
"id": "05bae755",
"metadata": {},
"source": [
"Итак, мы написали наш декоратор. Что здесь происходит? Мы, применяя декоратор, подменяем функцию `summator` новой функцией `wrapped`, и именно она уже будет выполняться. Эта функция новая, она принимает `num_list`, так же как и `summator`, получает результат работы `summator`'а, записывает результат в файл и просто возвращается. Давайте попробуем вызвать summator."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "d9bb65eb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"15"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"@logger\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"summator([1, 2, 3, 4, 5])"
]
},
{
"cell_type": "markdown",
"id": "4a881941",
"metadata": {},
"source": [
"У нас вернулось 15, потому что сумма, действительно, 15. Давайте попробуем открыть файл log.txt и убедиться, что там действительно что-то записано."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "d10ea350",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"15\n"
]
}
],
"source": [
"with open(\"log.txt\", \"r\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"id": "73ca8968",
"metadata": {},
"source": [
"Да, у нас действительно в `log.txt` записано 15. Отлично. У нас появился декоратор, который мы можем применять совершенно к разным функциям, которые принимают `num_list`, и они записывают результаты выполнения в файл.\n",
"\n",
"Так, однако, часто бывает полезно определить декоратор так, чтобы он мог применяться не только к функциям, которые принимают `num_list`, а, например, к функциям, которые принимают любое количество аргументов, любое количество параметров. Как вы могли догадаться, нам нужно нашу функцию определить так, чтобы она принимала любое количество аргументов. Мы делаем это с помощью звёздочек и передаём все аргументы, которые нам сюда пришли в нашу исходную функцию. Всё должно работать точно так же."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "b2bed53b",
"metadata": {},
"outputs": [],
"source": [
"def logger(func):\n",
" def wrapped(*args, **kwargs):\n",
" result = func(*args, **kwargs)\n",
" \n",
" with open(\"log.txt\", \"w\") as f:\n",
" f.write(str(result))\n",
" \n",
" return result\n",
" \n",
" return wrapped"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "046731ea",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"@logger\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"summator([1, 2, 3, 4])"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "4b3feec2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10\n"
]
}
],
"source": [
"with open(\"log.txt\", \"r\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"id": "fbfe35dd",
"metadata": {},
"source": [
"Да, действительно, если мы вызовем `summator` и выведем результат выполнения summator'а, у нас `summator` получил 10 и в файле записалось 10. Всё работает."
]
},
{
"cell_type": "markdown",
"id": "874a38cb",
"metadata": {},
"source": [
"Однако, есть один важный момент, и чтобы понять, чем он неудачен, можно написать, попробовать получить имя `summator`'а. Оказывается, `summator` у нас — это `wrapped`."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "9360cc5f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"wrapped\n"
]
}
],
"source": [
"print(summator.__name__)"
]
},
{
"cell_type": "markdown",
"id": "66eff2ab",
"metadata": {},
"source": [
"Как вы могли помнить, у нас функция подменяется, поэтому, на самом деле, `summator` больше не `summator`. Часто это бывает неудобно, потому что для каких-то методов интроспекции, для отладки важно помнить, в какой функции именно, например, произошло исключение. Для этого существует встроенный модуль `functools` с методом `wraps`, который вам позволяет немного сделать поприятнее, и он подменяет определённые аргументы, `docstring`'и и названия, для того чтобы у нас `summator` остался `summator`'ом, и у нас имя, видите, стало вновь `summator`."
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "88f873c9",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Summator: 21\n",
"summator\n"
]
}
],
"source": [
"import functools\n",
"\n",
"def logger(func):\n",
" @functools.wraps(func)\n",
" def wrapped(*args, **kwargs):\n",
" result = func(*args, **kwargs)\n",
" \n",
" with open(\"log.txt\", \"w\") as f:\n",
" f.write(str(result))\n",
" return result\n",
" \n",
" return wrapped\n",
"\n",
"\n",
"@logger\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"print(f\"Summator: {summator([1, 2, 3, 4, 5, 6])}\")\n",
"print(summator.__name__)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "010a833e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"21\n"
]
}
],
"source": [
"with open(\"log.txt\", \"r\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"id": "be0170f8",
"metadata": {},
"source": [
"Собственно, с этим логгером у нас всё. У нас появился декоратор, который может декорировать функции и записывать их результат в файл.\n",
"\n",
"Давайте, пойдём дальше и напишем более сложный логгер. Так, наша задача будет написать декоратор с параметром, который записывает лог-файл, то есть делает то же самое, однако может принимать в качестве параметра файл, в который нужно записать.\n",
"\n",
"Что же для этого нам нужно сделать? \n",
"\n",
"Давайте попробуем подумать. Есть наш новый декоратор, который принимает уже не функцию, а принимает `filename`. Собственно, записывает в этот `filename` результат выполнения функции.\n",
"\n",
"Что должен вернуть наш декоратор? Он должен вернуть декоратор, то есть, на самом деле, у нас можно рассматривать `logger` не как декоратор, а как просто функцию, которая возвращает декоратор. И возвращает она декоратор, который принимает функцию. Таким образом, мы вызовем `logger` вначале, у нас вернётся декоратор, и потом этот декоратор уже будет применяться к функции `summator`."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "e0bf0f15",
"metadata": {},
"outputs": [],
"source": [
"def logger(filename):\n",
" def decorator(func):\n",
" pass\n",
" \n",
" return decorator\n",
"\n",
"@logger(\"new_log.txt\")\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"# summator([1, 2, 3, 4, 5])"
]
},
{
"cell_type": "markdown",
"id": "c9c12801",
"metadata": {},
"source": [
"Что у нас происходит внутри декоратора? Всё абсолютно то же самое, у нас есть wrapped или decorated, мы можем назвать, который принимает какое-то количество аргументов, нам не важно, какое. Собственно, наш декоратор потом её возвращает."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "e436af25",
"metadata": {},
"outputs": [],
"source": [
"def logger(filename):\n",
" def decorator(func):\n",
" def wrapped(*args, **kwargs):\n",
" pass\n",
" \n",
" return wrapped\n",
" \n",
" return decorator\n",
"\n",
"@logger(\"new_log.txt\")\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"# summator([1, 2, 3, 4, 5])"
]
},
{
"cell_type": "markdown",
"id": "241d2f1f",
"metadata": {},
"source": [
"Отлично. Что же происходит внутри? У нас выполняется наша функция, записывается всё в `result`, в функцию мы передаём аргументы её. И, обратите внимание, когда мы открываем файл, мы уже используем не `log.txt`, а `filename` с верхнего уровня с знакомым вам замыканием, и записываем в `filename` результаты выполнения функции. Да, и не забудем, конечно, вернуть `result`, чтобы всё работало."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "7c793e93",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"21"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def logger(filename):\n",
" def decorator(func):\n",
" def wrapped(*args, **kwargs):\n",
" result = func(*args, **kwargs)\n",
" \n",
" with open(filename, \"w\") as f:\n",
" f.write(str(result))\n",
" \n",
" return result\n",
" \n",
" return wrapped\n",
" \n",
" return decorator\n",
"\n",
"@logger(\"new_log.txt\")\n",
"def summator(num_list):\n",
" return sum(num_list)\n",
"\n",
"summator([1, 2, 3, 4, 5, 6])"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "622ec4a2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"21\n"
]
}
],
"source": [
"with open(\"new_log.txt\", \"r\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"id": "39ff16bd",
"metadata": {},
"source": [
"Так, давайте попробуем запустить. У нас 21, и откроем `new_log.txt` и проверим, сколько там записалось. Да, у нас в `new_log.txt` 21, а значит произошло ровно то, что мы хотели. Давайте посмотрим более внимательно на этот пример. Что здесь происходит? У нас применяется декоратор к функции `summator`. Вызывается функция `logger`, которой мы передаём `filename`. Функция `logger` возвращает декоратор, который потом уже с помощью `@` применяется по знакомому вам правилу. Этот декоратор принимает функцию, в данном случае это `summator`, и возвращает новую функцию `wrapped`, которая подменяет наш `summator`, и потом вызывается именно она. И внутри этой функции вызывается исходная функция, то есть `summator`, и записывается результат её выполнения в файл. Ничего сложного, и можно написать аналог. Для синтаксического сахара это выглядит как-то так."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "b2f9d4ab",
"metadata": {},
"outputs": [],
"source": [
"summator = logger(\"log.txt\")(summator)"
]
},
{
"cell_type": "markdown",
"id": "e7a339c4",
"metadata": {},
"source": [
"У нас есть `logger`, который принимает `log.txt`, возвращает декоратор, который принимает summator и возвращает новую функцию. На самом деле, происходит что-то в этом роде.\n",
"\n",
"Итак, мы с вами рассмотрели, пожалуй, самый сложный пример на декораторы, и часто бывают именно с такими примерами проблемы. Разобраться в том, какой декоратор что принимает, а что возвращает, довольно сложно. Пожалуйста, обратите на это внимание. Это очень важный концепт, который правда очень часто используется."
]
},
{
"cell_type": "markdown",
"id": "a3a7cea3",
"metadata": {},
"source": [
"И последний пример на декораторы. Давайте посмотрим, что будет, если применить сразу несколько декораторов. Декораторы можно чейнить, то есть создавать цепочки из декораторов, определяя сразу несколько декораторов один друг за другом. Например, нам нужно модифицировать как-то поведение функции в каких-то нескольких плоскостях, например, мы можем проверять, залогинен ли пользователь и переданы ли какие-то данные, необходимые для того, чтобы происходила дальше транзакция какая-то или операция. \n",
"\n",
"Определим два простых декоратора, первый декоратор и второй декоратор, который просто принтит и вызывает функцию."
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "11336dfb",
"metadata": {},
"outputs": [],
"source": [
"def first_decorator(func):\n",
" def wrapped():\n",
" print(\"Inside first_decorator product\")\n",
" return func()\n",
" \n",
" return wrapped\n",
"\n",
"def second_decorator(func):\n",
" def wrapped():\n",
" print(\"Inside second_decorator product\")\n",
" return func()\n",
" \n",
" return wrapped"
]
},
{
"cell_type": "markdown",
"id": "58cdcbc2",
"metadata": {},
"source": [
"Применим наши декораторы к функции `decorated` и посмотрим, в каком порядке они вызовутся."
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "e4d147dd",
"metadata": {},
"outputs": [],
"source": [
"@first_decorator\n",
"@second_decorator\n",
"def decorated():\n",
" print(\"Finally called...\")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "1a98d952",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Inside first_decorator product\n",
"Inside second_decorator product\n",
"Finally called...\n"
]
}
],
"source": [
"decorated()"
]
},
{
"cell_type": "markdown",
"id": "0861caa2",
"metadata": {},
"source": [
"У нас вначале вызвался первый декоратор, потом — второй декоратор. Почему так произошло? Чтобы понять почему, можно написать опять же в простом виде без синтаксического сахара и разобраться, что у нас происходит при применении декораторов."
]
},
{
"cell_type": "code",
"execution_count": 39,
"id": "a05eacb4",
"metadata": {},
"outputs": [],
"source": [
"decorated = first_decorator(second_decorator(decorated))"
]
},
{
"cell_type": "markdown",
"id": "6542fd87",
"metadata": {},
"source": [
"Вначале у нас вызывается функция `second_decorator`, которая возвращает новую функцию `wrapped`, таким образом, у нас в целом подменяется функция `wrap` внутри `second_dercorator`'а. После этого вызывается `first_decorator`, который принимает функцию полученную из `second_decorator`'а `wrapped` и возвращает ещё одну функцию `wrapped`, заменяя `decorated` на неё. Таким образом, у нас итоговая функция `decorated` — это вот эта вот функция, вызывающая функцию из `second_decorator`'а. Когда у нас вызывается `decorated`, у нас вначале пишется `print`, потом вызывается вот эта вот функция, которая на самом деле попала сюда из `second_decorator`'а, и потом принтится `second_decorator`. \n",
"\n",
"Будьте с этим внимательны. Порядок применения декораторов очень важен, и от него часто зависит поведение.\n",
"\n",
"И ещё один пример на чейн декораторов немного другой. Если мы опишем два декоратора `bold` и `italic` и попробуем, например, отформатировать текст, окажется, что у нас `italic` внутри `bold`'а, хотя, казалось бы, у нас только что декоратор выполнялся сверху вниз, а здесь - снизу вверх."
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "72d0d6d5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<b><i>hello world</i></b>\n"
]
}
],
"source": [
"def bold(func):\n",
" def wrapped():\n",
" return \"<b>\" + func() + \"</b>\"\n",
" \n",
" return wrapped\n",
"\n",
"\n",
"def italic(func):\n",
" def wrapped():\n",
" return \"<i>\" + func() + \"</i>\"\n",
" \n",
" return wrapped\n",
"\n",
"@bold\n",
"@italic\n",
"def hello():\n",
" return \"hello world\"\n",
"\n",
"# hello = bold(italic(hello))\n",
"print(hello())"
]
},
{
"cell_type": "markdown",
"id": "bab7df7e",
"metadata": {},
"source": [
"То же самое, чтобы разобраться, что здесь происходит, нужно просто записать это в классическом виде и посмотреть. У нас вначале вызывается функция `italic`, которая возвращает `wrapped`, потом вызывается `bold`, и именно вот функция `wrapped` из `bold`'а записывается в `hello`. Она и будет вызываться. У нас вызывается `hello`, вызывается функция `wrapped`, которая внутри себя возвращает вот такой вот объект. \n",
"\n",
"Что происходит? Чтобы создать этот объект, нам нужно вызвать функцию вот эту вот. А функция `func` в данном случае, на самом деле, эта функция из `italic`'а. И именно поэтому у нас вначале внутри написан тег `i`, а не `b`, как вы могли подумать, читая сверху вниз.\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,47 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "bf5fb96a",
"metadata": {},
"source": [
"# Документация #"
]
},
{
"cell_type": "markdown",
"id": "73f4d123",
"metadata": {},
"source": [
"- [Туториал по функциям из документации](https://docs.python.org/3/tutorial/controlflow.html#defining-functions \"4. More Control Flow Tools\")\n",
"- [Работа с файлами](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files \"7. Input and Output\")\n",
"- [Встроенные функции](https://docs.python.org/3/library/functions.html \"2. Built-in Functions\")\n",
"- [Сортировка](https://docs.python.org/3/howto/sorting.html \"Sorting HOW TO\")\n",
"- [Функциональное программирование](https://docs.python.org/3/howto/functional.html \"Functional Programming HOWTO\")\n",
"- [Модуль functools](https://docs.python.org/3/library/functools.html \"10.2. functools — Higher-order functions and operations on callable objects\")\n",
"- [Декораторы](https://www.codeschool.com/blog/2016/05/12/a-guide-to-python-decorators/ \"A Guide to Python Decorators\")"
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,200 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4a1dd7c8",
"metadata": {},
"source": [
"# Задание. Key-value хранилище #"
]
},
{
"cell_type": "markdown",
"id": "4ba56dc0",
"metadata": {},
"source": [
"В этом блоке мы с вами реализуем собственный `key-value storage`. Вашей задачей будет написать скрипт, который принимает в качестве аргументов ключи и значения и выводит информацию из хранилища (в нашем случае — из файла)."
]
},
{
"cell_type": "markdown",
"id": "2cdd1fd1",
"metadata": {},
"source": [
"Запись значения по ключу"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f72ba88b",
"metadata": {},
"outputs": [],
"source": [
"! python storage.py --key key1 --val value1"
]
},
{
"cell_type": "markdown",
"id": "3ecc5e82",
"metadata": {},
"source": [
"Получение значения по ключу.\n",
"\n",
"Ответом в данном случае будет вывод с помощью print соответствующего значения:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "4d251cca",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"value1\r\n"
]
}
],
"source": [
"! python storage.py --key key1"
]
},
{
"cell_type": "markdown",
"id": "91c57474",
"metadata": {},
"source": [
"или"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "8346149f",
"metadata": {},
"outputs": [],
"source": [
"! python storage.py --key key1 --val value2"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "c6e52029",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"value1, value2\r\n"
]
}
],
"source": [
"! python storage.py --key key1"
]
},
{
"cell_type": "markdown",
"id": "e1e413c0",
"metadata": {},
"source": [
"Если значений по этому ключу было записано несколько. Метрики сохраняйте в порядке их добавления. Обратите внимание на пробел после запятой."
]
},
{
"cell_type": "markdown",
"id": "c16d0e67",
"metadata": {},
"source": [
"Если значений по ключу не было найдено, выводите пустую строку или `None`."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "520ff264",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"None\r\n"
]
}
],
"source": [
"! python storage.py --key key2"
]
},
{
"cell_type": "markdown",
"id": "0646920d",
"metadata": {},
"source": [
"Для работы с аргументами командной строки используйте модуль [argparse](https://docs.python.org/3/howto/argparse.html \"Argparse Tutorial\"). Вашей задачей будет считать аргументы, переданные вашей программе, и записать соответствующую пару ключ-значение в файл хранилища или вывести значения, если был передан только ключ. Хранить данные вы можете в формате **JSON** с помощью стандартного модуля [json](https://docs.python.org/3/library/json.html \"19.2. json — JSON encoder and decoder\"). Проверьте добавление нескольких ключей и разных значений.\n",
"\n",
"Файл следует создавать с помощью модуля [tempfile](https://docs.python.org/3/library/tempfile.html \"11.6. tempfile — Generate temporary files and directories\")."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "0fbd81cf",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import tempfile\n",
"\n",
"storage_path = os.path.join(tempfile.gettempdir(), \"storage.data\")\n",
"with open(storage_path, \"w\") as f:\n",
" ..."
]
},
{
"cell_type": "markdown",
"id": "de5a5212",
"metadata": {},
"source": [
"Создайте скрипт хранилища."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "2a4a9396",
"metadata": {},
"outputs": [],
"source": [
"! python storage.py --clear"
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,105 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b656cccd",
"metadata": {},
"source": [
"# Задание. Декоратор to_json #"
]
},
{
"cell_type": "markdown",
"id": "4f319165",
"metadata": {},
"source": [
"Чтобы передавать данные между функциями, модулями или разными системами используются форматы данных. Одним из самых популярных форматов является `JSON`. Напишите декоратор `to_json`, который можно применить к различным функциям, чтобы преобразовывать их возвращаемое значение в `JSON`-формат. Не забудьте про сохранение корректного имени декорируемой функции.\n",
"\n",
"```python\n",
"@to_json\n",
"def get_data():\n",
" return {\n",
" \"data\": 42\n",
" }\n",
" \n",
"get_data() # вернёт '{\"data\": 42}'\n",
"```\n",
"\n",
"Опишите декоратор в файле."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "41266401",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"data\": 42}'"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import json\n",
"\n",
"json.dumps({\"data\": 42})"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "993cc205",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{\"data\": 42}'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from to_json import to_json\n",
"\n",
"@to_json\n",
"def get_data():\n",
" return {\n",
" \"data\": 42\n",
" }\n",
"\n",
"get_data()"
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,122 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "88beaeb4",
"metadata": {},
"source": [
"# Тест по блоку #"
]
},
{
"cell_type": "markdown",
"id": "7aafcb76",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "a30f04bc",
"metadata": {},
"source": [
"##### 1. Можно ли использовать изменяемые объекты в качестве значений по умолчанию в функциях?\n",
"\n",
"- [ ] Нет, случится синтаксическая ошибка\n",
"- [ ] Да, но это может привести к неочевидным ошибкам"
]
},
{
"cell_type": "markdown",
"id": "791c8018",
"metadata": {},
"source": [
"##### 2. Выберите верные утверждения про кортежи:\n",
"\n",
"- [ ] кортежи изменяемые\n",
"- [ ] кортежи могут содержать элементы различных типов\n",
"- [ ] проверка на вхождение элемента в кортеж происходит за константное время\n",
"- [ ] проверка на вхождение элемента в кортеж происходит за линейное время\n",
"- [ ] кортежи неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "69434533",
"metadata": {},
"source": [
"##### 3. Какой записи эквивалентно применение декоратора?\n",
"\n",
"```python\n",
"@login_required\n",
"def send_feedback(request)\n",
"```\n",
"\n",
"- [ ] `send_feedback = login_required(send_feedback)`\n",
"- [ ] `def login_required(send_feedback)`\n",
"- [ ] `login_required = send_feedback(login_required)`\n",
"- [ ] `def login_required(send_feedback)(request)`"
]
},
{
"cell_type": "markdown",
"id": "b11c0bb1",
"metadata": {},
"source": [
"##### 4. Для чего используются декораторы?\n",
"\n",
"- [ ] Чтобы иметь возможность импортировать функцию в другой модуль\n",
"- [ ] Для эффективного использования памяти при итерации\n",
"- [ ] Для модификации поведения функций"
]
},
{
"cell_type": "markdown",
"id": "acae95b5",
"metadata": {},
"source": [
"##### 5. Выберите верные утверждения про множества:\n",
"\n",
"- [ ] проверка на вхождение элемента в множество происходит за константное время\n",
"- [ ] множества изменяемые\n",
"- [ ] проверка на вхождение элемента в множество происходит за линейное время\n",
"- [ ] множества неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "7a359014",
"metadata": {},
"source": [
"##### 6. Что происходит при итерации по генератору?\n",
"\n",
"- [ ] Каждую итерацию вызывается функция `next`, и генератор исполняется с начала\n",
"- [ ] Итерация происходит по списку значений, который вернул генератор при вызове\n",
"- [ ] Каждую итерацию вызывается функция `next`, и исполнение генератора возобновляется с момента после `yield`"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,113 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "88beaeb4",
"metadata": {},
"source": [
"# Тест по блоку #"
]
},
{
"cell_type": "markdown",
"id": "178d0d2a",
"metadata": {},
"source": [
"1. Можно ли использовать изменяемые объекты в качестве значений по умолчанию в функциях?\n",
"\n",
"- [ ] Нет, случится синтаксическая ошибка\n",
"- [x] Да, но это может привести к неочевидным ошибкам"
]
},
{
"cell_type": "markdown",
"id": "824d16df",
"metadata": {},
"source": [
"2. Выберите верные утверждения про кортежи:\n",
"\n",
"- [ ] кортежи изменяемые\n",
"- [x] кортежи могут содержать элементы различных типов\n",
"- [ ] проверка на вхождение элемента в кортеж происходит за константное время\n",
"- [x] проверка на вхождение элемента в кортеж происходит за линейное время\n",
"- [x] кортежи неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "db457384",
"metadata": {},
"source": [
"3. Какой записи эквивалентно применение декоратора?\n",
"\n",
"```python\n",
"@login_required\n",
"def send_feedback(request)\n",
"```\n",
"\n",
"- [x] `send_feedback = login_required(send_feedback)`\n",
"- [ ] `def login_required(send_feedback)`\n",
"- [ ] `login_required = send_feedback(login_required)`\n",
"- [ ] `def login_required(send_feedback)(request)`"
]
},
{
"cell_type": "markdown",
"id": "3fd4a5b6",
"metadata": {},
"source": [
"4. Для чего используются декораторы?\n",
"\n",
"- [ ] Чтобы иметь возможность импортировать функцию в другой модуль\n",
"- [ ] Для эффективного использования памяти при итерации\n",
"- [x] Для модификации поведения функций"
]
},
{
"cell_type": "markdown",
"id": "659228ff",
"metadata": {},
"source": [
"5. Выберите верные утверждения про множества:\n",
"\n",
"- [x] проверка на вхождение элемента в множество происходит за константное время\n",
"- [x] множества изменяемые\n",
"- [ ] проверка на вхождение элемента в множество происходит за линейное время\n",
"- [ ] множества неизменяемые"
]
},
{
"cell_type": "markdown",
"id": "bb5b6db9",
"metadata": {},
"source": [
"6. Что происходит при итерации по генератору?\n",
"\n",
"- [ ] Каждую итерацию вызывается функция `next`, и генератор исполняется с начала\n",
"- [ ] Итерация происходит по списку значений, который вернул генератор при вызове\n",
"- [x] Каждую итерацию вызывается функция `next`, и исполнение генератора возобновляется с момента после `yield`"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,98 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d37f7d4c",
"metadata": {},
"source": [
"# Тест по функциям #"
]
},
{
"cell_type": "markdown",
"id": "d10fac6b",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "c81785e7",
"metadata": {},
"source": [
"##### 1. Что по умолчанию возвращает функция, где не определен `return`?\n",
"\n",
"- [ ] 0\n",
"- [ ] В каждой функции необходимо использовать оператор `return`\n",
"- [ ] `None`\n",
"- [ ] 1"
]
},
{
"cell_type": "markdown",
"id": "1301ae95",
"metadata": {},
"source": [
"##### 2. Как оформляется тело функции в **Python**?\n",
"\n",
"- [ ] Операторами `BEGIN-END`\n",
"- [ ] Отступом\n",
"- [ ] Фигурными скобками\n",
"- [ ] Квадратными скобками"
]
},
{
"cell_type": "markdown",
"id": "a52fd238",
"metadata": {},
"source": [
"##### 3. Что произойдет при вызове функции `foo`?\n",
"\n",
"```python\n",
"def foo(*args, **kwargs): pass\n",
"```\n",
"\n",
"- [ ] Все именованные аргументы запишутся в кортеж `kwargs`\n",
"- [ ] Все именованные аргументы запишутся в словарь `kwargs`\n",
"- [ ] Синтаксическая ошибка\n",
"- [ ] Все позиционные аргументы запишутся в кортеж `args`\n"
]
},
{
"cell_type": "markdown",
"id": "2384b8b4",
"metadata": {},
"source": [
"##### 4. В каком случае можно вызвать функцию без параметров?\n",
"\n",
"- [ ] Если используются `*args`, `**kwargs`\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,89 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d37f7d4c",
"metadata": {},
"source": [
"# Тест по функциям #"
]
},
{
"cell_type": "markdown",
"id": "c81785e7",
"metadata": {},
"source": [
"1. Что по умолчанию возвращает функция, где не определен `return`?\n",
"\n",
"- [ ] 0\n",
"- [ ] В каждой функции необходимо использовать оператор `return`\n",
"- [x] `None`\n",
"- [ ] 1"
]
},
{
"cell_type": "markdown",
"id": "1301ae95",
"metadata": {},
"source": [
"2. Как оформляется тело функции в **Python**?\n",
"\n",
"- [ ] Операторами `BEGIN-END`\n",
"- [x] Отступом\n",
"- [ ] Фигурными скобками\n",
"- [ ] Квадратными скобками"
]
},
{
"cell_type": "markdown",
"id": "a52fd238",
"metadata": {},
"source": [
"3. Что произойдет при вызове функции `foo`?\n",
"\n",
"```python\n",
"def foo(*args, **kwargs): pass\n",
"```\n",
"\n",
"- [ ] Все именованные аргументы запишутся в кортеж `kwargs`\n",
"- [x] Все именованные аргументы запишутся в словарь `kwargs`\n",
"- [ ] Синтаксическая ошибка\n",
"- [x] Все позиционные аргументы запишутся в кортеж `args`\n"
]
},
{
"cell_type": "markdown",
"id": "2384b8b4",
"metadata": {},
"source": [
"4. В каком случае можно вызвать функцию без параметров?\n",
"\n",
"- [x] Если используются `*args`, `**kwargs`\n",
"- [ ] У каждой функции должны быть параметры, в этом суть функций\n",
"- [x] Если у всех аргументов есть значения по умолчанию\n",
"- [x] Если она не ожидает аргументов"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,378 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "be7bc6cb",
"metadata": {},
"source": [
"# Файлы #"
]
},
{
"cell_type": "markdown",
"id": "cc8b2eae",
"metadata": {},
"source": [
"На этой лекции мы поговорим с вами о файлах и том, как с ними работать.\n",
"\n",
"С файлами, на самом деле, в Python'е никаких проблем не должно возникнуть.\n",
"Все довольно просто. Чтобы открыть файлы мы используем встроенный метод `open`, встроенную функцию `open`, и передаем ей `filename`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5974cb39",
"metadata": {},
"outputs": [],
"source": [
"f = open(\"filename\")"
]
},
{
"cell_type": "markdown",
"id": "aee8d1e2",
"metadata": {},
"source": [
"У нас возвращается файловый объект, с которым мы потом можем работать, для того чтобы записывать данные или читать данные из файлов. \n",
"\n",
"Можно их открывать на запись, на чтение, на чтение и запись, и на дозапись. Делается это с помощью модов, которым мы тоже придаем функцию `open`. Например, `a` — это дозапись, `w` — это, очевидно, запись, `r` — это прочтение, `r+` — это запись и чтение одновременно. Точно так же мы можем открывать файл в бинарном виде, то есть работать с бинарными данными, и происходит все аналогично обычному типу. Мы просто добавляем в мод букву `b`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3584b6c2",
"metadata": {},
"outputs": [],
"source": [
"text_modes = [\"r\", \"w\", \"a\", \"r+\"]\n",
"binary_modes = [\"br\", \"bw\", \"ba\", \"br+\"]"
]
},
{
"cell_type": "markdown",
"id": "83c0ce60",
"metadata": {},
"source": [
"Чтобы открыть файл, например, на запись, мы передаем мод `w` в функцию `open`. Чтобы записать данный файл, мы берем наш файловый объект и пользуемся методом файла `write`, передавая туда строку. В данном случае мы передаем строку, и наш метод `write` возвращает количество символов, которые мы записали. Если это будет байтовая информация, то, собственно, это будет не количество символов, а количество байт. Чтобы закрыть файл, нам нужно вызвать метод `close`, и файл нужно обязательно закрывать."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "2b25c2b5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"47"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f = open(\"filename\", \"w\")\n",
"\n",
"f.write(\"The world is changed.\\nI taste it in the water.\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "ebc3244a",
"metadata": {},
"outputs": [],
"source": [
"f.close()"
]
},
{
"cell_type": "markdown",
"id": "16383eec",
"metadata": {},
"source": [
"Если вы откроете файл и не закроете файл, может произойти что-нибудь страшное и ужасное. На самом деле нет, но принято файлы закрывать, и мы поговорим с вами о том, как с этим работать. Итак, чтобы открыть файл на чтение и запись, нам нужно использовать `r+`, и мы можем читать данные из файла с помощью метода `read`. `Read` по умолчанию читает столько, сколько сможет. Если файл не поместится в памяти, то это ваши проблемы. Вы также можете указать в методе `read` конкретное количество информации, которое вы хотите прочитать, передав `size`, но по умолчанию читается весь файл и выводится, собственно."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9d740b8c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'The world is changed.\\nI taste it in the water.\\n'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f = open(\"filename\", \"r+\")\n",
"f.read()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "aee2da15",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"49"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f.tell()"
]
},
{
"cell_type": "markdown",
"id": "1d4c7a2b",
"metadata": {},
"source": [
"Здесь мы выводим все строчки, которые туда записали, и видим, что на самом деле информация в файл попала. Очень важный момент. Когда мы прочитали весь файл, у нас указатель того, где мы сейчас находимся в файле — в самом конце. В данном случае это 47 символов. Если мы попробуем прочитать еще раз, то мы ничего не найдем, потому что мы весь файл уже прочитали и у нас пойнтер в самом конце."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "cf719f78",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"''"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f.read()"
]
},
{
"cell_type": "markdown",
"id": "9b0798a6",
"metadata": {},
"source": [
"Для того чтобы прочитать его, например, заново, нам нужно использовать метод `seek` и перенести указатель на начало файла. Как вы видите, он равен нулю, и теперь мы можем действительно читать данные заново и, например, опять же закрыть файл, потому что файлы всегда нужно закрывать."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "57b6f5bb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f.seek(0)\n",
"f.tell()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "4ce93652",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The world is changed.\n",
"I taste it in the water.\n",
"\n"
]
}
],
"source": [
"print(f.read())\n",
"f.close()"
]
},
{
"cell_type": "markdown",
"id": "8109309b",
"metadata": {},
"source": [
"Собственно, кроме обычного метода `read`, есть также полезные методы для работы со строками, которые позволяют вам читать строки, одну, например, или несколько.\n",
"\n",
"Чтобы прочитать конкретно одну строку, можно использовать метод `readline` у того же файлового объекта, и метод вернет строку, разбив по символу переноса строки. Как видите, у нас вернулась только первая строка без второй."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "1a2fd52d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The world is changed.\n",
"\n"
]
}
],
"source": [
"f = open(\"filename\", \"r+\")\n",
"print(f.readline())\n",
"f.close()"
]
},
{
"cell_type": "markdown",
"id": "16da382f",
"metadata": {},
"source": [
"Если вам интересно прочитать сразу все строки, разбить их по символу переноса строки и записать все это, например, в список, можно использовать метод `readlines`. Он сделает именно это — вернет список строк, разбитых по символу перевода строки."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "1d65e32e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['The world is changed.\\n', 'I taste it in the water.\\n']"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f = open(\"filename\", \"r+\")\n",
"f.readlines()"
]
},
{
"cell_type": "markdown",
"id": "9fb151d1",
"metadata": {},
"source": [
"Если мы закроем файл и попробуем его прочитать, очевидно, у нас ничего не получится, потому что закрытый файл прочитать нельзя. Будьте внимательны."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "13c0a8d3",
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "I/O operation on closed file.",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-14-bf012af3c351>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mread\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mValueError\u001b[0m: I/O operation on closed file."
]
}
],
"source": [
"f.close()\n",
"f.read()"
]
},
{
"cell_type": "markdown",
"id": "6ac70071",
"metadata": {},
"source": [
"На самом деле, я рекомендую вам работать с файлами немного другим образом. Существует специальная вещь, называемая контекстный менеджер, который вам позволяет не заботиться о закрытии файлов. Вы можете открыть файл с помощью оператора `with`, и записать в файловый объект переменную `f` и потом работать с ним внутри этого контекстного блока и не закрывать его, потому что контекстный менеджер заботится об этом за вас."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "ed64d12d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The world is changed.\n",
"I taste it in the water.\n",
"\n"
]
}
],
"source": [
"with open(\"filename\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"id": "7318dd16",
"metadata": {},
"source": [
"Про контекстный менеджер мы поговорим позднее, а пока просто можете использовать эту конструкцию и не заботиться о том, чтобы закрывать файл.\n",
"\n",
"Итак, с файлами в Python работать довольно просто. У нас есть файловые объекты, мы можем в них писать, из них читать. Не забывайте закрывать файлы или используйте контекстный менеджер."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,741 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d8cf2624",
"metadata": {},
"source": [
"# Функции #"
]
},
{
"cell_type": "markdown",
"id": "f57336b3",
"metadata": {},
"source": [
"Привет. На этой лекции мы с вами поговорим о функциях и о том, как с ними работать в языке Python.\n",
"\n",
"Скорее всего, вы уже встречались с функциями в других языках программирования и знаете, что функция - это просто блок кода, который можно переиспользовать несколько раз в разных местах программы.\n",
"\n",
"Мы можем вызывать функцию с какими-то аргументами и получать значения обратно.\n",
"\n",
"Чтобы определить функцию в языке Python, нужно использовать литерал `def` и с помощью отступа определить блок кода функции. В данном случае мы определяем функцию `get_seconds`, которая просто возвращает количество секунд на данный момент."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8cb5198b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"32"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from datetime import datetime\n",
"\n",
"def get_seconds():\n",
" \"\"\"Return current seconds\"\"\"\n",
" return datetime.now().second\n",
"\n",
"get_seconds()"
]
},
{
"cell_type": "markdown",
"id": "11105ce4",
"metadata": {},
"source": [
"Обратите внимание, функцию я определяю с помощью нижнего подчеркивания по `PEP8`. А дальше у функции может идти документационная строка, которая описывает то, что, собственно, внутри функции происходит. Чтобы вернуть значения из функции, мы используем `return`, в данном случае возвращаем количество секунд, а можем не писать `return`, и тогда по умолчанию вернется `None`."
]
},
{
"cell_type": "markdown",
"id": "b58ccdf2",
"metadata": {},
"source": [
"Чтобы вызвать функцию, нужно использовать круглые скобочки, и если нужно, передать туда параметры. Чтобы получить документационную строку, можно обратиться к атрибуту `__doc__`, а имя функции получается с помощью атрибута `__name__`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9a6d8edf",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Return current seconds'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_seconds.__doc__"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b148cbf7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'get_seconds'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_seconds.__name__"
]
},
{
"cell_type": "markdown",
"id": "e378fc9d",
"metadata": {},
"source": [
"Чаще всего функция определяется именно с параметрами, потому что нам важно передать какие-то значения внутри функции и работать уже с ними. Мы определяем функцию `split_tags`, которая принимает какой-то `tag_string`, в данном случае это строка с тегами, например, это `python`, `perl` и `pascal`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5ad43014",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['python', 'perl', 'pascal']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def split_tags(tag_string):\n",
" tag_list = []\n",
" \n",
" for tag in tag_string.split(\",\"):\n",
" tag_list.append(tag.strip())\n",
" \n",
" return tag_list\n",
"\n",
"split_tags(\"python, perl, pascal\")"
]
},
{
"cell_type": "markdown",
"id": "a17c8dec",
"metadata": {},
"source": [
"Мы хотим разбить эту строку по запятым и вернуть список тегов. Эта функция, собственно, это и делает. Мы передаем в нашу функцию строку и получаем обратно список. Если мы попытаемся вызвать эту функцию без параметров, у нас выпадет ошибка `TypeError`, потому что функция ожидает параметр и не знает, что делать, если его нет."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "cc8143dd",
"metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "split_tags() missing 1 required positional argument: 'tag_string'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-5-459911d1e387>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msplit_tags\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m: split_tags() missing 1 required positional argument: 'tag_string'"
]
}
],
"source": [
"split_tags()"
]
},
{
"cell_type": "markdown",
"id": "96584d83",
"metadata": {},
"source": [
"Как вы могли заметить, мы не указываем явно, какого типа параметры функция ожидает, потому что Python - это динамический язык.\n",
"\n",
"Если вы уже сталкивались со статически типизированными языками вроде C, вы могли видеть, что, например, в C типы аннотируются, мы явно указываем, какого типа должен быть параметр функции и какого типа возвращаемые значения.\n",
"\n",
"[Aннотация типов](Aннотация%20типов.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "6edf6fa1",
"metadata": {},
"source": [
"Как же передаются параметры в наши функции? Например, в статических языках, опять же, вроде C, проводится очень четкое различие между передачей по ссылке и по значению.\n",
"\n",
"В Python'е каждая переменная является связью имени с объектом в памяти. И именно это имя, именно эта ссылка на объект передается в функцию."
]
},
{
"cell_type": "markdown",
"id": "a080fbf3",
"metadata": {},
"source": [
"Таким образом, если мы определим функцию `extender`, которая принимает какой-то `source_list`, то есть исходный `list`, и новый `list` и пытается расширить `source_list` с помощью `extend_list`'а, то есть просто добавить в конец все его элементы, мы вот определяем `values` и пытаемся расширить `values` с помощью `[4, 5, 6]`, то окажется, что `values` у нас изменится."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "65c8b6ed",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3, 4, 5, 6]\n"
]
}
],
"source": [
"def extender(source_list, extend_list):\n",
" source_list.extend(extend_list)\n",
" \n",
"values = [1, 2, 3]\n",
"extender(values, [4, 5, 6])\n",
"\n",
"print(values)"
]
},
{
"cell_type": "markdown",
"id": "883ff05c",
"metadata": {},
"source": [
"Что же произошло? Наша ссылка на объект в памяти попала в `extender`. И так как `list` является изменяемым объектом, мы можем изменить этот `list`, и объект в памяти изменился. Наш `values` в глобальном `scope` поменял свое значение. Если мы попытаемся как-то изменить неизменяемое значение, в данном случае `tuple`, то у нас ничего не получится, потому что мы передаем ссылку на объект в памяти, но объект является неизменяемым."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "2f508089",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('Guido', '31/01')\n"
]
}
],
"source": [
"def replacer(source_tuple, replace_with):\n",
" source_tuple = replace_with\n",
" \n",
"user_info = (\"Guido\", \"31/01\")\n",
"replacer(user_info, (\"Larry\", \"27/09\"))\n",
"\n",
"print(user_info)"
]
},
{
"cell_type": "markdown",
"id": "06ae74a9",
"metadata": {},
"source": [
"В данном случае у нас `user_info` осталось неизменным. Однако стоит быть внимательным с изменением каких-то глобальных переменных внутри функции. Это является плохим тоном, и не стоит так программировать, потому что часто бывает не очевидно, если вы вызываете какую-то функцию, а объект в глобальном `scope` изменяется. Используйте возвращаемое значение и не путайте других программистов."
]
},
{
"cell_type": "markdown",
"id": "0ff0ab4f",
"metadata": {},
"source": [
"В Python'е также существуют именованные аргументы, которые иногда бывают полезны. Например, мы можем определить функцию `say`, которая принимает два аргумента — `greeting` и `name`, просто приветствует какого-то человека.\n",
"\n",
"Однако мы можем также передать эти параметры в другом порядке, проименовав их с помощью `name` и `greeting`. Таким образом, мы передаем вначале имя, а потом `greeting`, тем не менее у нас всё работает точно так же."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "2580a8b3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello Kitty!\n",
"Hello Kitty!\n"
]
}
],
"source": [
"def say(greeting, name):\n",
" print(f\"{greeting} {name}!\")\n",
" \n",
"say(\"Hello\", \"Kitty\")\n",
"say(name=\"Kitty\", greeting=\"Hello\")"
]
},
{
"cell_type": "markdown",
"id": "0bbd0c13",
"metadata": {},
"source": [
"Немножко про область видимости. Важно понимать, что переменные, объявленные вне области видимости функции, нельзя изменять. В данном случае у нас есть глобальная переменная `result`, и мы пытаемся прибавить к ней внутри функции какое-то значение. Мы пытаемся к `result` прибавить единичку при вызове функции `increment`. У нас ничего не получается, падает ошибка, потому что мы не можем внутри функции изменять объекты из глобальной области видимости."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "1b46f831",
"metadata": {},
"outputs": [
{
"ename": "UnboundLocalError",
"evalue": "local variable 'result' referenced before assignment",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mUnboundLocalError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-20-b35f2869d543>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mincrement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32m<ipython-input-20-b35f2869d543>\u001b[0m in \u001b[0;36mincrement\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mincrement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mresult\u001b[0m \u001b[1;33m+=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 5\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mUnboundLocalError\u001b[0m: local variable 'result' referenced before assignment"
]
}
],
"source": [
"result = 0\n",
"\n",
"def increment():\n",
" result += 1\n",
" return result\n",
"\n",
"print(increment())"
]
},
{
"cell_type": "markdown",
"id": "cc5fa177",
"metadata": {},
"source": [
"И очень важно это соблюдать опять же по той же самой причине. Если мы внутри функции изменяем какие-то глобальные объекты, очень часто это не очевидно. У нас есть глобальная переменная, и вызов каких-то функций меняет ее значение. Совершенно непонятно, что это происходит и часто приводит к запутанному коду."
]
},
{
"cell_type": "markdown",
"id": "5a903105",
"metadata": {},
"source": [
"Существует в Python'е возможность изменять глобальные переменные с помощью `global`, например, или `non local`, но я не рекомендую вам использовать эти особенности."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "f60a05d7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"result = 0\n",
"\n",
"def increment():\n",
" global result\n",
" \n",
" result += 1\n",
" return result\n",
"\n",
"print(increment())"
]
},
{
"cell_type": "markdown",
"id": "4fe3001d",
"metadata": {},
"source": [
"Существует также возможность использовать аргументы по умолчанию вашей функции. Часто у вас бывают какие-то аргументы, которые можно передавать, а можно не передавать. И у них, у этих аргументов, могут быть какие-то дефолтные значения. В данном случаем мы можем приветствовать человека, если передано имя, а можем просто вывести какую-то дефолтную фразу."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "28ec1b28",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello, it's me...\n"
]
}
],
"source": [
"def greeting(name=\"it's me...\"):\n",
" print(f\"Hello, {name}\")\n",
" \n",
"greeting()"
]
},
{
"cell_type": "markdown",
"id": "be47e975",
"metadata": {},
"source": [
"Однако, стоит быть внимательным с аргументами по умолчанию, если мы используем в качестве аргументов по умолчанию изменяемые значения. Обратите внимание на пример.\n",
"\n",
"У нас есть функция `append_one`, и в качестве дефолтного значения, значения по умолчанию `iterable`, у нас используется список.\n",
"\n",
"Собственно `append_one` просто прибавляет единичку к переданному списку или дефолтному списку. То есть у нас возвращается либо наш исходный список плюс один, либо просто список из единички. По крайней мере так мы ожидаем."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "4723d072",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 1]\n"
]
}
],
"source": [
"def append_one(iterable=[]):\n",
" iterable.append(1)\n",
" return iterable\n",
"\n",
"print(append_one([1]))"
]
},
{
"cell_type": "markdown",
"id": "abbde05d",
"metadata": {},
"source": [
"Если мы передадим в `append_one` список из единицы, нам вернется две единички, что мы и ожидаем. Однако что же произойдет, если мы вызовем `append_one` два раза?"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "84b2ef43",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1]\n",
"[1, 1]\n"
]
}
],
"source": [
"print(append_one())\n",
"print(append_one())"
]
},
{
"cell_type": "markdown",
"id": "db6f038f",
"metadata": {},
"source": [
"Вначале, как мы и ожидаем, к нам вернется единичка, потому что мы взяли наше дефолтное значение, добавили единичку в список и вернули. Однако если мы вызовем во второй раз, окажется, что у нас уже две единички, хотя мы явно ожидаем одну.\n",
"\n",
"Чтобы понять, почему это так, можно посмотреть на дефолтное значение нашей функции, и окажется, что там уже содержатся эти самые две единички. Почему так происходит?\n",
"\n",
"При определении функции, когда интерпретатор Python'а бежит по вашему файлу с кодом, определяется связь между именем функции и дефолтными значениями. Таким образом, у каждой функции появляется словарь, появляется `tuple` какой-то с дефолтными значениями. И именно в эти переменные каждый раз и происходит запись. Таким образом, если дефолтные значения являются изменяемыми, в них можно записывать, потому что это обычные переменные."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "88a2eab2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"([1, 1],)\n"
]
}
],
"source": [
"print(append_one.__defaults__)"
]
},
{
"cell_type": "markdown",
"id": "da7c8a62",
"metadata": {},
"source": [
"Что же нужно делать в таком случае? Нужно определять дефолтные значения как `None`. И если нам не передан какой-то параметр, мы просто создаем новый список на лету. Можно это делать двумя механизмами."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "166799cb",
"metadata": {},
"outputs": [],
"source": [
"def function(iterable=None):\n",
" if iterable is None:\n",
" iterable = []\n",
" \n",
"def function(iterable=None):\n",
" iterable = iterable or []"
]
},
{
"cell_type": "markdown",
"id": "fe32e50c",
"metadata": {},
"source": [
"Очень красивой особенностью Python'а является возможность определения функции, которая принимает разные количества аргументов.\n",
"\n",
"Мы можем определить функцию `printer`, которая принимает разное количество аргументов. Может быть один, два, три, четыре, сотня аргументов.\n",
"\n",
"В данном случае все аргументы записываются в `tuple args`. И мы видим, что это действительно `tuple`, если мы выведем тип. И мы можем, например, эти аргументы как-то потом использовать, можем просто по ним как-то пробежаться и вывести их."
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "5a5112e7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'tuple'>\n",
"1\n",
"2\n",
"3\n",
"4\n",
"5\n"
]
}
],
"source": [
"def printer(*args):\n",
" print(type(args))\n",
" \n",
" for argument in args:\n",
" print(argument)\n",
"\n",
"printer(1, 2, 3, 4, 5)"
]
},
{
"cell_type": "markdown",
"id": "2552db47",
"metadata": {},
"source": [
"Точно так же можно разворачивать наш список в аргументах. Мы можем определить наш список с Джоном, Биллом и Эми и передать наш список как аргументы в нашу функцию."
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "b458e59b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'tuple'>\n",
"John\n",
"Bill\n",
"Amy\n"
]
}
],
"source": [
"name_list = [\"John\", \"Bill\", \"Amy\"]\n",
"printer(*name_list)"
]
},
{
"cell_type": "markdown",
"id": "6a2299cf",
"metadata": {},
"source": [
"Таким образом, первым аргументом станет Джон, вторым — Билл, и третьим — Эми.\n",
"\n",
"Это так называемая распаковка списка. Есть еще подобные способы использования:"
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "21a58d1d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 [3, 4, 5]\n"
]
}
],
"source": [
"a, b, *others = [1, 2, 3, 4, 5]\n",
"\n",
"print(a, b, others)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "1bdc2fa4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 [3, 4] 5\n"
]
}
],
"source": [
"a, b, *others, c = [1, 2, 3, 4, 5]\n",
"\n",
"print(a, b, others, c)"
]
},
{
"cell_type": "markdown",
"id": "cf3a0381",
"metadata": {},
"source": [
"Точно так же это работает в случае со словарями, в данном случае мы можем определить функцию `printer`, которая принимает разное количество именованных аргументов."
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "8ade7f94",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'dict'>\n",
"a 10\n",
"b 11\n"
]
}
],
"source": [
"def printer(**kwargs):\n",
" print(type(kwargs))\n",
" for key, value in kwargs.items():\n",
" print(f\"{key} {value}\")\n",
" \n",
"printer(a=10, b=11)"
]
},
{
"cell_type": "markdown",
"id": "5a455966",
"metadata": {},
"source": [
"Собственно, все записывается в `kwargs`, и таким образом `kwargs` у нас останется `dict`'ом. Если мы передадим два именованных аргумента, `a = 10` и `b = 11`, то у нас получится словарь, и мы можем потом этот словарь как-то использовать, используя параметры и, например, просто их выводя на экран.\n",
"\n",
"Точно так же мы можем разыменовывать, разворачивать эти словари в обратную сторону. Таким образом, если у нас есть словарь, мы можем передавать значения из этого словаря как аргументы, именованные, в нашу функцию. Таким образом, когда мы используем две звездочки при вызове функции, у нас первым параметром становится `user_id`, а вторым параметром становится `feedback`. Это используется практически везде и позволяет вам определять очень гибкие функции, которые принимают различное количество аргументов — именованных и позиционных."
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "d4989f77",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'dict'>\n",
"user_id 117\n",
"feedback {'subject': 'Registration fields', 'message': 'There is no country for old men'}\n"
]
}
],
"source": [
"payload = {\n",
" \"user_id\": 117,\n",
" \"feedback\": {\n",
" \"subject\": \"Registration fields\",\n",
" \"message\": \"There is no country for old men\"\n",
" }\n",
"}\n",
"\n",
"printer(**payload)"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,909 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "46044c32",
"metadata": {},
"source": [
"# Функциональное программирование #"
]
},
{
"cell_type": "markdown",
"id": "b1d0b1e3",
"metadata": {},
"source": [
"Мы с вами разобрали то, как работают и определяются функции в Python'e и настало время разобраться с таким сложным концептом как функциональное программирование.\n",
"\n",
"Что же это такое? Чтобы понять, что такое функциональное программирование, нужно понимать несколько особенностей.\n",
"\n",
"Во-первых, функции в Python'e - это такие же объекты, как и, например, строки, списки или классы.\n",
"Их можно передавать в функции другие, их можно возвращать из функций, их можно создавать на лету, то есть это объекты первого класса.\n",
"Например, мы можем определить функцию `caller`, которая принимает другую функцию."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "dd050baa",
"metadata": {},
"outputs": [],
"source": [
"def caller(func, params):\n",
" return func(*params)"
]
},
{
"cell_type": "markdown",
"id": "60905ede",
"metadata": {},
"source": [
"Функцию можно передавать в функцию, как я вам говорил. Она принимает другую функцию и какой-то список параметров, и, собственно, эта функция вызывается этим списком параметров с использованием звездочки, с которой мы только что с вами разобрались.\n",
"\n",
"Определим другую функцию — обычную `printer`, которая просто выводит какую-то строчку. Мы можем в наш `caller` передать функцию `printer` и вызвать её. Это может быть функция `printer` в нашем `caller`'е, а может быть какая-нибудь другая функция - нам неважно."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "bcfb80f1",
"metadata": {},
"outputs": [],
"source": [
"def printer(name, origin):\n",
" print(f\"I'm {name} of {origin}!\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5c79161f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm Moana of Motunui!\n"
]
}
],
"source": [
"caller(printer, [\"Moana\", \"Motunui\"])"
]
},
{
"cell_type": "markdown",
"id": "29db28ad",
"metadata": {},
"source": [
"Главное, чтобы мы передали туда функцию и список параметров. Итак, мы вызываем наш `caller` с функцией `printer` и передаем туда список параметров и у нас выводится строка.\n",
"\n",
"Собственно, главное, что нужно вынести из этого примера, что функции можно передавать другие функции. Это такие же объекты."
]
},
{
"cell_type": "markdown",
"id": "4aae0263",
"metadata": {},
"source": [
"Ещё один важный момент. Функции можно не только передавать, их еще можно создавать внутри других функций. Например, мы можем определить функцию `get_multiplier` и внутри функции `get_multiplier` определить другую функцию, и эта функция будет `inner`. Она будет просто умножать два аргумента ей переданных."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "77503f82",
"metadata": {},
"outputs": [],
"source": [
"def get_multiplier():\n",
" def inner(a, b):\n",
" return a * b\n",
" \n",
" return inner"
]
},
{
"cell_type": "markdown",
"id": "95090fff",
"metadata": {},
"source": [
"Мы можем вызвать функцию `get_multiplier` и получить обычный `multiplier`, который умножает, например, 10 на 11, то есть мы определили функцию, которая возвращает другую функцию."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "644b0061",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"110"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"multiplier = get_multiplier()\n",
"multiplier(10, 11)"
]
},
{
"cell_type": "markdown",
"id": "2a1490a6",
"metadata": {},
"source": [
"Пока ничего сложного. Обратите внимание, что именно `multiplier`'ы — это `inner`, потому что мы вернули новую функцию и `multiplier` перестал быть тем, каким мы его знали раньше. Он стал `inner`'ом. "
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "2978a144",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"inner\n"
]
}
],
"source": [
"print(multiplier.__name__)"
]
},
{
"cell_type": "markdown",
"id": "18d1ca79",
"metadata": {},
"source": [
"Гораздо более интересный момент - это если мы наш `get_multiplier` что-нибудь передадим. Давайте попробуем определить функцию `inner`, которая будет принимать один аргумент и умножать она его будет всегда на то самое число, которое мы передали в `get_multiplier`."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "3c7d8cef",
"metadata": {},
"outputs": [],
"source": [
"def get_multiplier(number):\n",
" def inner(a):\n",
" return a * number\n",
" \n",
" return inner"
]
},
{
"cell_type": "markdown",
"id": "f00d88e1",
"metadata": {},
"source": [
"Смотрите, мы передаем `get_multiplier` двойку и получаем функцию, которая всегда умножает переданный ей аргумент на двойку."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "06513593",
"metadata": {},
"outputs": [],
"source": [
"multiplier_by_2 = get_multiplier(2)"
]
},
{
"cell_type": "markdown",
"id": "840f794a",
"metadata": {},
"source": [
"Например, мы передали в `multiplier_by_2` десятку и получили 20."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "256d8b08",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"20"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"multiplier_by_2(10)"
]
},
{
"cell_type": "markdown",
"id": "e72a8eb0",
"metadata": {},
"source": [
"Эта концепция называется \"замыканием\". В данном случае у нас используется `number` из скопа выше, из области видимости функции `get_multiplier` и это очень важный момент, который в дальнейшем будет использоваться, например, в декораторах.\n",
"\n",
"Часто бывает необходимо определить какие-то свои функции, которые принимают другие функции, но ещё чаще эти функции используют примерно одинаково, например, для того, чтобы применить какую-то функцию к набору элементов.\n",
" \n",
"Существует несколько стандартных функций, которые вам позволяют в функциональном стиле работать.\n",
"\n",
"Одна из таких функций - это `map`. Функция `map` принимает функцию и какой-то итерабельный объект, то есть, например, список или что-то, по чему можно итерироваться, и применяет полученную функцию к этому итерабельному объекту.\n",
"\n",
"Например, мы можем определить функцию `squarify`, которая просто принимает число и возвращает вторую степень его, то есть возвращает квадрат."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "7460b616",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 4, 9, 16]"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def squarify(a):\n",
" return a ** 2\n",
"\n",
"list(map(squarify, range(5)))"
]
},
{
"cell_type": "markdown",
"id": "3fded733",
"metadata": {},
"source": [
"Функция `map` работает очень просто. Мы передаем ей функцию `squarify` и `range`, то есть итератор от ноля до четырех. Функция `map` бежит по этому итерабельному объекту, то есть по `rang`'у, и применяет `squarify` к элементу одному за другим. В данном случае у нас получается список от 0 до 16 квадратов. Обратите внимание, я вызываю функцию `list` вокруг `map`'а, потому что `map` по умолчанию возвращает `map object`. Это не список, а какой-то объект внутренний, которому самое главное, что нужно — итерироваться.\n",
"\n",
"Очень часто нам просто необходимо итерироваться по этому объекту, нам не важно конкретно, чтобы это был список. Собственно, что здесь происходит, можно посмотреть на чистом Python'e. У нас как бы в начале создается `squared_list`, и потом просто мы в цикле в этот `squared_list` записываем применение функции к текущему `number`'у.\n",
"\n",
"Однако, это делается всё довольно просто и коротко с помощью функции `map`. "
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "726ace85",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 1, 4, 9, 16]\n"
]
}
],
"source": [
"squared_list = []\n",
"\n",
"for number in range(5):\n",
" squared_list.append(squarify(number))\n",
" \n",
"print(squared_list)"
]
},
{
"cell_type": "markdown",
"id": "6518fd00",
"metadata": {},
"source": [
"Ещё одна функция, которая часто используется в контексте функционального программирования, это функция `filter`. Функция `filter` вам позволяет фильтровать по какому-то предикату итерабельный объект.\n",
"\n",
"У вас есть какое-то условие, в данном случае — это функция, которая принимает число и проверяет, что это число больше ноля и возвращает `True` в этом случае, если это ноль или меньше, то возвращается `False`."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "ee4c3b6f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def is_positive(a):\n",
" return a > 0\n",
"\n",
"list(filter(is_positive, range(-2, 3)))"
]
},
{
"cell_type": "markdown",
"id": "4442abda",
"metadata": {},
"source": [
"У нас `filter` принимает какую-то функцию и фильтрует все аргументы внутри итерабельного объекта по этому предикату. Например, мы можем оставить в нашем списке, или в нашем `rang`'е, в нашем итерабельном объекте только положительные числа. \n",
"\n",
"Собственно, на чистом Python'e это выглядело бы как-то так. У нас есть наш `positive_list` и мы в цикле просто проверяем, что если у нас функция возвращает `True`, то мы оставляем это значение, то есть прибавляем в `positive_list` это число. Иначе мы просто продолжаем итерацию и ничего не делаем. Можно убедиться, что выводится именно то же самое."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "ae7405d5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2]\n"
]
}
],
"source": [
"positive_list = []\n",
"\n",
"for number in range(-2, 3):\n",
" if is_positive(number):\n",
" positive_list.append(number)\n",
" \n",
"print(positive_list)"
]
},
{
"cell_type": "markdown",
"id": "d5f9f452",
"metadata": {},
"source": [
"`Filter` и `map` позволяет вам лаконично и просто записать стандартные какие-то паттерны программирования."
]
},
{
"cell_type": "markdown",
"id": "02a5ad50",
"metadata": {},
"source": [
"Сейчас мы с вами разберем анонимные функции. Хотелось бы заметить, что несмотря на то, что `map` и `filter` очень мощны, не стоит злоупотреблять ими, потому что, если вы пишете довольно сложный код, который сложно разобрать, если вы вкладываете `map` в `map`, и `filter` в `filter`, пишете многострочные какие-то функциональные штуки, программистам сложно будет читать этот код. \n",
"\n",
"Чем сильнее, чем выше когнитивная нагрузка у программиста, тем хуже. Пожалуйста, не злоупотребляйте ими, хотя это довольно мощные конструкции.\n",
"\n",
"Итак, часто нам бывает необходимо определить какую-то функцию, например, `squarify` как мы определяли, но нам не нужно будет её применять в дальнейшем. Нам она интересна только в каком-то контексте. Например, возвести во вторую степень все элементы в этом списке, но мы не хотим использовать эту функцию потом. Нам на помощь в данном случае придут анонимные функции, так они называются в Python'e или `lambda` функции. `Lambda` позволяет вам определить функцию прям `in place`, то есть без оператора `def`, без литерала `def`. Используйте её прям на месте и забыли про нее потом. В данном случае мы делаем точно то же самое, что и в предыдущем примере, только определяем функцию не с помощью `def`'а, где-то вверху, а прямо на месте и пишем, что наша анонимная функция принимает один параметр и возводит его в квадрат. Всё."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "312fd40e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 4, 9, 16]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(map(lambda x: x ** 2, range(5)))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "01b7bc1c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"function"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(lambda x: x ** 2)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "5d3224c3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(filter(lambda x: x > 0, range(-2, 3)))"
]
},
{
"cell_type": "markdown",
"id": "08af4100",
"metadata": {},
"source": [
"Происходит то же самое, только у нас функция создается `in place`. Собственно, как видите, `map` точно так же принимает функцию и итерабельный объект. Ничего нового. `Lambda` — это действительно функция, но она анонимная. У нее нет имени, как видите, просто какая-то функция, принимающая один объект и возвращающая квадрат. То же самое можно сделать с `filter`'ом, и `filter` у нас примет функцию `lambda`, и `lambda` проверит, что число больше нуля."
]
},
{
"cell_type": "markdown",
"id": "0f87bbbf",
"metadata": {},
"source": [
"Давайте в качестве примера напишем функцию, которая превращает список чисел в список строк. Сделайте это самостоятельно."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "0977cbb9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def stringify_list(num_list):\n",
" return list(map(str, num_list))\n",
"\n",
"stringify_list(range(10))"
]
},
{
"cell_type": "markdown",
"id": "1a27fac1",
"metadata": {},
"source": [
"Отлично, скорее всего у вас всё получилось. Ничего сложного не должно быть. Чтобы превратить список чисел в список строк нам нужно использовать функцию `map`, как вы могли догадаться, и передать функцию `map`, класс `str`. При применении класса `str` к числу, у нас получается строка и именно это нам и нужно. Нам нужно пробежаться по списку и всё передать в строку, то есть передать в конструктор `str`. Мы можем вызвать нашу функцию в `range` и получить список строк от нуля до девяти."
]
},
{
"cell_type": "markdown",
"id": "a31320c1",
"metadata": {},
"source": [
"Существует отличный модуль `functools`, который вам позволяет использовать функциональные особенности Python'а ещё лучше.\n",
"\n",
"Например, `functools` в последних версиях языка принесли функцию `reduce`, которая изначально была в общем скопе глобальных функций. Что делает `reduce`? `Reduce`, как вы уже могли догадаться, принимает функцию и итерабельный объект. Что она делает с ним? В данном случае у нас функция `multiply`, которая принимает два значения и перемножает их. Функция `reduce`, начиная слева направо, бежит и по цепочке применяет эту функцию."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "6007f8fc",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"120"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from functools import reduce\n",
"\n",
"def multiply(a, b):\n",
" return a * b\n",
"\n",
"reduce(multiply, [1, 2, 3, 4, 5])"
]
},
{
"cell_type": "markdown",
"id": "28512684",
"metadata": {},
"source": [
"Каким образом? В начале в функцию `multiply` попадают один и два, они перемножаются и запоминается двойка, у нас в памяти лежит двойка. Дальше, у нас эта двойка перемножается с тройкой. Таким образом у нас была двойка в памяти, берется следующий элемент, перемножается с тройкой. У нас уже шесть в памяти. Шесть в памяти умножается на четыре. У нас получается 24 и запоминается. 24 потом уже умножается на пять, получается 120, что мы получаем на входе. `Reduce` позволяет вам сжимать какие-то данные, применяя последовательно функцию и запоминая результат. То же самое можно сделать с помощью анонимных функций."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "634a1fde",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"24"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reduce(lambda x, y: x * y, range(1, 5))"
]
},
{
"cell_type": "markdown",
"id": "6fad411a",
"metadata": {},
"source": [
"Да, но получилось не 120 — `range`, как вы могли догадаться, пятерку не включает."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "377ef58d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"120"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reduce(lambda x, y: x * y, range(1, 6))"
]
},
{
"cell_type": "markdown",
"id": "0b35a963",
"metadata": {},
"source": [
"Также в `functools` есть отличный метод `partial`, который вам позволяет модифицировать немного поведение функций. Например, у нас есть функция `greeter`, которая просто приветствует человека с помощью какого-то приветствия `greeting`. Например, может поздороваться с Майклом. И мы можем определить какой-то новый набор функций, подменив по умолчанию определенные параметры. "
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "9dfec3a9",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hi, brother!\n",
"Hello, sir!\n"
]
}
],
"source": [
"from functools import partial\n",
"\n",
"def greeter(person, greeting):\n",
" return f\"{greeting}, {person}!\"\n",
"\n",
"hier = partial(greeter, greeting=\"Hi\")\n",
"helloer = partial(greeter, greeting=\"Hello\")\n",
"\n",
"print(hier(\"brother\"))\n",
"print(helloer(\"sir\"))"
]
},
{
"cell_type": "markdown",
"id": "737347b9",
"metadata": {},
"source": [
"Например, мы можем создать новую функцию `hier`, которая будет всегда приветствовать с помощью приветствия \"Hi\". Делается это с помощью функции `partial`, которая принимает, опять же, функцию и какой-то параметр, который нужно подменить. В данном случае у нас будет функция `hier` и функция `helloer`. `helloer` всегда приветствует с помощью \"Hallo\", а `hier` — всегда с помощью \"Hi\"."
]
},
{
"cell_type": "markdown",
"id": "9c07a270",
"metadata": {},
"source": [
"Например, у нас формальное и неформальное приветствие. Таким образом, у нас была функция `greeter`, которая принимает `greeting` и `person`, а стала только функция `hier` и `helloer`, которая принимает только `person`. Например, мы можем поздороваться неформально со своим братом и с каким-то незнакомым мужчиной с помощью \"Hallo\". Функция `partial` позволяет вам подменять определенные аргументы и модифицировать поведение функций. Например, мы можем определить логер, который записывает информацию только в какой-то конкретный файл или любые действия на ваш вкус."
]
},
{
"cell_type": "markdown",
"id": "5fe2ecb1",
"metadata": {},
"source": [
"Списочные выражения, очень важная концепция, которая используется практически повсеместно, потому, что позволяет вам упростить жизнь, и написать красивый код. Раньше, чтобы создать список, например, квадратов, мы с вами писали цикл. Создавали список и в цикле в этот список добавляли элементы — квадраты числа. Получалось довольно просто, однако нам необходимо определять список, определять цикл и в цикле ещё делать `append`.\n",
"\n",
"Это довольно многословно, в Python-е так не годится, и поэтому есть более простой, удобный метод написать то же самое. Этот метод называется \"списочное выражение\", или `list comprehensions`, которое выглядит так. У нас есть квадратные скобочки, и мы в квадратных скобочках пишем цикл. У нас цикл по `range`-у, мы берем `number`, этот `number` возводим в квадрат, и присваиваем его в список. Таким образом у нас список наполняется с помощью вот таких элементов. В цикле у нас добавляются квадраты числа и получается точно такой же результат — это называется \"списочное выражение\", оно работает немного быстрее, чем такой вот цикл и выглядит намного лаконичнее и короче. Поэтому практически всегда используются именно они, именно \"списочные выражения\"."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "606fba9b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n"
]
}
],
"source": [
"square_list = []\n",
"\n",
"for number in range(10):\n",
" square_list.append(number ** 2)\n",
" \n",
"print(square_list)"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "e6076d78",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n"
]
}
],
"source": [
"square_list = [number ** 2 for number in range(10)]\n",
"print(square_list)"
]
},
{
"cell_type": "markdown",
"id": "2d240574",
"metadata": {},
"source": [
"Точно так же можно написать списочное выражение с каким-то условием. А если нам нужны только четные числа в нашем списке, мы бы писали что-то в этом роде. У нас был бы цикл и условия, и мы только при выполнении условия бы добавляли в наш список элемент. Мы можем точно так же добавить условие в `list comprehensions`, и добавлять наш `num` только при выполнении условия остаток от деления равен нулю."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "fa856e67",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 2, 4, 6, 8]\n"
]
}
],
"source": [
"even_list = []\n",
"\n",
"for number in range(10):\n",
" if number % 2 == 0:\n",
" even_list.append(number)\n",
" \n",
"print(even_list)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "d417cb66",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 2, 4, 6, 8]\n"
]
}
],
"source": [
"even_list = [num for num in range(10) if num % 2 == 0]\n",
"print(even_list)"
]
},
{
"cell_type": "markdown",
"id": "9625d939",
"metadata": {},
"source": [
"Точно так же мы можем определять словари. Например, с помощью фигурных скобочек и двоеточия у нас соответственно в словаре, это ключ, и number в квадрате — это значение. Например, у нас строится отображение из числа в его квадрат. Мы можем определять точно так же с помощью `dict comprehensions` \"дикты\"."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "e584a051",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}\n"
]
}
],
"source": [
"square_map = {number: number ** 2 for number in range(5)}\n",
"print(square_map)"
]
},
{
"cell_type": "markdown",
"id": "2edd7643",
"metadata": {},
"source": [
"Мы можем делать с помощью обычных фигурных скобочек без двоеточий делать `set`-ы."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "04d502db",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
]
}
],
"source": [
"reminders_set = {num % 10 for num in range(100)}\n",
"print(reminders_set)"
]
},
{
"cell_type": "markdown",
"id": "2cbba536",
"metadata": {},
"source": [
"Обратите внимание, это опять же довольно мощные конструкции, которые здорово и легко использовать, однако, например, `list comprehensions`-ы позволяют вам вкладывать циклы `for` друг в друга и строить какие-то более сложные вещи.\n",
"\n",
"Например, вы можете в условиях писать сложные предикаты. "
]
},
{
"cell_type": "markdown",
"id": "e276ea97",
"metadata": {},
"source": [
"Пожалуйста не делайте этого, держите ваш код простым и понятным. Несмотря на то, что функциональные особенности языка очень легко использовать и писать довольно большие многострочные выражения, лучше, чтобы ваш код был простым и понятным. Не злоупотребляйте этим. Ну и что интересно, если мы просто без скобочек напишем такое выражение, то у нас тоже что-то получится. И на самом деле это генератор. О генераторах мы поговорим позже. Пока просто знайте, что это какой-то объект, в котором, например, можно итерироваться. Но это не список, не `tuple`, и не `set`."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "4d02e541",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'generator'>\n"
]
}
],
"source": [
"print(type(number ** 2 for number in range(5)))"
]
},
{
"cell_type": "markdown",
"id": "abb2a048",
"metadata": {},
"source": [
"Ещё одна важная функция, которая часто рассматривается в контексте функционального программирования, это функция `zip`. Функция `zip` позволяет вам склеить два итерабельных объекта. В данном случае у нас есть `numList`, `squaredList`, представляющий собой квадраты чисел, и мы склеиваем их. И у нас первый элемент одного списка с первым элементом других списков попадает в один `tuple`. Происходит что-то в этом роде. Часто бывает функция эта полезна."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "67c6fbd2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36)]"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num_list = range(7)\n",
"squared_list = [x ** 2 for x in num_list]\n",
"\n",
"list(zip(num_list, squared_list))"
]
},
{
"cell_type": "markdown",
"id": "53cb437e",
"metadata": {},
"source": [
"Итак. Мы с вами разобрали такую страшную концепцию, как функциональное программирование, которая на самом деле проста.\n",
"\n",
"Мы с вами узнали, что функции - это объекты первого класса, их можно передавать внутри функций, возвращать из функций, создавать внутри функций, возвращать, то есть работать с ними как с обычными объектами; разобрали с вами классические функции `map`, `filter` и `reduce`; посмотрели на анонимные функции, которые можно создавать `in place` и разобрали списочные выражения, которые часто вам могут упростить жизнь для того, чтобы создавать списки, `set`-ы, `dict`-ы, или что-то в этом роде."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,124 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "69d5ed93",
"metadata": {},
"source": [
"# Структуры данных и функции #"
]
},
{
"cell_type": "markdown",
"id": "325dbb76",
"metadata": {},
"source": [
"На этом блоке вы узнаете про новые типы данных — коллекции, познакомитесь с функциями, а так же научитесь использовать функциональное программирование в Python."
]
},
{
"cell_type": "markdown",
"id": "9f938f9d",
"metadata": {},
"source": [
"## Задачи обучения ##"
]
},
{
"cell_type": "markdown",
"id": "acd18a27",
"metadata": {},
"source": [
"- Научиться работать со стандартными структурами данных в Python.\n",
"- Научиться писать функции на Python.\n",
"- Применять функциональные особенности языка.\n",
"- Научиться работать с файлами с помощью языка Python."
]
},
{
"cell_type": "markdown",
"id": "f70a94ad",
"metadata": {},
"source": [
"## Оглавление ##"
]
},
{
"cell_type": "markdown",
"id": "ae73a075",
"metadata": {},
"source": [
"### Коллекции ###"
]
},
{
"cell_type": "markdown",
"id": "6f650a78",
"metadata": {},
"source": [
"- [Списки и кортежи](1.%20Коллекции/Списки%20и%20кортежи.ipynb)\n",
"- [Списки. Пример программы](1.%20Коллекции/Списки.%20Пример%20программы.ipynb)\n",
"- [Словари](1.%20Коллекции/Словари.ipynb)\n",
"- [Словари. Пример программы](1.%20Коллекции/Словари.%20Пример%20программы.ipynb)\n",
"- [Множества](1.%20Коллекции/Множества.ipynb)\n",
"- [Множества. Пример программы](1.%20Коллекции/Множества.%20Пример%20программы.ipynb)\n",
"- [Документация](1.%20Коллекции/Документация.ipynb)\n",
"- [Тест по коллекциям](1.%20Коллекции/Тест%20по%20коллекциям.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "41f515e3",
"metadata": {},
"source": [
"### Функции ###"
]
},
{
"cell_type": "markdown",
"id": "d9c03ccd",
"metadata": {},
"source": [
"- [Функции](2.%20Функции/Функции.ipynb)\n",
"- [Тест по функциям](2.%20Функции/Тест%20по%20функциям.ipynb)\n",
"- [Файлы](2.%20Функции/Файлы.ipynb)\n",
"- [Функциональное программирование](2.%20Функции/Функциональное%20программирование.ipynb)\n",
"- [Декораторы](2.%20Функции/Декораторы.ipynb)\n",
"- [Генераторы](2.%20Функции/Генераторы.ipynb)\n",
"- [Документация](2.%20Функции/Документация.ipynb)\n",
"- [Задание. Декоратор to_json](2.%20Функции/Задание.%20Декоратор%20to_json.ipynb)\n",
"- [Задание. Key-value хранилище](2.%20Функции/Задание.%20Key-value%20хранилище.ipynb)\n",
"- [Тест по блоку](2.%20Функции/Тест%20по%20блоку.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "87e687a1",
"metadata": {},
"source": [
"Вот и закончился второй блок нашего курса. Мы с вами разобрали типы даных коллекций, научились работать с функциями, узнали что такое функциональное программирование, как задавать свои собственные декораторы и генераторы. На следующем блоке вы будете изучать объектно-ориентрованное программирование в Python'е. [Далее...](../3.%20Объектно-ориентированное%20программирование/Readme.ipynb)"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,46 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "56227466",
"metadata": {},
"source": [
"# Документация #"
]
},
{
"cell_type": "markdown",
"id": "a63a522c",
"metadata": {},
"source": [
"- [Описание классов в документации Python 3.](https://docs.python.org/3.6/tutorial/classes.html \"9. Classes\")\n",
"- [Очень хорошая вводная статья на английском про классы.](https://www.python-course.eu/python3_object_oriented_programming.php \"Object-Oriented Programming\")\n",
"Также с того же ресурса [статья с примерами про атрибуты, @classmethod, @staticmethod.](https://www.python-course.eu/python3_class_and_instance_attributes.php \"Class and Instance Attributes\")\n",
"[И там же про @property.](https://www.python-course.eu/python3_properties.php \"Properties vs. Getters and Setters\")\n",
"\n",
"На русском языке [хорошая статья нашлась на Wikipedia](https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BD%D0%B0_Python \"Объектно-ориентированное программирование на Python\"). Обратите внимание, что помимо основ она содержит материалы, которые мы подробно осветим далее в курсе."
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,770 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "bde8fdc5",
"metadata": {},
"source": [
"# Методы #"
]
},
{
"cell_type": "markdown",
"id": "53f43d5b",
"metadata": {},
"source": [
"Давайте усложним немножко наши примеры и добавим классам методы.\n",
"\n",
"Методы это по большому счёту просто функции, которые действуют в контексте экземпляра класса.\n",
"\n",
"Таким образом так как они действует в контексте экземпляра и получают ссылку на экземпляр класса, они могут менять его состояние, обращаясь к атрибутам экземпляра или делать любую полезную работу, которая нам захочется.\n",
"\n",
"Давайте посмотрим, как создать метод экземпляра и вызвать его. Население планеты нестабильно, люди рождаются и умирают поэтому мы создали класс `Human`, у которого есть два атрибута `name` и `age`, имя и возраст и также у нас есть класс планеты, у которой есть атрибут `name` и также атрибут `population`, который является списком и этот список будет содержать людей, которые есть на планете. Вот здесь мы объявили метод `add_human`, который является методом экземпляра и по большому счёту это просто функция, которая принимает первым аргументом `self`, это ссылка на экземпляр класса, а вторым аргументом всё что мы захотим."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "307c9d5a",
"metadata": {},
"outputs": [],
"source": [
"class Human:\n",
" def __init__(self, name, age=0):\n",
" self.name = name\n",
" self.age = age\n",
" \n",
" \n",
"class Planet:\n",
" def __init__(self, name, population=None):\n",
" self.name = name\n",
" self.population = population or []\n",
" \n",
" def add_human(self, human):\n",
" print(f\"Welcome to {self.name}, {human.name}!\")\n",
" self.population.append(human)"
]
},
{
"cell_type": "markdown",
"id": "ba1164be",
"metadata": {},
"source": [
"И так как это обычная функция, она может принимать сколько угодно аргументов как позиционных, так и именованных. В данном случае она принимает один. Это будет экземпляр класса `Human`. Внутри этого метода мы печатаем на экран приветственное сообщение новому человеку и обновляем атрибут экземпляра `population`. Добавляем туда нового человека.\n",
"\n",
"Также внутри методов, так как это просто функция в контексте экземпляра, мы можем использовать конструкцию `return`, чтобы возвращать из них какие-то значения, но в данном случае нам это не потребовалось. Давайте посмотрим как этим пользоваться, мы создаем экземпляр класса `Planet`, `Mars`, дальше мы создаем экземпляр класса `Human`, даем человеку имя Боб и затем мы у экземпляра класса `Planet Mars` вызываем метод `add_human` и передаем в него аргумент, объект нашего человека."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7a00536d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Welcome to Mars, Bob!\n"
]
}
],
"source": [
"mars = Planet(\"Mars\")\n",
"bob = Human(\"Bob\")\n",
"mars.add_human(bob)"
]
},
{
"cell_type": "markdown",
"id": "a25fda62",
"metadata": {},
"source": [
"Таким образом, мы обновляем население планеты и если мы посмотрим на то что выглядит `mars.population`, мы видим что действительно население планеты обновилось. Kак раз то, что мы делали внутри метода экземпляра `add_human`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5653db75",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(len(mars.population))"
]
},
{
"cell_type": "markdown",
"id": "67cfdcf4",
"metadata": {},
"source": [
"Ничто не мешает нам из методов вызывать другие методы. Давайте посмотрим еще на один пример и убедимся в том, что это действительно возможно, и как это сделать. Мы объявляем класс `Human`, у которого программист предположим решил сделать атрибут `name` и `age` вот такими, то есть назвать их, начиная с символа нижнего подчёркивания. Также у этого класса метод экземпляра `say`, который также начинается с нижнего подчёркивания, а ещё два метода `say_name` и `say_how_old`, которые печатают, сколько человеку лет и какое у него имя."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8793452f",
"metadata": {},
"outputs": [],
"source": [
"class Human:\n",
" def __init__(self, name, age=0):\n",
" self._name = name\n",
" self._age = age\n",
" \n",
" def _say(self, text):\n",
" print(text)\n",
" \n",
" def say_name(self):\n",
" self._say(f\"Hello, I am {self._name}\")\n",
" \n",
" def say_how_old(self):\n",
" self._say(f\"I am {self._age} years old\")"
]
},
{
"cell_type": "markdown",
"id": "f407522c",
"metadata": {},
"source": [
"Что значат эти символы нижнего подчёркивания? В других языках программирования вы могли встречаться с `protected`, `рrivate` атрибутами то есть механизмами защиты атрибутов, которые определены внутри класса. В Python-e такого механизма нет и по большому счёту вы можете достучаться до любого атрибута, однако есть такое соглашение, что если атрибут либо метод названы c символа нижнего подчёркивания, то ими пользоваться не рекомендуется потому, что в дальнейших версиях той или иной библиотеки программист, который пишет может либо отказаться от этих атрибутов или методов, начинающихся с символа нижнего подчеркивания, либо поменять их поведение каким-то образом.\n",
"\n",
"Однако у нас есть класс, у которого есть два публичных метода, `say_name` и `say_how_old`, оба эти методы вызывают приватный метод `say`. Посмотрим как этим пользоваться."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "94893dfc",
"metadata": {},
"outputs": [],
"source": [
"bob = Human(\"Bob\", age=29)"
]
},
{
"cell_type": "markdown",
"id": "de0e06b9",
"metadata": {},
"source": [
"Мы объявляем переменным Боб, который является экземпляром класса `Human` и дальше мы можем вызывать методы, которые внутри себя будут вызывать другой метод, и мы видим что печатается то, что мы хотим."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "57686020",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello, I am Bob\n",
"I am 29 years old\n"
]
}
],
"source": [
"bob.say_name()\n",
"bob.say_how_old()"
]
},
{
"cell_type": "markdown",
"id": "04f544c3",
"metadata": {},
"source": [
"А вот так вот обращаться к атрибуту экземпляра и методу экземпляра не рекомендуется как раз потому что эти атрибуты и метод начинаются с символа нижнего подчёркивания."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "363cb13b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Bob\n",
"Whatever we want\n"
]
}
],
"source": [
"# не рекомендуется!\n",
"print(bob._name)\n",
"# не рекомендуется!\n",
"bob._say(\"Whatever we want\")"
]
},
{
"cell_type": "markdown",
"id": "02b00ebd",
"metadata": {},
"source": [
"Несмотря на то, что Python предоставляет эту возможность. \n",
"\n",
"Другой концепт, который есть в реализации классов на Python, это так называемый метод класса либо `classmethod`. Зачем это может быть нужно? Так может получиться, что вам нужно объявить метод, но этот метод не привязан к конкретному экземпляру, но в тоже время он вовлекает класс в свою работу тем или иным образом, сам класс, то есть сам класс, которым вы оперируете, лучше всего посмотреть на примере."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "78cf5748",
"metadata": {},
"outputs": [],
"source": [
"class Event:\n",
" def __init__(self, description, event_date):\n",
" self.description = description\n",
" self.date = event_date\n",
" \n",
" def __str__(self):\n",
" return f\"Event \\\"{self.description}\\\" at {self.date}\""
]
},
{
"cell_type": "markdown",
"id": "901db4da",
"metadata": {},
"source": [
"Давайте отвлечемся немножко от планет и людей и посмотрим класс `Event`, класс, который описывает события. У этого класса есть описание и дата, когда это событие происходит, также я переопределил метод `str`, чтобы когда мы печатали `event` на экран выводилось что-то осмысленное.\n",
"\n",
"Посмотрим как им пользоваться. Мы получаем текущую дату используя модуль `datetime` стандартной библиотеки Python, и дальше как обычно создаем экземпляр класса `Event`, инициализируя его с помощью описания и даты."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "9a620e3b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Event \"Рассказать, что такое @classmethod\" at 2021-06-08\n"
]
}
],
"source": [
"from datetime import date\n",
"\n",
"event_description = \"Рассказать, что такое @classmethod\"\n",
"event_date = date.today()\n",
"\n",
"event = Event(event_description, event_date)\n",
"print(event)"
]
},
{
"cell_type": "markdown",
"id": "8fdf3c22",
"metadata": {},
"source": [
"На данном слайде мы добавим нашему классу `Event` метод класса. Зачем?\n",
"\n",
"Давайте рассмотрим такой пример: повсеместно сейчас распространены мессенджеры, в которых есть масса умных помощников, которые предоставляют тот или иной функционал, а пользователи могут писать им текст и получать в ответ какой-то ответ, пользователь пишет умному помощнику \"Привет! Я хочу добавить такое-то событие на такую то дату\", умный помощник принимает этот ввод пользователя, анализирует его на серверной стороне и добавляет событие в календарь пользователя."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "1d655d29",
"metadata": {},
"outputs": [],
"source": [
"def extract_description(user_string):\n",
" return \"открытие чемпионата мира по футболу\"\n",
"\n",
"def extract_date(user_string):\n",
" return date(2018, 6, 14)\n",
"\n",
"class Event:\n",
" def __init__(self, description, event_date):\n",
" self.description = description\n",
" self.date = event_date\n",
" \n",
" def __str__(self):\n",
" return f\"Event \\\"{self.description}\\\" at {self.date}\"\n",
" \n",
" @classmethod\n",
" def from_string(cls, user_input):\n",
" description = extract_description(user_input)\n",
" date = extract_date(user_input)\n",
" return cls(description, date)"
]
},
{
"cell_type": "markdown",
"id": "a590e61d",
"metadata": {},
"source": [
"Давайте посмотрим как это можно было бы сделать на Python-е. А у нас есть все тот же класс `Event`, но мы добавили ему метод `from_string`, который обернули декоратором `classmethod`. `Сlassmethod` - это встроенный объект, вам не нужно его ниоткуда импортировать, данный декоратор делает метод методом класса, в отличие от метода экземпляра, метод класса первым аргументом принимает не ссылку на конкретный экземпляр класса, а сам класс непосредственно, то есть в данном случае это будет класс `Event`, а не конкретный экземпляр. Внутри этого метода мы из пользовательского ввода, в данном случае это `user_input`, каким-то образом выделяем дату, которую пользователь хочет создать и описание события, на примере мы сделали это с помощью коротких функций-заглушек, которые из строки выделяют дату и описание, на самом деле - это нетривиальные задачи. Для того чтобы анализировать пользовательский ввод в таком ключе, требуется специальные библиотеки или даже есть для этого сервисы. Это не так просто, но мы для простоты возвращаем просто какое-то описание и какую-то дату. Получив описание и дату мы можем проинициализировать класс и вернуть экземпляр класса события на основе вот той строки, которую нам передал пользователь и получить экземпляр класса `Event` и как-то дальше с ним оперировать, добавить его в календарь.\n",
"\n",
"Таким образом `classmethod` может быть полезен как например альтернативный конструктор вашего класса. Давайте посмотрим, как им пользоваться."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "9c063dcf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Event \"открытие чемпионата мира по футболу\" at 2018-06-14\n"
]
}
],
"source": [
"event = Event.from_string(\n",
" \"добавить в мой календарь открытие чемпионата мира по футболу на 14 июня 2018 года\"\n",
")\n",
"\n",
"print(event)"
]
},
{
"cell_type": "markdown",
"id": "1ebd41ff",
"metadata": {},
"source": [
"Когда после того как мы всё это объявили, у нас есть класс `Event` и мы можем вызвать метод класса `from_string` и передать в него строку. Произойдёт анализ этой строки и в результате нам вернётся экземпляр класса `Event` и мы видим, что у нас всё получилось, на экран вывелось как раз правильное описание события.\n",
"\n",
"Возможно вам сейчас не очень очевидно, зачем нужны класс-методы, но это становится более очевидным, когда появляется наследование. Класс-метод принимает на вход класс и этот класс будет всегда тем, который, внутри которого этот класс-метод описан и вы относительно этого класса можете не только его как-то инициализировать и вернуть, но вы также можете обращаться к атрибутам класса, делать всё что угодно, что вы можете сделать с классом. Внутри стандартной библиотеки класс-методы тоже активно используются. И например, вы знаете, что `dict` - это класс. И соответственно у `dict`а, у словаря, есть метод `fromkeys`. Это как раз метод класса, который принимая какой-то итерабельный объект, возвращает нам проинициализированный словарь."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "06466ac7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'1': None, '2': None, '3': None, '4': None, '5': None}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dict.fromkeys(\"12345\")"
]
},
{
"cell_type": "markdown",
"id": "f2091ae0",
"metadata": {},
"source": [
"В данном случае мы передали в него строку и получили словарь, где ключ - это элeменты последовательности в строке.\n",
"\n",
"Мы научились объявлять и работать с методами экземпляров, а также посмотрели на методы класса, которые в отличие от методов экземпляров принимают первым аргументом не ссылку на конкретный экземпляр класса, а сам класс непосредственно. \n",
"\n",
"Далее мы посмотрим на другие особенности реализации классов в Python-e и посмотрим на статический метод и на `property`."
]
},
{
"cell_type": "markdown",
"id": "7b3ebb12",
"metadata": {},
"source": [
"Следующим концептом, на который мы посмотрим, является статический метод, либо `staticmethod`. Давайте посмотрим на примере."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "d91edf92",
"metadata": {},
"outputs": [],
"source": [
"class Human:\n",
" def __init__(self, name, age=0):\n",
" self.name = name\n",
" self.age = age\n",
" \n",
" @staticmethod\n",
" def is_age_valid(age):\n",
" return 0 < age < 150"
]
},
{
"cell_type": "markdown",
"id": "848dda48",
"metadata": {},
"source": [
"Может так получиться, что вам нужно объявить метод в контексте класса, но этот метод не оперирует ни ссылкой на конкретный экземпляр класса, ни самим классом непосредственно, как мы видели в методе класса. В таком случае вам может помочь статический метод. Зачем вам может понадобиться статический метод? На самом деле, статический метод — это просто вопрос организации кода, то есть вы его используете тогда, когда вам необходимо, чтобы к нему обращались относительно имени класса либо экземпляра. Давайте посмотрим на примере, как объявить статический метод. Мы объявляем `class Human`, у которого есть имя и возраст, и объявили статический метод `is_age_valid`, который проверяет возраст на соответствие каким-то возрастным ограничениям.\n",
"\n",
"Чтобы объявить статический метод, мы воспользовались декоратором `staticmethod`, опять же, это встроенный объект в Python'е, не нужно его ниоткуда импортировать. После того как мы обернули функцию декоратором `staticmethod`, мы получаем статический метод. Статический метод принимает только те аргументы, которые ему передают. Обратите внимание, что здесь нет ни `self`, ни `class` аргументов. Обращаться к нему можно вот так, то есть относительно имени класса самого `Human`, либо относительно экземпляра. И в том, и в другом случае происходит одно и то же, и код отработает."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "a433bb69",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# можно обращаться от имени класса\n",
"Human.is_age_valid(35)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "e8b724cd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# или от экземпляра:\n",
"human = Human(\"Old Bobby\")\n",
"human.is_age_valid(234)"
]
},
{
"cell_type": "markdown",
"id": "f084a559",
"metadata": {},
"source": [
"Опять же, подчеркну, что в данном случае мы могли бы эту функцию объявить просто как обычную функцию, вне контекста класса, вне пространства имен класса. Но мы решили сделать это так, просто из соображений того, что этим будет удобнее пользоваться."
]
},
{
"cell_type": "markdown",
"id": "1c9f53ed",
"metadata": {},
"source": [
"Далее, еще один мощный концепт, который есть в языке Python и встречается в стандартной библиотеке и в библиотеках повсеместно, является `property`. `Property`, или по-другому вычисляемые свойства. Зачем они нужны? `Property` позволяют изменять поведение и выполнять какую-то вычислительную работу при обращении к атрибуту экземпляра, либо при изменении атрибута, либо при его удалении. Проще всего это понять на примере. Давайте начнем немножко издалека. Предположим, у нас есть класс `Robot`. У этого класса есть переопределенный метод `init`, который инициализирует экземпляр класса одним атрибутом `power` — это мощность робота."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "9c523914",
"metadata": {},
"outputs": [],
"source": [
"class Robot:\n",
" def __init__(self, power):\n",
" self.power = power"
]
},
{
"cell_type": "markdown",
"id": "39bdcadc",
"metadata": {},
"source": [
"Соответственно, пользоваться всем этим можно вот так, ничего нового для вас здесь нет."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "bc8b04f7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"200\n"
]
}
],
"source": [
"wall_e = Robot(100)\n",
"wall_e.power = 200\n",
"print(wall_e.power)"
]
},
{
"cell_type": "markdown",
"id": "4e298ac2",
"metadata": {},
"source": [
"Мы инициализируем экземпляр и далее можем в любой момент поменять мощность робота и установить ему новое значение."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "430005b5",
"metadata": {},
"outputs": [],
"source": [
"wall_e.power = -20"
]
},
{
"cell_type": "markdown",
"id": "5d666cb5",
"metadata": {},
"source": [
"Предположим, что так получилось, что вы заметили, что другие программисты, которые пользуются вашим классом `Robot`, иногда ставят ему отрицательную мощность, а это невалидное значение, и вам хотелось бы, чтобы когда другие программисты ставят отрицательную мощность, эта мощность на самом деле ставилась бы в ноль. Как можно поступить в таком случае? Первое, что приходит на ум, это отрефакторить наш класс и добавить метод экземпляра `set_power`, который, принимая мощность, будет проверять, что мощность меньше нуля и в таком случае ставить ее в ноль, либо ставить в то значение, которое нам передали."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "c1379a30",
"metadata": {},
"outputs": [],
"source": [
"class Robot:\n",
" def __init__(self, power):\n",
" self.power = power\n",
" \n",
" def set_power(self, power):\n",
" if power < 0:\n",
" self.power = 0\n",
" else:\n",
" self.power = power"
]
},
{
"cell_type": "markdown",
"id": "93e760ed",
"metadata": {},
"source": [
"Таким образом, пользоваться этим можно уже вот так. Мы инициализируем объект `Robot` и дальше вызываем у него метод `set_power`, который в случае отрицательной мощности поставит ее в ноль, и мы видим, что у нас получилось."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "cfbadc0b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n"
]
}
],
"source": [
"wall_e = Robot(100)\n",
"wall_e.set_power(-20)\n",
"print(wall_e.power)"
]
},
{
"cell_type": "markdown",
"id": "192fed6c",
"metadata": {},
"source": [
"Однако здесь есть небольшой нюанс. В таком случае программисту, который использует ваш класс, придется менять свой код. То есть это потребует не только рефакторинга от вас, но и рефакторинга от других программистов. Есть ли способ проще? Есть."
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "4cac7f06",
"metadata": {},
"outputs": [],
"source": [
"class Robot:\n",
" def __init__(self, power):\n",
" self._power = power\n",
" \n",
" power = property()\n",
" \n",
" @power.setter\n",
" def power(self, value):\n",
" if value < 0:\n",
" self._power = 0\n",
" else:\n",
" self._power = value\n",
" \n",
" @power.getter\n",
" def power(self):\n",
" return self._power\n",
" \n",
" @power.deleter\n",
" def power(self):\n",
" print(\"make robot useless\")\n",
" del self._power"
]
},
{
"cell_type": "markdown",
"id": "11ac651e",
"metadata": {},
"source": [
"Как раз здесь и появляется `property`. Давайте посмотрим, что это. В данном случае это тот же самый класс `Robot`, у которого `power` теперь является объектом `property`. Опять же, это встроенный объект, ниоткуда не нужно его импортировать. У этого `property` есть на самом деле три метода — это метод `getter`, метод `setter`, и метод `deleter`. Все эти три метода мы можем переопределить, чтобы менять поведение и выполнять какую-то полезную работу при обращении к атрибуту, при присваивании атрибуту какого-то нового значения либо при удалении атрибута. \n",
"\n",
"Давайте посмотрим, как это сделано. Мы объявили `class Robot`, и теперь `_power` у него является приватным атрибутом. А `power` без нижнего подчеркивания теперь является объектом `property`. Вот таким образом он инициализируется. Далее мы можем объявить три метода и обернуть их декораторами. `power.setter` — это тот метод, который будет выполняться, когда мы будем менять атрибут экземпляра `power`. `power.getter` — это тот метод, который будет выполняться, когда мы будем читать атрибут `power`. `power.deleter` — это тот метод, соответственно, который будет выполняться при удалении атрибута.\n",
"\n",
"Внутри этих методов мы можем выполнять какую-то полезную работу. Например, в методе `setter`, который принимает значение, которое пользователь пытается присвоить атрибуту, мы проверяем, что это значение меньше нуля, и, соответственно, ставим приватный атрибут power в ноль. То же самое, что мы делали в методе `set_power` чуть ранее. \n",
"\n",
"Соответственно, в методе `getter` объекта `property` мы возвращаем значение приватного атрибута `power`. Теперь, когда другой программист будет пользоваться вашим классом, он может по прежнему использовать старый подход и ставить атрибут экземпляру. Даже если он поставит отрицательный атрибут, этот атрибут будет выставлен в ноль в итоге, потому что отработает `setter` метод нашего свойства, нашего `property`.\n",
"\n",
"Если мы попробуем удалить этот атрибут, то выполнится `deleter` метод, который мы также переопределили, и, соответственно, удалит атрибут `power` из нашего экземпляра, тем самым сделав робота бесполезным."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "cdf26fee",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n"
]
}
],
"source": [
"wall_e = Robot(100)\n",
"wall_e.power = -20\n",
"print(wall_e.power)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "ace80de7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"make robot useless\n"
]
}
],
"source": [
"del wall_e.power"
]
},
{
"cell_type": "markdown",
"id": "1adc41dc",
"metadata": {},
"source": [
"Иногда нужно как-то модифицировать чтение атрибута и выполнять какую-то полезную работу при чтении, и это единственное, что вам требуется. То есть не нужно менять поведение при изменении значения атрибута либо при его удалении. В таком случае есть более короткая запись."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "5b4c26cb",
"metadata": {},
"outputs": [],
"source": [
"class Robot:\n",
" def __init__(self, power):\n",
" self._power = power\n",
" \n",
" @property\n",
" def power(self):\n",
" # здесь могут быть любые полезные вычисления\n",
" return self._power"
]
},
{
"cell_type": "markdown",
"id": "1be7bf60",
"metadata": {},
"source": [
"Мы можем объявить метод, обернуть его декоратором `property` без всяких суффиксов `getter`, `setter` и `deleter`, и это будет вычисляемым свойством класса, к которому можно обращаться вот так, как вы видите на слайде, то есть просто `power`. Удобно."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "09978b6e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"200"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wall_e = Robot(200)\n",
"wall_e.power"
]
},
{
"cell_type": "markdown",
"id": "d4425bc7",
"metadata": {},
"source": [
"На этой лекции мы рассмотрели, что такое статический метод. То есть в отличие от обычного метода экземпляра, который на вход принимает ссылку на конкретный экземпляр, и методы класса, который на вход принимает `class`, с которым идет работа, статический метод не принимает ничего, только те аргументы, которые в него передают. Используется он для того, когда нам из соображений удобства удобно обращаться к функции в контексте класса. Также мы посмотрели на такой концепт, как `property`, то есть вычисляемое свойство."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,368 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "7749287c",
"metadata": {},
"source": [
"# Пример на классы #"
]
},
{
"cell_type": "markdown",
"id": "3efae2d7",
"metadata": {},
"source": [
"Теперь от теории классов давайте перейдем к практике.\n",
"\n",
"Мы напишем небольшую программку, причем напишем её с нуля, с самого начала и эта программа будет содержать класс.\n",
"\n",
"Класс будет принимать на вход название города и будет иметь метод, который будет возвращать прогноз погоды в этом городе.\n",
"\n",
"На самом деле этот класс можно будет расширять бесконечно, например добавляя ему методы для получения грядущих событий в вашем любимом городе либо новостей относящихся к нему.\n",
"\n",
"Давайте начнём."
]
},
{
"cell_type": "markdown",
"id": "69e00a67",
"metadata": {},
"source": [
"Первое что мы сделаем, это создадим директорию в которой будем работать, перейдем в неё, создадим virtual env - вы уже умеете это делать. После того, как виртуальное окружение создастся мы должны его активировать. Нам понадобятся две сторонние библиотеки для работы нашей программы. Давайте их установим. Первая из них это библиотечка `requests`, а вторая это `python-dateutil`. `Request` нам потребуется чтобы делать `http` запросы, а `Python-dateutil` позволит преобразовывать даты, которые представлены в виде строки в Python'овское представление, то есть, в объекты модуля `datetime`. У нас установились наши библиотеки."
]
},
{
"cell_type": "markdown",
"id": "2c37bb80",
"metadata": {},
"source": [
" Сейчас я начну программировать в среде программирования `Visual Studio Code` или по другому говоря `VSCode`. Это новая для вас среда, возможно многие из вас с ней не знакомы. Вот собственно сейчас мы с ней и познакомимся. \n",
"\n",
"\n",
"Внутри `VSCode`'а мы откроем нашу директорию в которой мы только что создали виртуальное окружение. Это делается с помощью `file - open`. Открываем нашу директорию и вот она открылась. Мы видим наше виртуальное окружение. Следующим шагом мы должны указать, где же находится интерпретатор Python для нашего проекта. `Select wordspace interpreter`. Выбираем интерпретатор Python из нашего виртуального окружения. Внутри нашего проекта мы можем создать файлик. Назовем его `city.py`. Напомню что наша программка будет выдавать прогноз погоды в городе. Как только мы создали файл, `Visual Studio Code` предлагает нам установить `linter`, то есть специальные утилиты, которые помогают нам программировать. В данном случае мы установим `linter pep8` чтобы он следил за стилем нашего кода, а также `linter flake8`. Он будет помогать нам находить очевидные ошибки в нашем коде. `Pylint` мы не будем устанавливать, но это также замечательный `linter`. Теперь мы наконец-то можем программировать. С чего же нам начать? Давайте начнем со стандартного шага. Напишем `if __name__ = \"__main__\"`, то есть будем запускать нашу программу только тогда, когда она вызывается напрямую. А не импортируется. Внутри этой конструкции вызовем функцию `_main`. Назовем её начиная с символа нижнего подчеркивания, чтобы подчеркнуть то, что она является приватной и не должна использоватся из стороннего кода. Объявим эту функцию. Нам нужен класс, который будет принимать на вход имя города. Давайте начнем программу писать с конца. То есть не имея ещё класса объявим скелет нашей программы. Будем смотреть погоду в Москве."
]
},
{
"cell_type": "markdown",
"id": "c918e4d0",
"metadata": {},
"source": [
"У экземпляра класса `CityInfo` будет метод `weather_forecast`. Этот метод вернет нам список с прогнозом погоды на несколько дней вперед и последнее, что мы сделаем, это напечатаем его. Причем напечатаем красиво и будем для этого использовать модуль стандартной библиотеки `pprint` - это `pretty print`. Чтобы это заработало, нам конечно же нужно импортировать модуль `pretty print`. Теперь у нас есть код, который мы должны реализовать."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "791cc1a5",
"metadata": {},
"outputs": [],
"source": [
"import pprint\n",
"\n",
"def _main():\n",
" city = CityInfo(sys.argv[1])\n",
" forecast = city.weather_forecast()\n",
" pprint.pprint(forecast)\n",
" \n",
"if __name__ == \"__main__\":\n",
" _main()"
]
},
{
"cell_type": "markdown",
"id": "22e87f7b",
"metadata": {},
"source": [
"`Flake8` подчеркивает нам то, что у нас ещё нет класса `CityInfo`. Давайте его создадим. `Class CityInfo`. У него будет переопределен метод `__init__`, то есть иницализатор класса и он будет принимать название города. Устанавливаем атрибут экземпляра. И также у этого класса должен быть метод `weather_forecast`, который должен возвращать прогноз погоды. Пока он ничего не будет делать."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "da8d8628",
"metadata": {},
"outputs": [],
"source": [
"class CityInfo:\n",
" def __init__(self, city):\n",
" self.city = city\n",
" \n",
" def weather_forecast(self):\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "96ccaa75",
"metadata": {},
"source": [
"Давайте подумаем о том, откуда нам брать прогноз погоды. В интернете есть замечательный ресурс `yahoo weather api`, представляемый компанией `Yahoo`. Мы будем пользоваться им. Перейдем на их сайт и посмотрим как нам получить прогноз погоды. Вот в этой формочке мы можем поэкспериментировать с их `api`. На самом деле тут уже практически готов правильный запрос. Нужно только в одном месте поменять название места, где мы хотим смотреть прогноз погоды, на наш и указать, что результат мы хотим получать не в Фаренгейтах, а в Цельсиях, температуру. Я заранее посмотрел, как это делается, конечно же и мы можем нажать кнопочку тест и `yahoo` предоставляет нам ссылку, по которой мы можем перейти и увидеть прогноз погоды в Москве. На самом деле это большой `json`, внутри которого есть много вложенных полей. И прогноз погоды скрыт достаточно глубоко. Вот он. Мы видим, что здесь несколько объектов внутри списка по дням. Здесь есть, например, сегодняшняя дата и так далее.\n",
"\n",
"Наша цель - получить эти данные. Однако мы не будем просто так писать код, делающий http-запрос внутри метода `weather_forecast`. Мы создадим специальный класс, который будет называтся `YahooWeatherForecast`. У него будет метод `get`, который будет принимать город. И вот именно этот класс будет ответственный за то, чтобы ходить по сети и делать запросы к `yahoo`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "31b8e09c",
"metadata": {},
"outputs": [],
"source": [
"class YahooWeatherForecast:\n",
" def get(self, city):\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "2cb6eb49",
"metadata": {},
"source": [
"На самом деле чем это хорошо? А тем что мы этот класс впоследствии сможем подменить другим. Если вдруг с `API Yahoo` что-то случится, у нас всегда будет возможность поменять только один компонент нашей программы, только один класс заменить и весь остальной код программы продолжит работать. Давайте сделаем http-запрос. У нас в буфере обмена содержится тот url-адрес, который `Yahoo` нам предоставил. Это достаточно длинная строка и нам нужно в ней заменить название города `moscow` на то что мы получаем в аргументах, на наш `city`. По-хорошему эту строку желательно разбить на несколько, чтобы `pep8 linter` не ругался, но так как это пример, мы этого сейчас делать не будем, но на практике это нужно сделать.\n",
"\n",
"Теперь мы можем воспользоватся библиотечкой `requests`. Давайте сначала её импортируем. И мы можем получить данные. Делаем `requests.get(url).json` То есть преобразовываем ответ `json`'а в Python'овское представление. В данном случае это будет словарь. \n",
"\n",
"Теперь нам нужно достучаться до того вложенного объекта, который был в этих данных. Давайте это сделаем. Напишем, зададим переменную `forecast_data`, которая будет равнятся следующему."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f8bf861e",
"metadata": {},
"outputs": [],
"source": [
"class YahooWeatherForecast:\n",
" def get(self, city):\n",
" url = f\"http://query.yahooapis.com/city/{city}\"\n",
" data = requests.get(url).json()\n",
" \n",
" forecast_data = data[\"query\"][\"forecast\"]"
]
},
{
"cell_type": "markdown",
"id": "2dce3928",
"metadata": {},
"source": [
"Смотрите, у нас внутри этих данных на верхнем уровне находится словарь `query`. Обращаемся к нему. Дальше внутри `query` у нас список `forecast`. Список с прогнозом погоды на несколько дней. Здесь дата представлена как строка и также есть самая максимальная температура за день, она доступна по ключу `high`, мы будем брать дату и вот эту температуру самую максимальную. \n",
"\n",
"Определим список словарей `forecast` и заполним его данными. Для этого мы проитерируемся по `forecast_data`, полученным с Yahoo и заполним `forecast` данными, пригодными для нас.\n",
"\n",
"Возьмем дату оттуда, а также возьмем оттуда самую максимальную температуру за день. Что еще хотелось бы здесь сразу сделать, мы видим, что дата здесь представлена как строка. Как раз для этого нам пригодится модуль `dateutil`, который мы устанавливали в самом начале, в нем содержится полезная функция `parse`, внутри модуля `dateutil.parser`, внутри пакета и мы можем ее импортировать и с помощью ее преобразовать строку, содержащую дату в объект даты из модуля `datetime` стандартной библиотеки Python. И в принципе все, мы можем сделать `return forecast` и сохранить."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5e39fe6e",
"metadata": {},
"outputs": [],
"source": [
"from dateutil.parser import parse\n",
"\n",
"class YahooWeatherForecast:\n",
" def get(self, city):\n",
" url = f\"http://query.yahooapis.com/city/{city}\"\n",
" data = requests.get(url).json()\n",
" \n",
" forecast = []\n",
" forecast_data = data[\"query\"][\"forecast\"]\n",
" \n",
" for day_data in forecast_data:\n",
" forecast.append({\n",
" \"date\": parse(day_data[\"date\"]),\n",
" \"high_temp\": int(day_data[\"high\"])\n",
" })\n",
"\n",
" return forecast"
]
},
{
"cell_type": "markdown",
"id": "8cfbb47a",
"metadata": {},
"source": [
"Теперь мы вернемся к методу экземпляра `weather_forecast` и в этом методе мы обратимся к `YahooWeatherForecast` классу и вызовем его метод `get` для нужного города. Давайте сделаем, расширим метод `__init__`. Он ну нас будет принимать дополнительный параметр необязательный `forecast_provider` и ставить приватную переменную `_forecast_provider` экземпляру класса. Если же мы `forecast_provider` не передали, будет по умолчанию использоваться `YahooWeatherForecast` экземпляр."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "5b81e29b",
"metadata": {},
"outputs": [],
"source": [
"class CityInfo:\n",
" def __init__(self, city, forecast_provider=None):\n",
" self.city = city.lower()\n",
" self._forecast_provider = forecast_provider or YahooWeatherForecast()"
]
},
{
"cell_type": "markdown",
"id": "82101659",
"metadata": {},
"source": [
"Теперь мы внутри метода `weather_forecast` можем вызвать `self._forecast_provider`, то есть обратиться к приватному атрибуту и вызвать у него метод `get`, передав ему `self.city`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "db98eeda",
"metadata": {},
"outputs": [],
"source": [
"class CityInfo:\n",
" def __init__(self, city, forecast_provider=None):\n",
" self.city = city.lower()\n",
" self._forecast_provider = forecast_provider or YahooWeatherForecast()\n",
" \n",
" def weather_forecast(self):\n",
" return self._forecast_provider.get(self.city)"
]
},
{
"cell_type": "markdown",
"id": "08480c53",
"metadata": {},
"source": [
"На этом по идее у нас программа должна начать работать, давайте попробуем запустить ее. Перейдем в консоль. Мы видим, что мы получили прогноз погоды в Москве. Как мы можем улучшить наш код. Он уже сейчас работает, он достаточно расширяемый, но мы можем сделать еще лучше."
]
},
{
"cell_type": "markdown",
"id": "6d200fe3",
"metadata": {},
"source": [
"Давайте представим, что нашим приложением, которое мы пишем, пользуется достаточно много пользователей, и на самом деле приходит множество запросов о том, чтобы посмотреть прогноз погоды в Москве. Давайте это сэмулируем. Например, пусть пришло 5 запросов. В этот момент, на каждый из 5 запросов мы будем отсылать `http` запрос к `Yahoo`, для того, чтобы получить прогноз. Это не очень хорошо, потому что каждый `http` запрос - достаточно долгая операция, давайте посмотрим, как мы можем сделать лучше.\n",
"\n",
"Мы можем расширить класс `YahooWeatherForecast` и добавить к нему метод `__init__`. И создать внутри экземпляра приватных переменных, которые будут содержать в себе кэш данных по городам. \n",
"\n",
"Кэш представляет собой словарь, который содержит прогноз погоды в определенном городе, каждый раз, когда будет вызываться метод `get` нашего класса `YahooWeatherForecast`, мы будем проверять, что если город находится в словаре, то мы будем возвращать данные, которые принадлежат этому городу из этого `city` кэша. И каждый раз, когда мы получили данные, мы будем обновлять приватный словарик, приватный атрибут и ставить городу определенный прогноз погоды."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "472c7365",
"metadata": {},
"outputs": [],
"source": [
"class YahooWeatherForecast:\n",
" def __init__(self):\n",
" self._city_cache = {}\n",
" \n",
" def get(self, city):\n",
" if city in self._cached_data:\n",
" return self._cached_data[city]\n",
" \n",
" url = f\"http://query.yahooapis.com/city/{city}\"\n",
" \n",
" print(\"sending http request\")\n",
" data = requests.get(url).json()\n",
" \n",
" forecast = []\n",
" forecast_data = data[\"query\"][\"forecast\"]\n",
" \n",
" for day_data in forecast_data:\n",
" forecast.append({\n",
" \"date\": parse(day_data[\"date\"]),\n",
" \"high_temp\": int(day_data[\"high\"])\n",
" })\n",
"\n",
" return forecast"
]
},
{
"cell_type": "markdown",
"id": "55a4c91c",
"metadata": {},
"source": [
"Теперь, перед тем, как отправить `http` запрос, давайте напишем `print(\"sending http request\")`."
]
},
{
"cell_type": "markdown",
"id": "5e6c0394",
"metadata": {},
"source": [
"`sending http request` напечаталось только один раз. Это во-первых и быстрее работает в итоге и мы экономим на `http` запросах, например, это может быть полезно так как api-шки сторонние часто бывают платными.\n",
"\n",
"В данном примере мы применили композицию классов, то есть внутри класса `CityInfo` у нас атрибуту присвоен другой класс, часто это очень хороший подход к написанию кода. В данном примере мы не обрабатывали исключения, какие здесь они могли быть. Во-первых сайт `Yahoo` мог быть недоступен, во-вторых, каждый раз, когда мы обращались к ключу словаря мог произойти `key error`, то есть ключа такого могло не произойти, потому что `Yahoo` вернул нам невалидные данные. Также когда мы преобразовывали дату из строки в объект `datetime`, также мог произойти `exception`. Вы пока обрабатывать `exception` не умеете, но вам осталось узнать о классах совсем немножко про наследование и после этого мы научим вас обрабатывать исключения в программах. В данном случае мы написали класс, который умеет получать информацию о городе, причем этот класс достаточно поддерживаемый и его компоненты легко заменяемы. И также он расширяемый."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "f6663800",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2021-01-06 00:00:00\n",
"12\n",
"2021-02-06 00:00:00\n",
"18\n",
"2021-03-06 00:00:00\n",
"12\n",
"2021-04-06 00:00:00\n",
"17\n",
"2021-05-06 00:00:00\n",
"14\n",
"2021-06-06 00:00:00\n",
"14\n",
"2021-07-06 00:00:00\n",
"14\n",
"2021-08-06 00:00:00\n",
"13\n",
"2021-09-06 00:00:00\n",
"18\n"
]
}
],
"source": [
"from random import randint\n",
"from dateutil.parser import parse\n",
"\n",
"\n",
"forecast_data = [\n",
" {\n",
" \"date\": f\"0{i}/06/2021\",\n",
" \"high\": randint(12, 18)\n",
" } for i in range(1, 10)\n",
"]\n",
"\n",
"\n",
"for day_data in forecast_data:\n",
" print(parse(day_data[\"date\"]))\n",
" print(int(day_data[\"high\"]))"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,142 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "71482a2a",
"metadata": {},
"source": [
"# Тест по классам и объектам #"
]
},
{
"cell_type": "markdown",
"id": "a9a9349c",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "1ae3f26a",
"metadata": {},
"source": [
"##### 1. Как узнать тип объекта `obj`?\n",
"\n",
"- [ ] `isinstance(obj)`\n",
"- [ ] `type(obj)`"
]
},
{
"cell_type": "markdown",
"id": "f185dff8",
"metadata": {},
"source": [
"##### 2. Отметить что является классом:\n",
"\n",
"- [ ] `\"1024\"`\n",
"- [ ] `str`\n",
"- [ ] `1024`\n",
"- [ ] `class`\n",
"- [ ] `int`"
]
},
{
"cell_type": "markdown",
"id": "5eec52e6",
"metadata": {},
"source": [
"##### 3. Куда записываются атрибуты объекта?\n",
"\n",
"- [ ] `obj.__class__`\n",
"- [ ] `obj.__attrt__`\n",
"- [ ] `obj.__doc__`\n",
"- [ ] `obj.__dict__`"
]
},
{
"cell_type": "markdown",
"id": "e67e6935",
"metadata": {},
"source": [
"##### 4. Когда вызывается метод `__init__`?\n",
"\n",
"- [ ] При обращении к методу экземпляра\n",
"- [ ] При объявлении класса\n",
"- [ ] При создании экземпляра"
]
},
{
"cell_type": "markdown",
"id": "5e090536",
"metadata": {},
"source": [
"##### 5. Экземпляры классов хешируются?\n",
"\n",
"- [ ] Да\n",
"- [ ] Нет"
]
},
{
"cell_type": "markdown",
"id": "e425d1b2",
"metadata": {},
"source": [
"##### 6. Отметьте верные утверждения про `classmethod`\n",
"\n",
"- [ ] Метод первым аргументом принимает класс\n",
"- [ ] Метод не принимает дополнительных аргументов кроме указанных программистом\n",
"- [ ] К этому методу можно обращаться от экземпляра класса\n",
"- [ ] К этому методу можно обращаться от имени класса\n",
"- [ ] Метод первым аргументом принимает ссылку на экземпляр класса\n"
]
},
{
"cell_type": "markdown",
"id": "d11a3139",
"metadata": {},
"source": [
"##### 7. Отметьте верные утверждения про `staticmethod`\n",
"\n",
"- [ ] К этому методу можно обращаться от экземпляра класса\n",
"- [ ] Метод первым аргументом принимает ссылку на экземпляр класса\n",
"- [ ] Метод первым аргументом принимает класс\n",
"- [ ] Метод не принимает дополнительных аргументов кроме указанных программистом\n",
"- [ ] К этому методу можно обращаться от имени класса"
]
},
{
"cell_type": "markdown",
"id": "782a6823",
"metadata": {},
"source": [
"##### 8. Для чего используются `@property`?\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,133 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "71482a2a",
"metadata": {},
"source": [
"# Тест по классам и объектам #"
]
},
{
"cell_type": "markdown",
"id": "c37061e7",
"metadata": {},
"source": [
"1. Как узнать тип объекта `obj`?\n",
"\n",
"- [ ] `isinstance(obj)`\n",
"- [x] `type(obj)`"
]
},
{
"cell_type": "markdown",
"id": "f18159b4",
"metadata": {},
"source": [
"2. Отметить что является классом:\n",
"\n",
"- [ ] `\"1024\"`\n",
"- [x] `str`\n",
"- [ ] `1024`\n",
"- [ ] `class`\n",
"- [x] `int`"
]
},
{
"cell_type": "markdown",
"id": "aec77133",
"metadata": {},
"source": [
"3. Куда записываются атрибуты объекта?\n",
"\n",
"- [ ] `obj.__class__`\n",
"- [ ] `obj.__attrt__`\n",
"- [ ] `obj.__doc__`\n",
"- [x] `obj.__dict__`"
]
},
{
"cell_type": "markdown",
"id": "d4d6f3a9",
"metadata": {},
"source": [
"4. Когда вызывается метод `__init__`?\n",
"\n",
"- [ ] При обращении к методу экземпляра\n",
"- [ ] При объявлении класса\n",
"- [x] При создании экземпляра"
]
},
{
"cell_type": "markdown",
"id": "068e0ff4",
"metadata": {},
"source": [
"5. Экземпляры классов хешируются?\n",
"\n",
"- [x] Да\n",
"- [ ] Нет"
]
},
{
"cell_type": "markdown",
"id": "32585fbd",
"metadata": {},
"source": [
"6. Отметьте верные утверждения про `classmethod`\n",
"\n",
"- [x] Метод первым аргументом принимает класс\n",
"- [ ] Метод не принимает дополнительных аргументов кроме указанных программистом\n",
"- [x] К этому методу можно обращаться от экземпляра класса\n",
"- [x] К этому методу можно обращаться от имени класса\n",
"- [ ] Метод первым аргументом принимает ссылку на экземпляр класса\n"
]
},
{
"cell_type": "markdown",
"id": "6be13d07",
"metadata": {},
"source": [
"7. Отметьте верные утверждения про `staticmethod`\n",
"\n",
"- [x] К этому методу можно обращаться от экземпляра класса\n",
"- [ ] Метод первым аргументом принимает ссылку на экземпляр класса\n",
"- [ ] Метод первым аргументом принимает класс\n",
"- [x] Метод не принимает дополнительных аргументов кроме указанных программистом\n",
"- [x] К этому методу можно обращаться от имени класса"
]
},
{
"cell_type": "markdown",
"id": "5ccb9128",
"metadata": {},
"source": [
"8. Для чего используются `@property`?\n",
"\n",
"- [x] Чтобы создать вычисляемый атрибут\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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,41 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "fe31482e",
"metadata": {},
"source": [
"# Документация #"
]
},
{
"cell_type": "markdown",
"id": "ed3bdc2c",
"metadata": {},
"source": [
"[Наследование классов в python](https://docs.python.org/3/tutorial/classes.html#inheritance \"9.5. Inheritance\")"
]
}
],
"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.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,524 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "59ecb880",
"metadata": {},
"source": [
"# Композиция классов #"
]
},
{
"cell_type": "markdown",
"id": "c0953a7c",
"metadata": {},
"source": [
"В предыдущей лекции мы рассмотрели, как работает наследование и множественное наследование в классах Python. Как я уже говорил, если создать достаточно большую иерархию классов и использовать множественное наследование, а также большое количество классов-примесей, то итоговый код может показаться очень сложным, и его будет очень трудно читать программисту и разбираться, как он работает, что делает его менее выразительным.\n",
"\n",
"В Python существует альтернативный подход наследованию — это композиция.\n",
"\n",
"В этой лекции мы с вами на примере разберем, как работает композиция, и вы сможете сравнить, какой из подходов вам больше нравится — наследование или композиция.\n",
"\n",
"Давайте немного вспомним классы. У нас был класс \"питомец\", мы от него унаследовали класс \"собачку\" (класс `Dog`). Затем мы захотели, чтобы наши объекты классов \"собака\" могли выполнять экспорт данных, и мы ввели класс-примесь `ExportJSON`. После этого наш финальный класс `ExDog` использовал множественное наследование и наследовался от класса \"собачка\" и `ExportJSON`, тем самым он решал все наши задачи."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7abf9ef1",
"metadata": {},
"outputs": [],
"source": [
"class Pet:\n",
" pass\n",
"\n",
"class Dog(Pet):\n",
" pass\n",
" \n",
"class ExportJSON:\n",
" pass\n",
"\n",
"class ExDog(Dog, ExportJSON):\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "609625a7",
"metadata": {},
"source": [
"Давайте представим, что нам понадобится экспортировать данные не только в формате `json`, но и еще в другом формате, например, пусть будет это `XML`. Как тогда изменится структура нашей программы? Тогда, очевидно, нам нужно будет добавить еще один класс-примесь, и тогда наш код будет выглядеть так, как показано на слайде, то есть пока не вдаемся в подробности реализации самих классов-примесей."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e3465f4b",
"metadata": {},
"outputs": [],
"source": [
"class ExportJSON:\n",
" def to_json(self):\n",
" pass\n",
"\n",
"class ExportXML:\n",
" def to_xml(self):\n",
" pass\n",
" \n",
"class ExDog(Dog, ExportJSON, ExportXML):\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e5b3966",
"metadata": {},
"outputs": [],
"source": [
"dog = ExDog(\"Фокс\", \"мопс\")\n",
"dog.to_xml()\n",
"dog.to_json()"
]
},
{
"cell_type": "markdown",
"id": "55fa6dc7",
"metadata": {},
"source": [
"У нас появился наш новый класс-примесь `ExportXML`, у него есть свой собственный метод `to_xml`, и наш итоговый класс `ExDog` теперь наследуется уже от трех классов-родителей. Тем самым объекты этого класса `ExDog` смогут экспортировать данные как в `json`, так и в `xml`.\n",
"\n",
"Давайте представим, что нам нужно будет добавлять еще несколько методов для экспорта данных. Какие сложности могут возникнуть в таком случае?\n",
"\n",
"Во-первых, нам постоянно придется изменять код нашего класса `ExDog`, постоянно дописывать туда новые классы-примеси.\n",
"\n",
"Во-вторых, это уже сильно усложнит сам код, и в итоговой программе нам нужно будет вызывать разные методы этих классов-примесей, то есть мы будем постоянно что-то изменять в своей программе. И это уже не то, чего хотелось бы нам достичь.\n",
"\n",
"Давайте попробуем рассмотреть, как в таком случае работает композиция. Для этого нам понадобится Jupyter Notebook. Давайте попробуем решить эту задачу немного другим способом."
]
},
{
"cell_type": "markdown",
"id": "f698b300",
"metadata": {},
"source": [
"Предположим, у нас будет класс для экспорта. Давайте объявим его. Пусть он будет называться `PetExport` и у него будет один метод, `export`. Обратите внимание, я сейчас использовал генерацию исключения. Об исключениях мы с вами будем говорить в последующих лекциях, а пока вам нужно для себя усвоить, что мы не будем создавать объекты данного класса `PetExport` и он предназначен только для наследования. Давайте объявим другие классы, которые будут заниматься экспортом данных. `JSON`. Также наш класс `export` должен принимать сам объект, который он будет экспортировать. Пока опустим реализацию самого экспорта. Также добавим класс для экспорта данных в формате `xml`."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "b2e18c93",
"metadata": {},
"outputs": [],
"source": [
"class PetExport:\n",
" def export(self):\n",
" raise NotImplementedError\n",
"\n",
"class ExportJSON:\n",
" def export(self, dog):\n",
" pass\n",
" \n",
"class ExportXML:\n",
" def export(self, dog):\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "b5b69659",
"metadata": {},
"source": [
"Итак, у нас готова иерархия классов для экспорта данных. Давайте теперь вспомним наши классы. \"Питомец\", у которого у каждого питомца было имя, мы его сохраняли в атрибуте `name`, и также класс \"собачка\", у которого появился дополнительный атрибут порода, `breed`, и мы сохранили его в одноименном атрибуте."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "14a2f4cc",
"metadata": {},
"outputs": [],
"source": [
"class Pet:\n",
" def __init__(self, name=None):\n",
" self.name = name\n",
" \n",
" \n",
"class Dog(Pet):\n",
" def __init__(self, name, breed=None):\n",
" super().__init__(name)\n",
" self.breed = breed"
]
},
{
"cell_type": "markdown",
"id": "b3b38ead",
"metadata": {},
"source": [
"Давайте теперь объявим класс `ExDog`. Теперь мы не будем использовать свое множественное наследование, мы будем расширять существующий класс `Dog`. Переопределим инициализатор. Он тоже будет принимать на вход имя питомца, породу, а также дополнительный объект для экспорта данных. Вызовем конструктор базового класса и сохраним объект `exporter` в `self`."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "3098e00d",
"metadata": {},
"outputs": [],
"source": [
"class ExDog(Dog):\n",
" def __init__(self, name, breed=None, exporter=None):\n",
" super().__init__(name, breed)\n",
" self._exporter = exporter"
]
},
{
"cell_type": "markdown",
"id": "40256353",
"metadata": {},
"source": [
"Обратите внимание, что в данном классе мы не используем наследование. Вместо этого мы используем композицию и передаем нужный объект для экспорта в инициализаторе этого класса. Давайте добавим ему метод `export`, и теперь наш класс сможет экспортировать данные."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "e3cd85ac",
"metadata": {},
"outputs": [],
"source": [
"class ExDog(Dog):\n",
" def __init__(self, name, breed=None, exporter=None):\n",
" super().__init__(name, breed)\n",
" self._exporter = exporter\n",
" \n",
" def export(self):\n",
" return self._exporter.export(self)"
]
},
{
"cell_type": "markdown",
"id": "5484df82",
"metadata": {},
"source": [
"Делать он это будет при помощи нашего `exporter`'а. Передадим ему `self` для экспорта. \n",
"\n",
"Давайте попробуем создать экземпляр нашего класса `ExDog`. Пусть это будет собачка \"Шарик\", порода \"Дворняга\". Предположим, мы хотим, чтобы объект этого класса умел экспортировать свои данные в `xml`. Давайте передадим нужный `exporter`."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "0a9757ad",
"metadata": {},
"outputs": [],
"source": [
"dog = ExDog(\"Шарик\", \"Дворняга\", exporter=ExportXML())"
]
},
{
"cell_type": "markdown",
"id": "63846315",
"metadata": {},
"source": [
"Обратите внимание, что при использовании композиции нужный объект создается именно в момент выполнения уже конкретной программы. Давайте попробуем выполнить метод `export`."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "d2a062c4",
"metadata": {},
"outputs": [],
"source": [
"dog.export()"
]
},
{
"cell_type": "markdown",
"id": "f88aac87",
"metadata": {},
"source": [
"Осталось реализовать только методы для экспорта в начальной иерархии классов. Давайте сделаем это. С `json` все просто, мы уже разбирали пример, используя модуль `json` и метод `dumps`."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "cfded06f",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"class ExportJSON:\n",
" def export(self, dog):\n",
" return json.dumps(\n",
" {\n",
" \"name\": dog.name,\n",
" \"breed\": dog.breed\n",
" }\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "09aad031",
"metadata": {},
"source": [
"Давайте реализуем теперь метод `export` в классе `ExportXML`. Реализация может выглядеть достаточно просто. Создаем сам `xml`. В нем будут объекты `name` и порода нашей собаки. Всё, осталось отформатировать данную строку при помощи метода `format`."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "376dfd98",
"metadata": {},
"outputs": [],
"source": [
"class ExportXML:\n",
" def export(self, dog):\n",
" return \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<dog>\n",
" <name>{name}</name>\n",
" <breed>{breed}</breed>\n",
"</dog>\n",
"\"\"\".format(name=dog.name, breed=dog.breed)"
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "bcfad312",
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"'<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n<dog>\\n <name>Шарик</name>\\n <breed>Дворняга</breed>\\n</dog>\\n'"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dog = ExDog(\"Шарик\", \"Дворняга\", exporter=ExportXML())\n",
"dog.export()"
]
},
{
"cell_type": "markdown",
"id": "5be78f28",
"metadata": {},
"source": [
"Точно так же мы с легкостью можем сделать экспорт данных и в формате `json`. Можем создать другую собачку. Пусть это будет \"Тузик\" другой породы."
]
},
{
"cell_type": "code",
"execution_count": 39,
"id": "ef8c4c01",
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"'{\"name\": \"\\\\u0422\\\\u0443\\\\u0437\\\\u0438\\\\u043a\", \"breed\": \"\\\\u041c\\\\u043e\\\\u043f\\\\u0441\"}'"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dog = ExDog(\"Тузик\", \"Мопс\", exporter=ExportJSON())\n",
"dog.export()"
]
},
{
"cell_type": "markdown",
"id": "c52e1bfa",
"metadata": {},
"source": [
"Однако, неудобно, если каждый раз задавать метод для экспорта. Давайте немного изменим наш класс `ExDog` и зададим метод для экспорта по умолчанию. Можно это сделать следующим образом. \n",
"\n",
"Давайте выполним еще одну важную вещь. Мы проверим на то, является ли переданный объект экземпляром класса `PetExport` и вообще может ли он выполнять экспорт данных. Для этого мы можем воспользоваться проверкой `isinstance`. Нам нужен наш `exporter` и указываем класс. Что делать, если нам передали объект, который не может выполнять экспорт? Давайте пока сгенерируем исключение.\n",
"Для вас это будет означать, что программа дальше не сможет продолжить свою работу и будет остановлена. Например, `ValueError` нам подойдет."
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "624c763b",
"metadata": {},
"outputs": [],
"source": [
"class ExDog(Dog):\n",
" def __init__(self, name, breed=None, exporter=None):\n",
" super().__init__(name, breed)\n",
" \n",
" self._exporter = exporter or ExportJSON()\n",
" \n",
" if not isinstance(self._exporter, PetExport):\n",
" raise ValueError(\"bad exporter\", exporter)\n",
" \n",
" def export(self):\n",
" return self._exporter.export(self)"
]
},
{
"cell_type": "code",
"execution_count": 42,
"id": "812aff80",
"metadata": {},
"outputs": [],
"source": [
"class ExportJSON(PetExport):\n",
" def export(self, dog):\n",
" return json.dumps(\n",
" {\n",
" \"name\": dog.name,\n",
" \"breed\": dog.breed\n",
" }\n",
" )\n",
"\n",
"class ExportXML(PetExport):\n",
" def export(self, dog):\n",
" return \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<dog>\n",
" <name>{name}</name>\n",
" <breed>{breed}</breed>\n",
"</dog>\n",
"\"\"\".format(name=dog.name, breed=dog.breed)"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "41174209",
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"'{\"name\": \"\\\\u0422\\\\u0443\\\\u0437\\\\u0438\\\\u043a\", \"breed\": \"\\\\u041c\\\\u043e\\\\u043f\\\\u0441\"}'"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dog = ExDog(\"Тузик\", \"Мопс\")\n",
"dog.export()"
]
},
{
"cell_type": "markdown",
"id": "fe1efbd1",
"metadata": {},
"source": [
"Теперь, если мы не объявим объект `exporter`, по умолчанию наша собачка будет экспортировать данные в `json`. Давайте еще раз посмотрим на то, какой код у нас получился, и подумаем, что с ним произойдет, если его нужно будет расширить. "
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "4bb4fda8",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"class Pet:\n",
" def __init__(self, name=None):\n",
" self.name = name\n",
" \n",
" \n",
"class Dog(Pet):\n",
" def __init__(self, name, breed=None):\n",
" super().__init__(name)\n",
" self.breed = breed\n",
" \n",
" \n",
"class PetExport:\n",
" def export(self):\n",
" raise NotImplementedError\n",
"\n",
" \n",
"class ExportJSON(PetExport):\n",
" def export(self, dog):\n",
" return json.dumps(\n",
" {\n",
" \"name\": dog.name,\n",
" \"breed\": dog.breed\n",
" }\n",
" )\n",
"\n",
" \n",
"class ExportXML(PetExport):\n",
" def export(self, dog):\n",
" return \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<dog>\n",
" <name>{name}</name>\n",
" <breed>{breed}</breed>\n",
"</dog>\n",
"\"\"\".format(name=dog.name, breed=dog.breed)\n",
" \n",
" \n",
"class ExDog(Dog):\n",
" def __init__(self, name, breed=None, exporter=None):\n",
" super().__init__(name, breed)\n",
" \n",
" self._exporter = exporter or ExportJSON()\n",
" \n",
" if not isinstance(self._exporter, PetExport):\n",
" raise ValueError(\"bad exporter\", exporter)\n",
" \n",
" def export(self):\n",
" return self._exporter.export(self)"
]
},
{
"cell_type": "markdown",
"id": "291bee59",
"metadata": {},
"source": [
"Предположим, если нам нужно будет добавить в этот код новый метод для экспорта, мы с легкостью сможем сделать это. Просто объявим новый класс, добавим его в существующую иерархию для экспорта, а класс `ExDog` теперь мы больше менять не будем. Таким образом, если у нас система будет усложняться, наш класс `ExDog` будет оставаться неизменяемым или неизменным. А экспортировать в различные форматы мы уже сможем легко и удобно в итоговой программе, подставив нужный exporter или создав его.\n",
"\n",
"Не пугайтесь, если вам сейчас непонятны плюсы данного подхода по отношению к наследованию. Чем вы больше будете программировать на Python, пробовать и реализовывать различные подходы, тем вам станет понятно, где действительно лучше применять наследование, а в каких местах следует использовать композицию."
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,618 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "20e9c323",
"metadata": {},
"source": [
"# Наследование в Python #"
]
},
{
"cell_type": "markdown",
"id": "19ca4a24",
"metadata": {},
"source": [
"Вы уже знаете достаточно большое количество информации о языке Python.\n",
"\n",
"Например, то, что всё в Python — это объект, и это очень сильно отличает его от других языков программирования.\n",
"\n",
"Также вы умеете объявлять свои собственные классы, добавлять к ним атрибуты, создавать методы, а также создавать экземпляры этих классов и обращаться к атрибутам и методам.\n",
"\n",
"Однако это далеко не все возможности объектно-ориентированного языка Python. Сегодня мы углубимся в детали реализации классов и поговорим про наследование.\n",
"\n",
"Для чего же нужно наследование классов? Прежде всего оно нужно для изменения поведения конкретного класса, а также расширения его функционала. Давайте представим, что нам необходимо программировать процессы, которые происходят на нашей планете Земля, и мы хотим населить нашу планету Земля домашними питомцами.\n",
"\n",
"Предположим, у нас есть класс, назовем его \"питомец\" или \"домашний питомец\". И у каждого домашнего питомца есть имя."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "54da589d",
"metadata": {},
"outputs": [],
"source": [
"class Pet:\n",
" def __init__(self, name=None):\n",
" self.name = name"
]
},
{
"cell_type": "markdown",
"id": "3ed53134",
"metadata": {},
"source": [
"Нам неинтересно населять планету Земля непонятными питомцами и давайте попробуем ее населить, например, собаками."
]
},
{
"cell_type": "markdown",
"id": "103a7252",
"metadata": {},
"source": [
"Для этого нам поможет наследование. Класс \"питомец\" может использоваться уже другими программистами, и нам не хотелось бы его менять. Поэтому давайте попробуем расширить его.\n",
"\n",
"Наследование в Python выглядит очень просто, мы объявляем класс \"собака\", класс `Dog`, и в скобках указываем родительский класс \"питомец\". Новый класс, созданный при помощи наследования, наследует все атрибуты и методы родительского класса. В данном случае класс \"питомец\" является родительским классом, его также еще называют базовым классом или суперклассом. А класс \"собака\" называется дочерним классом или классом-наследником."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b28d0ecd",
"metadata": {},
"outputs": [],
"source": [
"class Dog(Pet):\n",
" def __init__(self, name, breed=None):\n",
" super().__init__(name)\n",
" self.breed = breed\n",
" \n",
" def say(self):\n",
" return f\"{self.name}: waw\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7248d621",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Шарик\n",
"Шарик: waw\n"
]
}
],
"source": [
"dog = Dog(\"Шарик\", \"Доберман\")\n",
"print(dog.name)\n",
"print(dog.say())"
]
},
{
"cell_type": "markdown",
"id": "f61360a2",
"metadata": {},
"source": [
"Давайте попробуем изменить поведение нашего класса \"питомец\". Для этого мы переопределим метод `init`, или инициализатор класса, и добавим новый атрибут `breed`, в котором будем хранить породу собаки. Если этот атрибут равен значению `None`, то порода не определена. Также обратите внимание, мы в нашем методе `init` вызываем метод `init` из родительского класса. Для этого мы пользуемся функцией `super`.\n",
"\n",
"Хочу обратить ваше внимание, что мы именно не скопировали код из родительского класса, а именно обратились, вызвали его, тем самым мы расширили поведение этого класса.\n",
"\n",
"Также мы можем добавить, кроме атрибутов, и собственный метод. Давайте назовем его `say` и напишем реализацию. Таким образом, наши питомцы, или наши собаки, умеют подавать голос, если мы обратимся к этому методу `say`.\n",
"\n",
"Ну что же, мы создали класс при помощи наследования, который решает нужные нам задачи. Вы можете создать свои классы, унаследовать от класса \"питомец\" и населить планету Земля различными животными, птицами, рыбами, котами, какими угодно."
]
},
{
"cell_type": "markdown",
"id": "aecc21a3",
"metadata": {},
"source": [
"В Python разрешено наследование от нескольких классов предков, или как это еще называется, множественное наследование. Очень часто этот прием используется для реализации класса примесей. \n",
"\n",
"Предположим, что нам необходимо экспортировать данные о наших объектах собачках в формате `json`, для того чтобы хранить эти данные на жестком диске, либо передавать по сети. Мы можем решить подобную задачу при помощи класса примесей и множественного наследования. Объявим класс `ExportJSON`, реализуем метод, который экспортирует данные в формате `json`, и создадим новый класс, который называется `ExDog`, и он будет наследоваться от класса \"собака\", `dog`, и нашего нового класса примесей, `ExportJSON`."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "5ac468ac",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"class ExportJSON:\n",
" def to_json(self):\n",
" return json.dumps(\n",
" {\n",
" \"name\": self.name,\n",
" \"breed\": self.breed\n",
" }\n",
" )\n",
"\n",
"class ExDog(Dog, ExportJSON):\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "2a5229c2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\"name\": \"\\u0411\\u0435\\u043b\\u043a\\u0430\", \"breed\": \"\\u0414\\u0432\\u043e\\u0440\\u043d\\u044f\\u0436\\u043a\\u0430\"}\n"
]
}
],
"source": [
"dog = ExDog(\"Белка\", breed=\"Дворняжка\")\n",
"print(dog.to_json())"
]
},
{
"cell_type": "markdown",
"id": "8c6e5c84",
"metadata": {},
"source": [
"Созданный класс при помощи множественного наследования объединяет в себе свойства всех родительских классов. Если мы создадим объект `dog`, который будет являться экземпляром нашего нового класса `ExDog`, то мы сможем обратиться к методу `to_json`, который является методом примесей `ExportJSON`.\n",
"\n",
"С одной стороны, это кажется очень удобным и гибким, однако множественное наследование и использование большого количества примесей имеет ряд своих недостатков и минусов.\n",
"\n",
"Если у вас будет очень сложная иерархия классов и большое количество примесей, то код станет менее выразительным, и программисту, который разбирается с вашим кодом, будет достаточно тяжело его читать. Поэтому не стоит сильно увлекаться и создавать большое количество классов примесей.\n",
"\n",
"Любой класс в Python является потомком класса `object`. Мы можем легко убедиться в этом, если попробуем использовать функцию `issubclass`. Например, мы можем ее вызвать для класса `int`, и она вернет значение \"истина\". Также мы можем попробовать проверить, является ли наш класс `Dog`, \"собачка\", потомком класса `object` или потомком класса \"питомец\"."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b5a24c8d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(int, object)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "365c1aec",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(Dog, object)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "ed18b7da",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(Dog, Pet)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "4a623d15",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(Dog, int)"
]
},
{
"cell_type": "markdown",
"id": "c6af4304",
"metadata": {},
"source": [
"Однако наш класс \"собака\" не является потомком для класса `int`, и эта функция вернет значение \"ложь\". Также при помощи функции `isinstance` мы можем проверять, является ли конкретный объект экземпляром нужного нам класса, и, например, мы можем создать объект \"собака\" и проверить, является ли наш объект экземпляром класса `Dog`, `Pet` или `Object`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "1bb8a142",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(dog, Dog)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "ac1239f4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(dog, Pet)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "8bfa0602",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(dog, object)"
]
},
{
"cell_type": "markdown",
"id": "1c2b0b7f",
"metadata": {},
"source": [
"Все эти три вызова вернут значение \"истина\". Вы можете использовать эти функции `issubclass`, `isinstance` в своем коде, они очень часто вам будут нужны.\n",
"\n",
"При помощи наследования Python позволяет выстраивать достаточно сложные иерархии классов. Несмотря на то, что мы создали небольшое количество классов, давайте взглянем на то, как выглядит наша полученная иерархия в итоге."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "7c8d63fb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(__main__.ExDog, __main__.Dog, __main__.Pet, __main__.ExportJSON, object)"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"\"\"\n",
"\n",
" object\n",
" / \\\n",
"Pet ExportJSON\n",
" | /\n",
"Dog /\n",
" \\ /\n",
" ExDog\n",
"\n",
"\"\"\"\n",
"\n",
"ExDog.__mro__"
]
},
{
"cell_type": "markdown",
"id": "821a6937",
"metadata": {},
"source": [
"Так, у нас есть класс `ExDog`, который мы создали при помощи множественного наследования от класса `Dog` и класса примесей `ExportJSON`. В свою очередь, класс `Dog` наследуется от класса \"питомец\", и все остальные классы наследуются от класса `object`. Если мы попробуем создать экземпляр класса `ExDog` и обратиться к атрибуту `name`, то как же Python будет искать этот атрибут в существующей иерархии классов? Для этого в Python существует так называемый `Method Resolution Order`, или порядок разрешения методов, и он устроен не так просто, как вам бы могло показаться. \n",
"\n",
"Однако, и вообще, в целом это отдельная тема для изучения. Однако все, что вам нужно знать, это порядок, в котором Python ищет нужный атрибут или метод. Итак, у нас есть иерархия классов, связи, все родительские, прародительские классы, и обратиться к этому списку можно при помощи атрибута `__mro__`. Если мы попробуем обратиться к атрибуту `name`, то Python будет искать сначала в классе `ExDog`, затем `Dog`, и после того как он обратится к классу `Pet`, нужный атрибут `name` будет найден. Данный список еще называется линеаризацией класса, то есть Python любые атрибуты и методы ищет в этом списке линеаризации. Если он пройдется по всему списку и не найдет, то будет сгенерировано исключение `AttributeError`."
]
},
{
"cell_type": "markdown",
"id": "68e92ddb",
"metadata": {},
"source": [
"Про исключения мы с вами будем говорить позже. Теперь вы знаете, как Python ищет атрибуты и методы в сложной иерархии классов. Двигаемся дальше.\n",
"\n",
"В самом начале, когда мы создавали класс `Dog`, мы рассматривали вызов инициализатора базового класса. Это выглядит достаточно просто, мы просто вызываем `super` без параметров, и происходит вызов функции или метода из базового класса. Однако в Python можно обратиться не только к базовому классу, но и к любому методу в существующей иерархии. Как это делается? Давайте рассмотрим очередной пример."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a08baca6",
"metadata": {},
"outputs": [],
"source": [
"class ExDog(Dog, ExportJSON):\n",
" def __init__(self, name, breed=None):\n",
" super().__init__(name, breed)\n",
" # super(ExDog, self).__init__(name)"
]
},
{
"cell_type": "markdown",
"id": "59557161",
"metadata": {},
"source": [
"Вызов функции `super` без параметров равносилен тому, если б мы указали сам класс и передали туда объект `self`. Опускать параметры очень удобно и зачастую вам часто именно так и придется делать. Однако если вам необходимо вызвать метод конкретного класса, то в функцию `super` надо передать его родителя. Хочу обратить ваше внимание, что именно нужно передавать родителя. Итак, если мы создадим новый класс `WoolenDog` и захотим обратиться к инициализатору класса \"питомец\", то нам необходимо в функции `super` указать класс \"родитель\". Если мы попробуем создать объект класса `Dog` и обратиться к атрибуту `breed`, то получим вывод, который показан на слайде."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "25b98630",
"metadata": {},
"outputs": [],
"source": [
"class WoolenDog(Dog, ExportJSON):\n",
" def __init__(self, name, breed=None):\n",
" super(Dog, self).__init__(name)\n",
" self.breed = f\"Шерстяная собака породы {breed}\""
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "d74097fc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Шерстяная собака породы Такса\n"
]
}
],
"source": [
"dog = WoolenDog(\"Жучка\", breed=\"Такса\")\n",
"print(dog.breed)"
]
},
{
"cell_type": "markdown",
"id": "ff956135",
"metadata": {},
"source": [
"Тем самым, вы теперь знаете, как работает функция `super` и как можно обратиться к любому методу в сложной иерархии классов.\n",
"\n",
"Также в Python существуют приватные атрибуты. Давайте поговорим немного о них. Для того чтобы создать приватный атрибут, необходимо его имя записать через два символа нижнего подчёркивания. Предположим, мы атрибут `breed` или породу собак решили сделать приватным, объявили его следующим образом, тогда в самом классе к нему можно обращаться так же, а вот для классов-наследников этот атрибут будет уже недоступен."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "fc92c066",
"metadata": {},
"outputs": [],
"source": [
"class Dog(Pet):\n",
" def __init__(self, name, breed=None):\n",
" super().__init__(name)\n",
" self.__breed = breed\n",
" \n",
" def say(self):\n",
" return f\"{self.name}: waw\"\n",
" \n",
" def get_breed(self):\n",
" return self.__breed"
]
},
{
"cell_type": "markdown",
"id": "b94634e4",
"metadata": {},
"source": [
"Давайте попробуем выполнить код, который указан на слайде в Python ноутбуке. Для этого нам потребуется код этих классов. Давайте сделаем это. Итак, у нас есть класс \"Собака\", который унаследован от \"Питомца\". У класса \"Собаки\" есть приватный атрибут `breed` и обращение к нему. Итак, наш итоговый класс `ExDog`, в котором есть функция `get_breed`, и в ней записано обращение к приватному атрибуту суперкласса."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "79743a39",
"metadata": {},
"outputs": [],
"source": [
"class ExDog(Dog, ExportJSON):\n",
" def get_breed(self):\n",
" return f\"Порода: {self.name} - {self.__breed}\""
]
},
{
"cell_type": "markdown",
"id": "3b9ebc1f",
"metadata": {},
"source": [
"Давайте попробуем создать объект нашего класса `ExDog`. Пусть собаку зовут \"Тузик\", и её порода будет \"питбуль\". Давайте теперь попробуем вызвать метод `get_breed` и попробуем получить её породу."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "9dd3da45",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Фокс', '_Dog__breed': 'Мопс'}"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dog = ExDog(\"Фокс\", \"Мопс\")\n",
"dog.__dict__"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "cc812895",
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'ExDog' object has no attribute '_ExDog__breed'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-22-c2424d891e87>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdog\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_breed\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32m<ipython-input-20-01db35386576>\u001b[0m in \u001b[0;36mget_breed\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mExDog\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mDog\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mExportJSON\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mget_breed\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[1;34mf\"Порода: {self.name} - {self.__breed}\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m: 'ExDog' object has no attribute '_ExDog__breed'"
]
}
],
"source": [
"dog.get_breed()"
]
},
{
"cell_type": "markdown",
"id": "a685d675",
"metadata": {},
"source": [
"Как мы видим, у нас не получилось это сделать, возникло исключение `AttributeError`. Давайте попробуем разобраться, в чём же дело. Можно распечатать внутренний атрибут `__dict__`, который нам покажет все атрибуты нашего созданного объекта. Итак, мы видим, что вместо атрибута `__breed` Python автоматически поставил имя класса. \n",
"\n",
"В целом, если нам очень нужно обратиться к этому приватному атрибуту, Python позволяет это сделать, и мы можем исправить код нашего класса, добавить префикс с классом Dog и попробовать ещё раз обратиться к нему."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "3076e8c5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Порода: Фокс - Мопс'"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class ExDog(Dog, ExportJSON):\n",
" def get_breed(self):\n",
" return f\"Порода: {self.name} - {self._Dog__breed}\"\n",
" \n",
"dog = ExDog(\"Фокс\", \"Мопс\")\n",
"dog.get_breed()"
]
},
{
"cell_type": "markdown",
"id": "6bac3f91",
"metadata": {},
"source": [
"Итак, у нас получилось. То есть, как я уже сказал, Python позволяет обращаться к приватным атрибутам класса вне самого класса, однако, лучше сильно этим не увлекаться.\n",
"\n",
"Мы обсудили с вами то, как устроено наследование классов в Python, мы поговорили про множественное наследование, обсудили, как Python ищет атрибуты и методы в сложной иерархии классов. Также поговорили о вызове методов при помощи функции `super` в сложной иерархии классов и обсудили приватные атрибуты или `name mangling`. \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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,107 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "76f6cab6",
"metadata": {},
"source": [
"# Тест по наследованию #"
]
},
{
"cell_type": "markdown",
"id": "3799df11",
"metadata": {},
"source": [
"##### Вас зовут:\n",
"___"
]
},
{
"cell_type": "markdown",
"id": "2b586a08",
"metadata": {},
"source": [
"##### 1. Наследование классов нужно:\n",
"\n",
"- [ ] для создания экземпляров класса\n",
"- [ ] для изменения поведения класса\n",
"- [ ] для расширения функционала класса\n",
"- [ ] для ограничения доступа к атрибутам класса предка"
]
},
{
"cell_type": "markdown",
"id": "fe834941",
"metadata": {},
"source": [
"##### 2. Выберите истинные утверждения\n",
"\n",
"- [ ] Для вызова нужного метода используется линеаризация класса\n",
"- [ ] В **Python** разрешено множественное наследование\n",
"- [ ] Все классы в **python** унаследованы от класса `object`\n",
"- [ ] Классы-примеси используются в множественном наследовании"
]
},
{
"cell_type": "markdown",
"id": "353fd015",
"metadata": {},
"source": [
"##### 3. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте все варианты вызова метода `Pet.__init__` из инициализатора класса потомка.\n",
"\n",
"- [ ] `super(Dog, self).__init__()`\n",
"- [ ] `super(Pet, self).__init__()`\n",
"- [ ] `super().__init__()`"
]
},
{
"cell_type": "markdown",
"id": "7d3d9230",
"metadata": {},
"source": [
"##### 4. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте варианты, которые вернут `True`\n",
"\n",
"- [ ] `issubclass(Pet, Dog)`\n",
"- [ ] `issubclass(Dog, object)`\n",
"- [ ] `issubclass(Pet, object)`\n",
"- [ ] `issubclass(Dog, Pet)`"
]
},
{
"cell_type": "markdown",
"id": "24f973e5",
"metadata": {},
"source": [
"##### 5. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте варианты, которые вернут `True`\n",
"\n",
"- [ ] `isinstance(Dog(), Dog)`\n",
"- [ ] `isinstance(Dog(), Pet)`\n",
"- [ ] `isinstance(Dog, Dog)`\n",
"- [ ] `isinstance(Pet(), Dog)`\n",
"- [ ] `isinstance(Pet(), object)`"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,98 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "76f6cab6",
"metadata": {},
"source": [
"# Тест по наследованию #"
]
},
{
"cell_type": "markdown",
"id": "2b586a08",
"metadata": {},
"source": [
"1. Наследование классов нужно:\n",
"\n",
"- [ ] для создания экземпляров класса\n",
"- [x] для изменения поведения класса\n",
"- [x] для расширения функционала класса\n",
"- [ ] для ограничения доступа к атрибутам класса предка"
]
},
{
"cell_type": "markdown",
"id": "fe834941",
"metadata": {},
"source": [
"2. Выберите истинные утверждения\n",
"\n",
"- [x] Для вызова нужного метода используется линеаризация класса\n",
"- [x] В **Python** разрешено множественное наследование\n",
"- [x] Все классы в **python** унаследованы от класса `object`\n",
"- [x] Классы-примеси используются в множественном наследовании"
]
},
{
"cell_type": "markdown",
"id": "353fd015",
"metadata": {},
"source": [
"3. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте все варианты вызова метода `Pet.__init__` из инициализатора класса потомка.\n",
"\n",
"- [x] `super(Dog, self).__init__()`\n",
"- [ ] `super(Pet, self).__init__()`\n",
"- [x] `super().__init__()`"
]
},
{
"cell_type": "markdown",
"id": "7d3d9230",
"metadata": {},
"source": [
" 4. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте варианты, которые вернут `True`\n",
"\n",
"- [ ] `issubclass(Pet, Dog)`\n",
"- [x] `issubclass(Dog, object)`\n",
"- [x] `issubclass(Pet, object)`\n",
"- [x] `issubclass(Dog, Pet)`"
]
},
{
"cell_type": "markdown",
"id": "24f973e5",
"metadata": {},
"source": [
"5. Предположим есть базовый класс питомец - `Pet` и класс наследник - `Dog`. Отметьте варианты, которые вернут `True`\n",
"\n",
"- [x] `isinstance(Dog(), Dog)`\n",
"- [x] `isinstance(Dog(), Pet)`\n",
"- [ ] `isinstance(Dog, Dog)`\n",
"- [ ] `isinstance(Pet(), Dog)`\n",
"- [x] `isinstance(Pet(), object)`"
]
}
],
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,8 @@
car_type;brand;passenger_seats_count;photo_file_name;body_whl;carrying;extra
car;Nissan xTtrail;4;f1.jpeg;;2.5;
truck;Man;;f2.png;8x3x2.5;20;
truck;Man;;f2.png;;20;
car;Mazda 6;4;f3.jpeg;;2.5;
;;;
spec_machine;Hitachi;;f4;;1.2;Легкая техника для уборки снега
1 car_type;brand;passenger_seats_count;photo_file_name;body_whl;carrying;extra
2 car;Nissan xTtrail;4;f1.jpeg;;2.5;
3 truck;Man;;f2.png;8x3x2.5;20;
4 truck;Man;;f2.png;;20;
5 car;Mazda 6;4;f3.jpeg;;2.5;
6 ;;;
7 spec_machine;Hitachi;;f4;;1.2;Легкая техника для уборки снега

@ -0,0 +1,142 @@
"""Решение задачи про классы и наследование"""
from csv import reader
from functools import reduce
from os.path import splitext
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import List
class CarBase:
"""Базовый класс для автомобилей и спецтехники"""
def __init__(
self,
brand: "str",
photo_file_name: "str",
carrying: "float",
car_type: "str" = "car",
):
"""Конструктор базового класса"""
self.brand: "str" = brand
self.photo_file_name: "str" = photo_file_name
self.carrying: "float" = carrying
self.__car_type: "str" = car_type
def __repr__(self) -> "str":
"""Строковое представления объекта"""
return f"{self.car_type}: {self.brand} {self.carrying}"
@property
def car_type(self) -> "str":
"""Тип автомобиля или спецтехники"""
return self.__car_type
def get_photo_file_ext(self) -> "str":
"""Предоставляет расширение файла с фото"""
return splitext(self.photo_file_name)[1]
class Car(CarBase):
"""Класс легковых автомобилей"""
def __init__(
self,
brand: "str",
photo_file_name: "str",
carrying: "float",
passenger_seats_count: "int",
):
"""Конструктор класса легковых автомобилей"""
super().__init__(brand, photo_file_name, carrying)
self.passenger_seats_count: "int" = passenger_seats_count
class Truck(CarBase):
"""Класс грузовых автомобилей"""
def __init__(
self,
brand: "str",
photo_file_name: "str",
carrying: "float",
body_whl: "str",
):
"""Конструктор класса грузовых автомобилей"""
super().__init__(brand, photo_file_name, carrying, "truck")
self.body_width: "float" = 0.0
self.body_height: "float" = 0.0
self.body_length: "float" = 0.0
if body_whl:
try:
self.body_width, self.body_height, self.body_length = tuple(
map(float, body_whl.split("x", maxsplit=2))
)
except ValueError as error:
raise ValueError(
"Invalid format argument body_whl."
) from error
def get_body_volume(self) -> "float":
"""Возвращает объем кузова в метрах кубических"""
return reduce(
lambda x, y: x * y,
(self.body_width, self.body_height, self.body_length),
)
class SpecMachine(CarBase):
"""Класс спецтехники"""
def __init__(
self,
brand: "str",
photo_file_name: "str",
carrying: "float",
extra: "str",
):
"""Конструктор класса спецтехники"""
super().__init__(brand, photo_file_name, carrying, "spec_machine")
self.extra: "str" = extra
def get_car_list(csv_filename: "str") -> "List[CarBase]":
"""Чтение данных из csv файла и представление их в виде списка объектов"""
car_list: "List[CarBase]" = []
with open(csv_filename, encoding="UTF-8") as csv_fd:
for i, row in enumerate(reader(csv_fd, delimiter=";")):
if i and len(row) == 7:
try:
if row[0] == "car":
car_list.append(
Car(row[1], row[3], float(row[5]), int(row[2]))
)
elif row[0] == "truck":
car_list.append(
Truck(row[1], row[3], float(row[5]), row[4])
)
elif row[0] == "spec_machine":
car_list.append(
SpecMachine(row[1], row[3], float(row[5]), row[6])
)
except ValueError:
continue
return car_list

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save