summaryrefslogtreecommitdiff
path: root/PYTHON
diff options
context:
space:
mode:
authorvlapa <vlapa@ya.ru>2026-06-13 20:33:26 +0300
committervlapa <vlapa@ya.ru>2026-06-13 20:33:26 +0300
commite5abc1fe99340b979b18864d978179f71fe8f5c5 (patch)
tree98fd0ad7c6d0744894d6e74e446daacd9e6dd1b5 /PYTHON
First
Diffstat (limited to 'PYTHON')
-rw-r--r--PYTHON/-args, --kwargs.md43
-rw-r--r--PYTHON/12 однострочных конструкций.md65
-rw-r--r--PYTHON/8Gb файл открыть на ОЗУ 2Gb.md9
-rw-r--r--PYTHON/AsyncIO.md155
-rw-r--r--PYTHON/BEGIN + PIP.md226
-rw-r--r--PYTHON/COM-порт.md41
-rw-r--r--PYTHON/DAEMON на Python.md126
-rw-r--r--PYTHON/DEFAULTDICT.md33
-rw-r--r--PYTHON/DJANGO.md36
-rw-r--r--PYTHON/ENUMERATE, ENUM.md11
-rw-r--r--PYTHON/ENUMERATE, ZIP.md2
-rw-r--r--PYTHON/FOR, WHILE.md227
-rw-r--r--PYTHON/HTTP.md18
-rw-r--r--PYTHON/LIST COMPREHENSION.md6
-rw-r--r--PYTHON/MAP.md15
-rw-r--r--PYTHON/MATH.md1
-rw-r--r--PYTHON/Matprotlib.md29
-rw-r--r--PYTHON/NAMEDTUPLE.md18
-rw-r--r--PYTHON/OPEN - работа с файлами.md39
-rw-r--r--PYTHON/OS.md18
-rw-r--r--PYTHON/PATH.md111
-rw-r--r--PYTHON/PAUSE в PYTHON.md13
-rw-r--r--PYTHON/PEP8.md37
-rw-r--r--PYTHON/PROGRAMS.md160
-rw-r--r--PYTHON/PROGRAMS_py.md150
-rw-r--r--PYTHON/PROGRESS BAR.md138
-rw-r--r--PYTHON/PYINSTALLER.md164
-rw-r--r--PYTHON/PYTHON_tuturial.md2910
-rw-r--r--PYTHON/Password & key = PATH.md94
-rw-r--r--PYTHON/PrettyTable.md15
-rw-r--r--PYTHON/Python-cheatsheet.md3717
-rw-r--r--PYTHON/ROUND, CEIL, FLOOR.md20
-rw-r--r--PYTHON/SET, DICT, LIST, TUPLE, RANGE.md219
-rw-r--r--PYTHON/SPEEDTEST python.md51
-rw-r--r--PYTHON/SPLIT, JOIN.md2
-rw-r--r--PYTHON/STRING.md121
-rw-r--r--PYTHON/SnakeViz.md15
-rw-r--r--PYTHON/TELEGRAMM BOT.md59
-rw-r--r--PYTHON/THREADING.md128
-rw-r--r--PYTHON/TKINTER.md52
-rw-r--r--PYTHON/TRY ... EXCEPT.md269
-rw-r--r--PYTHON/Translators.md20
-rw-r--r--PYTHON/WITH.md28
-rw-r--r--PYTHON/any, all.md22
-rw-r--r--PYTHON/argparse.md5
-rw-r--r--PYTHON/dir().md17
-rw-r--r--PYTHON/exec.md6
-rw-r--r--PYTHON/hash() - хэширование.md19
-rw-r--r--PYTHON/itertools.md8
-rw-r--r--PYTHON/keys() + &.md8
-rw-r--r--PYTHON/nmcli - Работа с сетевыми интерфейсами.md56
-rw-r--r--PYTHON/pandas.md2
-rw-r--r--PYTHON/partition().md8
-rw-r--r--PYTHON/poyo.md35
-rw-r--r--PYTHON/pprint().md17
-rw-r--r--PYTHON/sleep().md8
-rw-r--r--PYTHON/tabulate.md12
-rw-r--r--PYTHON/Библиотеки для ленивых.md83
-rw-r--r--PYTHON/Возврат нескольких значений.md5
-rw-r--r--PYTHON/Генерируем QR-код.md10
-rw-r--r--PYTHON/Контекстные менеджеры.md8
-rw-r--r--PYTHON/Копируем файл в другой каталог.md4
-rw-r--r--PYTHON/Множественный ввод.md2
-rw-r--r--PYTHON/Моржовый оператор.md17
-rw-r--r--PYTHON/ОБНОВЛЕНИЕ до последней версии.md98
-rw-r--r--PYTHON/ОТЛАДКА КОДА.md32
-rw-r--r--PYTHON/Программа-напоминалка на Python.md156
-rw-r--r--PYTHON/Протоколы.md13
-rw-r--r--PYTHON/Своя библиотека для Python.md161
-rw-r--r--PYTHON/Строим линейные графики с помощью Python.md6
-rw-r--r--PYTHON/Тест кода в терминале.md27
-rw-r--r--PYTHON/Улучшаем код на Python.md650
-rw-r--r--PYTHON/ФУНКЦИИ.md192
73 files changed, 11298 insertions, 0 deletions
diff --git a/PYTHON/-args, --kwargs.md b/PYTHON/-args, --kwargs.md
new file mode 100644
index 0000000..22029c2
--- /dev/null
+++ b/PYTHON/-args, --kwargs.md
@@ -0,0 +1,43 @@
+**args**, когда неуверены, сколько аргументов будет передано функции, или если хотим передать сохраненный список или кортеж аргументов функции. 
+
+**kwargs** используется, когда мы не знаем, сколько аргументов ключевых слов будет передано, а также он может быть использован для передачи значений словаря в качестве аргументов ключевых слов. Идентификаторы args и kwargs необязательны, вы можете изменить их на другие, типа *другой **пример, но лучше использовать имя по умолчанию.
+
+```python
+# Пример *args
+def sample( *args) :
+print(args)
+sample( 'time', 1, True)
+
+# Пример **kwargs
+def sample(**kwargs) :
+print(kwargs)
+sample(a = 'time', b = 1)
+```
+
+====================================
+В Python можно распаковывать коллекции в аргументы или структуры — это мощно.
+
+```python
+#_***args и kwargs — собирают аргументы:_
+
+def f(*args, **kwargs):
+    print(args, kwargs)
+
+f(1, 2, x=10)  # (1, 2), {'x': 10}
+
+# 👍 _* и ** — распаковывают:_
+
+nums = [1, 2, 3]
+print(*nums)  # 1 2 3
+
+
+params = {"sep": " | "}
+print(1, 2, 3, **params)  # 1 | 2 | 3
+
+#_Распаковка в переменные:_
+
+a, *middle, b = [1, 2, 3, 4, 5]
+print(middle)  # [2, 3, 4]
+```
+
+**Где применять: передача параметров, адаптация API, читаемый код**
diff --git a/PYTHON/12 однострочных конструкций.md b/PYTHON/12 однострочных конструкций.md
new file mode 100644
index 0000000..1e5d8be
--- /dev/null
+++ b/PYTHON/12 однострочных конструкций.md
@@ -0,0 +1,65 @@
+`pip instal bpython`
+
+```python
+x, y, z = map(int, input().strip().split())
+print(f"{x=}, {y=}, {z=}")
+
+x, y, z = map(int, (x, y, z))
+
+###############
+from functools import reduce
+
+volume = reduce(lambda x, y: x * y, map(int, input().strip().split()))
+print(f"(volume= {volume}))
+
+###############
+names = ['Христофор', 'Адемар', 'Тэя', 'Стефания', 'Архип']
+names_starts_a = [name for name in names if name.startswith('A')]
+print(names_starts_a)
+
+###############
+names = ['Христофор', 'Адемар', 'Тэя', 'Стефания', 'Архип']
+names_starts_a = filter(lambda name: name.startswith('A'), names)
+print(list(names_starts_a))
+
+###############
+numbers = [1, 2, 3]
+
+# Копирование массива:
+another_numbers = numbers[:]
+
+# Перевернуть массив:
+numbers[::-1]
+
+# Проверка на вхождение:
+name = "Василий"
+if name in ('Алексей', 'Пётр', 'Христофор'):
+ print(name)
+
+# Проверка все-ли True:
+if all((a, b, c, d, e)): print("All True")
+
+# Проверка если хотя бы один True:
+if any((a, b, c, d, e)): print("Any True")
+###############
+# Тернарный оператор:
+admin_email = "admin@site.ru" if config.IS_PRODACTION else "mymail@mail.com"
+
+# Выбор метода в зависимости от <group>:
+class User:
+ def __init__(self, group: str):
+ self.group = group
+
+user = User(group='admin')
+
+group_to_process_method = {
+ 'admin': process_admin_request,
+ 'manager': process_manager_request,
+ 'client': process_client_request,
+ 'anon': process_anon_request,
+
+}
+
+
+```
+
diff --git a/PYTHON/8Gb файл открыть на ОЗУ 2Gb.md b/PYTHON/8Gb файл открыть на ОЗУ 2Gb.md
new file mode 100644
index 0000000..6ee4267
--- /dev/null
+++ b/PYTHON/8Gb файл открыть на ОЗУ 2Gb.md
@@ -0,0 +1,9 @@
+#### Как прочитать файл объемом 8 ГБ на Python с помощью компьютера с 2 ГБ ОЗУ?
+
+Это решение работает для любых больших файлов.
+
+Когда вы открываете файл, всё, что вам нужно сделать, это использовать объект файла в качестве итератора: при циклическом просмотре этого объекта, вы будете извлекать по одной строке за раз, а предыдущие строки будут удалены из памяти.
+
+Таким образом, файл никогда не будет полностью загружен в память, и ваша обработка будет выполняться на ходу.
+
+![[Pasted image 20250214130256.png]] \ No newline at end of file
diff --git a/PYTHON/AsyncIO.md b/PYTHON/AsyncIO.md
new file mode 100644
index 0000000..2c8e365
--- /dev/null
+++ b/PYTHON/AsyncIO.md
@@ -0,0 +1,155 @@
+### Под капотом asyncio: принципы работы и ключевые концепции ###
+
+**Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях.
+
+Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях.
+
+Этот материал взят из нашей субботней [email-рассылки](https://proglib.io/w/edaee37b), посвященной Python. Подпишитесь, чтобы быть в числе первых, кто получит дайджест.
+
+**Asyncio** – библиотека, которая предоставляет инфраструктуру для написания параллельного кода с использованием концепции асинхронного программирования. Она позволяет эффективно обрабатывать многочисленные задачи ввода-вывода (например, сетевые операции или чтение/запись из файлов) без необходимости создавать множество потоков или процессов. Ключевые концепции asyncio:
+
+**Событийный цикл (Event Loop)**
+
+- Это ядро **asyncio**, которое отвечает за планирование и выполнение задач (корутин).
+- Реализован на языке **C** для максимальной эффективности.
+- Работает по принципу **однопоточной многозадачности** (single-threaded concurrency).
+- Постоянно опрашивает очередь событий и выполняет соответствующие задачи.
+
+**Корутины (Coroutines)**
+
+- Это специальные функции, выполнение которых можно приостанавливать на определенных точках с помощью ключевого слова `await`.
+- Используются для написания асинхронного, событийно-ориентированного кода.
+- Могут ждать асинхронные события (ввод-вывод, сигналы или другие корутины).
+- Определяются с помощью `async def`.
+
+**Задачи (Tasks)**
+
+- Представляют собой обертки над корутинами для их планирования и выполнения.
+- Создаются с помощью `asyncio.create_task(coroutine)` или `asyncio.ensure_future(coroutine)`.
+- Задачи можно отменять, объединять с другими задачами и отслеживать на предмет исключений.
+- Задачи добавляются в событийный цикл и выполняются в порядке их готовности.
+
+**Будущие объекты (Futures)**
+
+- Представляют результат асинхронной операции, который может быть доступен позже.
+- Задачи наследуются от класса `Future` и добавляют собственные методы управления.
+- Могут быть использованы для координации между разными частями программы.
+
+**Транспорты и протоколы**
+
+- **Транспорты** (sockets) отвечают за **передачу** данных по сети.
+- **Протоколы** определяют, как **интерпретировать** эти данные (HTTP, WebSocket и др.).
+- asyncio предоставляет высокоуровневые абстракции для работы с транспортами и протоколами.
+
+**Другие инструменты**
+
+- Пулы для управления ограниченными ресурсами (потоки, подпроцессы).
+- Синхронизаторы для координации между корутинами (**Lock****,** **Event**, **Condition)**.
+- Очереди для безопасной передачи данных между корутинами.
+- Сигналы для обработки внешних событий (UNIX-сигналы).
+
+**Вот как работает asyncio:**
+
+- Создается **событийный цикл**, который будет управлять выполнением задач.
+- Определяются корутины, которые выполняют некоторую работу и могут приостанавливаться на операциях ввода-вывода с помощью ключевого слова `await`.
+- Корутины оборачиваются в задачи с помощью функции `asyncio.create_task()`.
+- Задачи планируются для выполнения в событийном цикле с помощью функции `asyncio.run()` или добавляются в цикл вручную.
+- Событийный цикл начинает выполнять задачи по очереди. Когда задача достигает операции ввода-вывода (например, `await asyncio.sleep()`), она приостанавливается, и событийный цикл переключается на другую задачу.
+- Когда операция ввода-вывода завершается, задача возобновляется и продолжает выполнение до следующей операции ввода-вывода или завершения. 
+
+Статья по теме
+
+[Упрощаем разработку: асинхронные функции Python](https://proglib.io/p/uproshchaem-razrabotku-asinhronnye-funkcii-python-2019-10-06)
+
+Так asyncio позволяет эффективно использовать **один** поток для выполнения **множества** задач с операциями ввода-вывода, избегая блокировки и простоев из-за ожидания завершения этих операций.
+
+Важно отметить, что asyncio оптимален **именно для I/O-bound задач** (сеть, файловые операции и т. д.), и **не подходит для CPU-bound операций** (вычислительно-интенсивных задач), поскольку в этом случае он не сможет переключаться на другие задачи во время блокировки. Для такого рода задач лучше использовать многопоточность или многопроцессорность. Подробнее о принципах работы asyncio – в [этой статье](https://jacobpadilla.com/articles/recreating-asyncio) и [в этом видео](https://www.youtube.com/watch?v=RIVcqT2OGPA)[](http://savefrom.net/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRIVcqT2OGPA&utm_source=userjs-safari&utm_medium=extensions&utm_campaign=link_modifier "Получи прямую ссылку").
+
+=======================================
+🚀 **asyncio** – это библиотека для асинхронного программирования, позволяющая писать код, выполняющий несколько задач параллельно (без потоков и процессов).
+
+✅ async def – объявление асинхронной функции (корутина).
+✅ await – приостанавливает выполнение до завершения асинхронной операции.
+✅ asyncio.run() – запускает главную асинхронную функцию.
+✅ asyncio.create_task() – запускает корутину в фоновом режиме.
+✅ asyncio.sleep() – имитирует задержку без блокировки других задач.
+
+```python
+import asyncio
+
+async def say_hello():
+    await asyncio.sleep(1)  # Имитация задержки
+    print("Привет, мир!")
+
+async def main():
+    task1 = asyncio.create_task(say_hello())
+    task2 = asyncio.create_task(say_hello())
+    await task1
+    await task2
+
+asyncio.run(main())  # Запуск асинхронной программы
+```
+**Выведет с задержкой 1 секунду:**
+
+Привет, мир!
+Привет, мир!
+
+Обе задачи выполняются **параллельно**, а не последовательно!
+
+📌 Когда использовать asyncio?
+
+✅ Запросы в интернет (aiohttp).
+✅ Работа с базами данных (aiomysql, asyncpg).
+✅ Асинхронное чтение файлов/сокетов.
+✅ Многозадачность без потоков и процессов.
+
+#======================================
+**Вот как работает asyncio — без магии:**
+
+📌 1. **Event Loop — это «планировщик», который гоняет корутины. Он сам не создаёт потоки. Он ждёт, когда одна задача «освободит» CPU, и сразу берётся за другую.**
+```python
+import asyncio
+
+async def main():
+    print("Start")
+    await asyncio.sleep(1)
+    print("End")
+
+asyncio.run(main())
+```
+Здесь await asyncio.sleep(1) — не блокирует поток. Это ***yield*,** который говорит: "я пока жду, можешь заняться чем-то ещё".
+
+📌 2. **Ты не должен использовать time.sleep() в async-коде. Это блокирует весь event loop. Заменяй на await asyncio.sleep().**
+
+📌 3. **Любая синхронная тяжёлая операция — угроза. Например, чтение файла или SQL-запрос через обычный драйвер.**
+
+✔️ Как решить:
+
+👍 Используй aiofiles для файлов.
+👍 Используй async-драйверы (asyncpg, aiomysql, httpx и т.д.)
+👍 Если нет async-аналога — выноси в ThreadPoolExecutor.
+```python
+from concurrent.futures import ThreadPoolExecutor
+import asyncio
+
+def blocking():
+    ...
+
+async def main():
+    loop = asyncio.get_running_loop()
+    await loop.run_in_executor(None, blocking)
+```
+
+📌 4.**Не забывай про семафоры, если запускаешь 1000 задач одновременно. async не значит всё сразу.**
+```python
+sem = asyncio.Semaphore(100)
+
+async def safe_task():
+    async with sem:
+        await do_something()
+```
+
+🧠 asyncio — это не про параллельность. Это про эффективное переключение задач, которые  ждут  чего-то: ответа сервера, данных, паузы.
+
+🗣 **Применяется в: Web-серверах (FastAPI, aiohttp), парсинге, бэкенде с I/O, высоконагруженных API.**
+
diff --git a/PYTHON/BEGIN + PIP.md b/PYTHON/BEGIN + PIP.md
new file mode 100644
index 0000000..d67c8c0
--- /dev/null
+++ b/PYTHON/BEGIN + PIP.md
@@ -0,0 +1,226 @@
+
+![[Python_шпаргалка.png]]
+
+```bash
+which python3
+which pip
+
+sudo apt install -y python3-pip
+pip install numpy
+
+sudo apt install -y python3-virtualenv
+which virtualenv
+
+# в папке проекта для ДЕактивации среды:
+deactivate
+
+#=========================================
+cd code
+mkdir pythontests
+cd pythontests
+
+python3 -m venv .venv-name
+. ./.venv-name/bin/activate
+
+nvim pyrightconfig.json
+
+{
+ "venv": "env",
+ "venvPath": "."
+}
+
+# в папке проекта для ДЕактивации среды:
+deactivate
+
+#==========================================
+# Список пакетов с зависимостями:
+pip freeze
+
+# Создаем файл с зависимостями:
+pip freeze > requirements.txt
+
+# Устанавливаем все зависимости в другом месте:
+pip install -r requirements.txt
+
+# список установленных пакетов:
+pip list
+
+# удалить пакет:
+pip uninstall <packet> -y
+
+```
+
+```sh
+# Активация в Windows:
+python -m venv venv
+.\venv\Scripts\activate
+
+# Создаем файл с зависимостями:
+pip freeze > requirements.txt
+
+# создаем файл requirements.txt (со всеми необходимыми зависимостями) и запускаем команду:
+pip install -r .\requirements.txt
+
+```
+```sh
+git clone user@address:~/packet.git
+cd packet
+
+# Создаем сам скрипт:
+vim install.sh
+
+# Вносим в него:
+apt update
+apt install -y gcc python3-dev python3-venv
+python3 -m venv env
+source env-allocation/bin/activate
+pip install wheel
+pip install -r requirements.txt
+export KEY_SECRET=''
+
+# возможно нужно дать разрешение на запуск!
+sudo sh ./install.sh
+
+sudo python3 ./runner.py
+
+```
+=========================
+Вот как поместить Python-программу в systemd для автозапуска:
+
+## 1. Создайте unit-файл для systemd
+
+Создайте файл в `/etc/systemd/system/` (для системных сервисов) или `~/.config/systemd/user/` (для пользовательских сервисов):
+
+bash
+
+sudo nano /etc/systemd/system/myapp.service
+
+## 2. Пример конфигурации unit-файла
+
+ini
+
+[Unit]
+Description=My Python Application
+After=network.target
+
+[Service]
+Type=simple
+# Если используете виртуальное окружение:
+ExecStart=/path/to/venv/bin/python /path/to/your/app.py
+# Или без виртуального окружения:
+# ExecStart=/usr/bin/python3 /path/to/your/app.py
+
+# Рабочая директория
+WorkingDirectory=/path/to/your/app/directory
+
+# Пользователь для запуска (опционально)
+User=username
+Group=groupname
+
+# Перезапуск при ошибке
+Restart=always
+RestartSec=5
+
+# Переменные окружения
+Environment="PYTHONPATH=/path/to/your/app"
+Environment="DATABASE_URL=mysql://user:pass@localhost/db"
+
+[Install]
+WantedBy=multi-user.target
+
+## 3. Для приложений с зависимостями (виртуальное окружение)
+
+ini
+
+[Service]
+Type=simple
+ExecStart=/home/user/myapp/venv/bin/python /home/user/myapp/main.py
+WorkingDirectory=/home/user/myapp
+Environment="PATH=/home/user/myapp/venv/bin"
+
+## 4. Основные команды для управления
+
+bash
+
+# Загрузить изменения конфигурации
+sudo systemctl daemon-reload
+
+# Запустить сервис
+sudo systemctl start myapp
+
+# Остановить сервис
+sudo systemctl stop myapp
+
+# Включить автозапуск при загрузке
+sudo systemctl enable myapp
+
+# Проверить статус
+sudo systemctl status myapp
+
+# Просмотреть логи
+sudo journalctl -u myapp -f
+
+## 5. Полезные опции для Python-приложений
+
+- **`Restart`** - политика перезапуска:
+
+ - `no` - не перезапускать
+
+ - `on-success` - только при успешном завершении
+
+ - `on-failure` - при ошибке (рекомендуется)
+
+ - `always` - всегда перезапускать
+
+- **`StandardOutput`/`StandardError`** - куда перенаправлять вывод:
+
+ ini
+
+ StandardOutput=journal
+ StandardError=journal
+ # Или в файл:
+ StandardOutput=file:/var/log/myapp.log
+ ## 6. Пример для Flask/Django приложения
+
+ini
+
+[Unit]
+Description=My Flask Web Application
+After=network.target mysql.service
+
+[Service]
+Type=simple
+User=www-data
+Group=www-data
+WorkingDirectory=/var/www/myapp
+ExecStart=/var/www/myapp/venv/bin/gunicorn --workers 3 --bind unix:/tmp/myapp.sock wsgi:app
+Environment="FLASK_ENV=production"
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
+
+## 7. Проверка и отладка
+
+bash
+
+# Проверить синтаксис конфигурации
+sudo systemd-analyze verify /etc/systemd/system/myapp.service
+
+# Тестовый запуск без systemd
+sudo -u username /path/to/python /path/to/app.py
+
+# Подробные логи
+sudo journalctl -u myapp -n 100 -f
+
+## Советы:
+
+1. Убедитесь, что Python-скрипт имеет права на выполнение
+
+2. Используйте абсолютные пути во всех настройках
+
+3. Для разработки можно запускать как user-сервис без sudo
+
+4. Для приложений с GUI может понадобиться `Environment="DISPLAY=:0"`
+
+=====================
diff --git a/PYTHON/COM-порт.md b/PYTHON/COM-порт.md
new file mode 100644
index 0000000..b1706c3
--- /dev/null
+++ b/PYTHON/COM-порт.md
@@ -0,0 +1,41 @@
+```python
+import serial
+
+from serial.tools import list_ports
+ports = list(list_ports.comports())
+i = 1
+for p in ports:
+ print(i, ' - ', p.device)
+ i += 1
+
+port = 8 # int(input('Введите номер COM-порта: '))
+baudrate = 115200 # int(input('Введите скорость COM-порта: '))
+
+while port > len(ports):
+ print("Такого порта - НЕТ !")
+ port = int(input('Введите номер COM-порта: '))
+
+print(ports[port - 1].device)
+
+try:
+ ser = serial.Serial(ports[port - 1].device, baudrate=baudrate)
+ print("Serial connection established.")
+
+ while True:
+ line = ser.readline().decode().strip()
+
+ if line:
+ print(line)
+
+except serial.SerialException as se:
+ print("Serial port error:", str(se))
+
+except KeyboardInterrupt:
+ pass
+
+finally:
+ if ser.is_open:
+ ser.close()
+ print("Serial connection closed.")
+```
+
diff --git a/PYTHON/DAEMON на Python.md b/PYTHON/DAEMON на Python.md
new file mode 100644
index 0000000..94416cc
--- /dev/null
+++ b/PYTHON/DAEMON на Python.md
@@ -0,0 +1,126 @@
+
+Демон — это процесс, который работает в фоновом режиме, обычно без прямого взаимодействия с пользователем. Демоны часто запускаются системой при старте и продолжают работать независимо от пользовательских сеансов.
+
+### Разница между демоном и обычным процессом
+
+Отличие демона от обычного процесса состоит в том, что демон отделён от терминала, не имеет своего интерфейса ввода-вывода и продолжает работать в фоновом режиме, даже когда пользователь завершает свою сессию.
+
+### Области применения демонов
+
+- Проверка состояния серверов и служб.
+- Автоматическая фоновая обработка данных.
+- Выполнение периодических задач, таких как обновление баз данных или отправка отчетов.
+
+## Основные библиотеки для создания демонов
+
+Для создания демонов на языке Python существует несколько популярных библиотек:
+
+- `daemon`: Простая и удобная библиотека для создания демонов.
+- `systemd`: Более продвинутый инструмент для управления демонами, интегрированный с системой и использующий все её возможности.
+- `multiprocessing`: Позволяет создавать многопроцессорные приложения с функциональностью демонов.
+
+### Преимущества и недостатки каждой библиотеки
+
+- **daemon**:
+- Преимущества: Простота использования.
+- Недостатки: Ограниченные возможности конфигурации и интеграции с системой.
+
+- **systemd**:
+- Преимущества: Глубокая интеграция с системой, мощные возможности конфигурации.
+- Недостатки: Требует знания специфики конфигурации systemd.
+
+- **multiprocessing**:
+- Преимущества: Поддержка многопроцессорных задач.
+- Недостатки: Сложность в настройке и управлении демонами.
+
+## Подготовка окружения Python
+
+Перед тем как начать писать код демона, необходимо установить необходимые библиотеки и настроить окружение Python.
+
+### Установка необходимых библиотек
+
+```plain
+pip install daemon
+```
+
+## Написание базового демона
+
+Создадим базовую структуру демона, уделяя внимание функции `run_as_daemon()`.
+
+### Создание базовой структуры демон-программы
+
+```python
+import daemon
+import time
+
+def run() -> None:
+ """Основная функция работы демона."""
+ while True:
+ print('Демон работает...')
+ time.sleep(5)
+
+if __name__ == '__main__':
+ with daemon.DaemonContext():
+ run()
+```
+
+## Логирование и какие библиотеки использовать
+
+Логирование позволяет отслеживать события и ошибки, что облегчает диагностику и поддержку демона.
+
+### Примеры настройки логирования
+
+```python
+import logging
+
+logging.basicConfig(level=logging.INFO, filename='daemon.log')
+
+def run() -> None:
+ """Основная функция работы демона с логированием."""
+ while True:
+ logging.info('Демон работает...')
+ time.sleep(5)
+```
+
+## Обработка сигналов
+
+Сигналы позволяют управлять работой демона, например, корректно завершить его работу.
+
+### Объяснение, что такое сигналы и как их обработка помогает управлять демоном
+
+Сигналы — это механизмы межпроцессного взаимодействия, позволяющие одному процессу посылать уведомления другому.
+
+### Примеры обработки сигналов, таких как SIGTERM и SIGINT
+
+```python
+import signal
+
+def handle_signal(signum: int, frame) -> None:
+ """Обработка сигнала завершения."""
+ logging.info('Демон завершает работу...')
+ exit(0)
+
+signal.signal(signal.SIGTERM, handle_signal)
+signal.signal(signal.SIGINT, handle_signal)
+```
+
+### Создание systemd юнита для автоматического запуска демона
+```sh
+[Unit]
+Description=Мой Демон
+
+[Service]
+ExecStart=/path/to/your/daemon
+
+[Install]
+WantedBy=multi-user.target
+```
+
+### Как управлять демоном с помощью системных команд
+```sh
+sudo systemctl start my-daemon.service
+sudo systemctl stop my-daemon.service
+sudo systemctl enable my-daemon.service
+sudo systemctl disable my-daemon.service
+```
+
diff --git a/PYTHON/DEFAULTDICT.md b/PYTHON/DEFAULTDICT.md
new file mode 100644
index 0000000..4efc9f9
--- /dev/null
+++ b/PYTHON/DEFAULTDICT.md
@@ -0,0 +1,33 @@
+**Использование коллекций** defaultdict
+
+Часто при работе со словарями в Python приходится проверять наличие ключа перед его использованием.
+
+Вместо этого можно использовать defaultdict из модуля collections.
+
+`from collections import defaultdict  
+
+# Создаем словарь с дефолтным значением 0  
+
+`word_count = defaultdict(int)  
+
+`words = ["python", "code", "python", "script", "code", "python"]  
+
+`for word in words:  
+
+    word_count[word] += 1  # Автоматически создаются новые ключи  
+
+`print(word_count)  
+
+# Вывод: defaultdict(<class 'int'>, {'python': 3, 'code': 2, 'script': 1})
+
+**📌 Почему это удобно?**
+
+🟢 Не нужно вручную проверять наличие ключа
+
+🟢 Автоматически создаются значения по умолчанию
+
+🟢 Полезно для счетчиков, группировки и работы со структурами данных
+
+
+
+➡️ Также можно использовать `defaultdict(list)` или `defaultdict(set)` для удобного хранения данных. \ No newline at end of file
diff --git a/PYTHON/DJANGO.md b/PYTHON/DJANGO.md
new file mode 100644
index 0000000..07d5ac8
--- /dev/null
+++ b/PYTHON/DJANGO.md
@@ -0,0 +1,36 @@
+```bash
+python3 -V
+
+python3 -m venv venv
+
+. venv/bin/activate
+# deactivate - выключить вирт окружение
+
+pip install --upgrade pip
+pip install django
+
+django-admin startproject coolsite
+cd coolsite
+python3 manage.py runserver
+# CTRL_C
+
+python3 manage.py startapp women
+```
+
+```python
+# coolsite/settings.py
+'women.apps.WomenConfig'
+
+# women/views.py
+from django.http import HttpResponse
+
+def index(Request): #HttpRequest
+ return HttpResponse("Страница приложения women.")
+
+# coolsite/urls.py
+from coolsite.women.views import index
+
+path('women/', index),
+
+
+``` \ No newline at end of file
diff --git a/PYTHON/ENUMERATE, ENUM.md b/PYTHON/ENUMERATE, ENUM.md
new file mode 100644
index 0000000..ee2ed64
--- /dev/null
+++ b/PYTHON/ENUMERATE, ENUM.md
@@ -0,0 +1,11 @@
+Итерация по любому содержимому в Python проста – обычный цикл for. Однако очень часто требуются одновременно и индекс элемента, и сам элемент. Программисты используют len() и range() для перебора списка по индексу, но есть более простой способ.
+
+
+![[Pasted image 20250501031753.png]]
+
+![[Pasted image 20250217114433.png]]
+
+## **ENUM
+
+![[telegram-cloud-photo-size-2-5221956955604644168-y.jpg]]
+
diff --git a/PYTHON/ENUMERATE, ZIP.md b/PYTHON/ENUMERATE, ZIP.md
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/PYTHON/ENUMERATE, ZIP.md
@@ -0,0 +1,2 @@
+
+
diff --git a/PYTHON/FOR, WHILE.md b/PYTHON/FOR, WHILE.md
new file mode 100644
index 0000000..5bf423d
--- /dev/null
+++ b/PYTHON/FOR, WHILE.md
@@ -0,0 +1,227 @@
+```python
+# Фильтрация элементов по типу:
+def filter_list(list_to_filter, value_type):
+ filtered_list = []
+ for element in list_to_filter:
+ if type(element) == value_type:
+ filtered_list.append(element)
+
+ # Not recomended! Because bool is subclass of int !
+ # if isinstance(element, value_type):
+ # filtered_list.append(element)
+
+ return filtered_list
+
+
+print(filter_list([35, True, 'abc', 10], int))
+print(filter_list([35, True, 'abc', 10], str))
+print(filter_list([35, True, 'abc', 10], bool))
+
+###########################################################
+# Сокращенный FOR:
+all_nums = [-3, 1, 0, 10, -20, 5]
+
+absolute_nums = [abs(num) for num in all_nums]
+
+print(absolute_nums)
+print(all_nums)
+
+#-----------------
+all_nums = [-3, 1, 0, 10, -20, 5]
+
+positive_nums = [num for num in all_nums if num > 0]
+
+# positive_nums = []
+
+# for num in all_nums:
+# if num > 0:
+# positive_nums.append(num)
+
+print(positive_nums)
+print(all_nums)
+
+#-----------------
+my_set = {1, 10, 15}
+
+new_set = {val * val for val in my_set}
+
+print(new_set)
+print(my_set)
+
+#-----------------
+my_scores = {
+ 'a': 10,
+ 'b': 7,
+ 'm': 14
+}
+
+scores = {k: v * 10 for k, v in my_scores.items()}
+
+# scores = {}
+
+# for key, value in my_scores.items():
+# scores[key] = value * 10
+
+print(scores)
+print(my_scores)
+
+#-----------------
+my_scores = [10, 7, 14]
+
+scores = {index: elem * 2 for index, elem in enumerate(my_scores)}
+
+print(scores)
+
+#-----------------
+# my_motorbike = {
+ # 'brand': 'bmw',
+ # 'country': 'germany',
+ # 'owner': 'vlapa'
+# }
+
+# bike = {k: v.upper() for k, v in my_motorbike.items()}
+
+
+my_motorbike = ['BMW', 'Germany', 'Vlapa']
+
+bike = [el for el in my_motorbike if len(el) > 3]
+
+
+print(bike)
+
+print(my_motorbike)
+
+###########################################################
+
+while True:
+ try:
+ num_one = float(input("Please enter number one: "))
+ num_two = float(input("Please enter number two: "))
+ except ValueError as e:
+ print(e)
+ print("You must enter numbers!")
+ continue
+
+ print(num_one / num_two)
+
+ answer = input("Do you want to continue? (yes/no): ")
+ if answer == 'no':
+ break
+```
+
+1️⃣ **Цикл** for
+
+Используется для перебора элементов в итерируемых объектах (списки, строки, множества, кортежи, словари, генераторы и т. д.).
+
+**Пример 1: Перебор списка**
+
+fruits = ["яблоко", "банан", "вишня"]
+for fruit in fruits:
+    print(fruit)
+
+**Пример 2:** for **с** range()
+
+for i in range(5):  # От 0 до 4
+    print(i)
+
+**Пример 3: Перебор словаря**
+
+person = {"имя": "Алиса", "возраст": 25}
+for key, value in person.items():
+    print(f"{key}: {value}")
+
+2️⃣ **Цикл** while
+
+Выполняется **до тех пор, пока условие истинно.**
+
+**Пример:**
+
+x = 5
+while x > 0:
+    print(f"x = {x}")
+    x -= 1  # Уменьшаем x
+
+🗣️ Используйте for когда заранее известно количество итераций, и while когда не известно.
+
+🖥 **Циклы. Управление потоком**
+
+1️⃣ break – прерывает выполнение цикла.
+2️⃣ continue – пропускает текущую итерацию и переходит к следующей.
+3️⃣ pass – заглушка, ничего не делает (используется, когда код еще не написан).
+
+**Примеры:**
+# break – выход из цикла при достижении 5
+
+for i in range(10):
+    if i == 5:
+        break
+    print(i)  # Выведет 0,1,2,3,4
+
+# continue – пропускает 5
+
+for i in range(7):
+    if i == 5:
+        continue
+    print(i)  # Выведет 0,1,2,3,4,6
+
+
+# pass – заглушка
+
+for i in range(3):
+    pass  # Код пока не написан
+
+
+🖥 **Использование** else **в циклах**
+
+else в цикле выполняется, если цикл завершился естественным образом (без break и return).
+
+**Пример 1:** for **...** else
+
+for i in range(3):
+    print(i)
+else:
+    print("Цикл завершился без break")
+0
+1
+2
+# Цикл завершился без break
+
+**Пример 2:** while **...** else
+
+x = 3
+while x > 0:
+    print(x)
+    x -= 1
+else:
+    print("Цикл while завершен")
+3
+2
+1
+
+# Цикл while завершен
+
+**Пример 3: проверка вхождения в список**
+
+def contains(num, numbers):
+    for n in numbers:
+        if n == num:
+            return True
+    else:
+        return False
+print(contains(3, [1,2,3,4,5])) # True
+print(contains(3, [1,2,4,5])) # False
+
+**Пример 4: определение простого числа**
+
+def is_prime_number(num):
+    for i in range(2, int(num ** 0.5) + 1):
+        if num % i == 0:
+            print(f"{num} не простое число")
+            break
+    else:
+        print(f"{num} — простое число")
+
+is_prime_number(17) # 17 — простое число
+is_prime_number(16) # 16 не простое число
+
+🗣️ Если нет break и return, else всегда сработает, и его можно просто вынести за пределы цикла. \ No newline at end of file
diff --git a/PYTHON/HTTP.md b/PYTHON/HTTP.md
new file mode 100644
index 0000000..9e83b93
--- /dev/null
+++ b/PYTHON/HTTP.md
@@ -0,0 +1,18 @@
+```shell
+# Быстрый запуск простого HTTP сервера для передачи ФАЙЛОВ !
+# по умолчанию порт = 8000
+
+# его нужно открыть в firewall:
+sudo ufw allow 8000
+# sudo firewall-cmd --add-port=8000/tcp
+
+# переходим в нужный каталог:
+cd </my/catalog>
+
+# запускаем:
+python3 -m http.server 8000
+# python3 -m http.server --bind 127.0.0.1 8000
+
+# открываем в браузере http://89.110.92.137:8000
+# и качаем нужные ФАЙЛЫ !
+```
diff --git a/PYTHON/LIST COMPREHENSION.md b/PYTHON/LIST COMPREHENSION.md
new file mode 100644
index 0000000..0f9f0bd
--- /dev/null
+++ b/PYTHON/LIST COMPREHENSION.md
@@ -0,0 +1,6 @@
+![[telegram-cloud-photo-size-2-5220111584251211568-y.jpg]]
+![[telegram-cloud-photo-size-2-5220111584251211577-y.jpg]]
+![[telegram-cloud-photo-size-2-5220111584251211571-y.jpg]]
+![[telegram-cloud-photo-size-2-5220111584251211570-y.jpg]]
+![[telegram-cloud-photo-size-2-5220111584251211574-y.jpg]]
+![[telegram-cloud-photo-size-2-5220111584251211572-y.jpg]]![[telegram-cloud-photo-size-2-5220111584251211573-y.jpg]]
diff --git a/PYTHON/MAP.md b/PYTHON/MAP.md
new file mode 100644
index 0000000..f1b79ff
--- /dev/null
+++ b/PYTHON/MAP.md
@@ -0,0 +1,15 @@
+```python
+# Функция map() применяется к каждому элементу последовательности и возвращает итератор.
+
+numbers = [1, 2, 3, 4]
+squared = map(lambda x: x**2, numbers)
+print(list(squared))  # [1, 4, 9, 16]
+
+# Можно передавать несколько списков:
+
+a = [1, 2, 3]
+b = [4, 5, 6]
+print(list(map(lambda x, y: x + y, a, b)))  # [5, 7, 9]
+
+# Полезно для применения функций к списку без for
+```
diff --git a/PYTHON/MATH.md b/PYTHON/MATH.md
new file mode 100644
index 0000000..6912e2c
--- /dev/null
+++ b/PYTHON/MATH.md
@@ -0,0 +1 @@
+![[telegram-cloud-photo-size-2-5230966980947866123-y.jpg]]
diff --git a/PYTHON/Matprotlib.md b/PYTHON/Matprotlib.md
new file mode 100644
index 0000000..ed42f2d
--- /dev/null
+++ b/PYTHON/Matprotlib.md
@@ -0,0 +1,29 @@
+📊 **Как нарисовать график в 3 строки кода**
+
+С matplotlib можно строить графики всего за пару секунд!
+
+**✔️ Установка:**
+
+```sh
+pip install matplotlib
+```
+
+**➡️ Простой график:**
+```python
+import matplotlib.pyplot as plt
+
+plt.plot([1, 2, 3, 4], [10, 20, 25, 30])
+plt.show()
+```
+
+⚙️ matplotlib поддерживает линии, гистограммы, диаграммы и многое другое.
+🗣 **Если хочешь визуализировать данные —** matplotlib **твой инструмент**!
+
+
+![[Pasted image 20250621003213.png]]
+![[Pasted image 20250621003225.png]]
+Базовый пример построения графика с помощью Python
+
+Для начала требуется установить библиотеку matplotlib — pip install matplotlib. Сама программа для создания примера простого графика представлена на картинке.
+
+В функцию plot передаются два массива — координаты точек кривой. Первый массив — координаты по x, второй массив — y. Функция show выдает картинку с графиком. \ No newline at end of file
diff --git a/PYTHON/NAMEDTUPLE.md b/PYTHON/NAMEDTUPLE.md
new file mode 100644
index 0000000..0c5614f
--- /dev/null
+++ b/PYTHON/NAMEDTUPLE.md
@@ -0,0 +1,18 @@
+```python
+# Обычно для хранения данных используют словари или классы. namedtuple — это легковесная альтернатива, сочетающая удобство атрибутов класса с простотой кортежей.
+
+# Создание namedtuple:
+
+from collections import namedtuple
+
+Point = namedtuple("Point", ["x", "y"])
+p = Point(3, 4)
+
+print(p.x, p.y)  # 3 4
+
+# Работает как обычный кортеж, но с доступом по именам:
+
+print(p[0], p[1])  # 3 4
+
+# Полезно, когда нужен неизменяемый контейнер с понятными атрибутами!
+```
diff --git a/PYTHON/OPEN - работа с файлами.md b/PYTHON/OPEN - работа с файлами.md
new file mode 100644
index 0000000..e8d3d51
--- /dev/null
+++ b/PYTHON/OPEN - работа с файлами.md
@@ -0,0 +1,39 @@
+_Ключи команды:_
+r - открывает файл на чтение (по умолчанию)
+w - запись, если нет - будет создан, если есть - будет перезаписан.
+x - запись, если есть - ошибка
+a - добавляет данные, если файла нет - создает
+t - открывает файл как текстовый (по умолчанию)
+\+ - и чтение и запись
+```python
+file = open(file='hello.txt', mode='r')
+print(file.name) # имя файла с которым сейчас работаем
+print(file.mode) # режим в котором работаем с файлом
+file.close() # устаревшее
+
+with open(file='hello.txt) as file:
+ print(file.read(8))
+ print(file.readline())
+ print(file.readlines())
+
+ for line in file.readlines():
+ print(linestrip())
+
+ for line in file:
+ print(line.strip())
+
+password_list = ['qwerty, '12345', 'xxxxx']
+
+with open(file='secret_file.txt', mode='w) as file:
+ for passwd in password_list:
+ file.write(f'{passwd}\n)
+
+ file.writelines([line+'\n' for line in password_list])
+
+# Сохранение изображений и др.:
+import requessts
+
+response = requests.get('https://<ссылка>')
+ file.write(response.content)
+```
+
diff --git a/PYTHON/OS.md b/PYTHON/OS.md
new file mode 100644
index 0000000..1a95ef9
--- /dev/null
+++ b/PYTHON/OS.md
@@ -0,0 +1,18 @@
+**Узнать текущую директорию
+
+`os.getcwd()
+`C:/Users/...`
+
+
+**Удаляем файл из папки на компьютере**
+
+Здесь сначала мы импортируем модуль `os`. Затем указываем полный путь к удаляемому файлу в переменной `file_path`. С помощью функции `os.path.exists()` проверяем, существует ли файл по указанному пути. 
+
+Если файл существует, мы удаляем его с помощью функции `os.remove()`. В противном случае выводим информацию, что такой файл не найден.
+
+![[telegram-cloud-photo-size-2-5235693785730632253-y.jpg]]
+
+
+**Выключение компьютера**
+![[telegram-cloud-photo-size-2-5425027665104058215-x.jpg]]
+
diff --git a/PYTHON/PATH.md b/PYTHON/PATH.md
new file mode 100644
index 0000000..6eaa7c7
--- /dev/null
+++ b/PYTHON/PATH.md
@@ -0,0 +1,111 @@
+```python
+import pathlib
+
+p = pathlib.Path(__file__)
+print(p)
+# получим путь к файлу, в котором сейчас находимся:
+
+/home/name/package/example.py
+
+```
+
+```
+from pathlib as Path
+```
+- `Path.cwd()`: Возвращает путь объекта текущей рабочей директории;
+- `Path.home()`: Возвращает путь объекта домашней директории;
+- `Path.stat()`: Возвращает информацию о пути;
+- `Path.chmod()`: Меняет режим и уровень доступа файла;
+- `Path.glob(pattern)`: Получение всех файлов которые соответствую паттерну, например `*.jpg` (все картинки) или `*.mp3` (все песни);
+- `Path.mkdir()`: создает новую папку по данному пути;
+- `Path.open()`: Открывает файл, созданный в пути;
+- `Path.rename()`: Переименовывает файл или директорию указанной цели;
+- `Path.rmdir()`: Удаляет пустую директорию;
+- `Path.unlink()`: Удаляет файл или символическую ссылку.
+
+
+## Генерация кроссплатформенных путей в Pathlib
+
+Пути используют разные соглашения в разных операционных системах. Windows использует обратный слеш `\` между названиями папок, тогда как все другие популярные операционные системы используют прямой слеш `/`.
+
+Если вы хотите, чтобы ваш код работал, независимо от базовой ОС, вам нужно будет обрабатывать различные соглашения, характерные для базовой платформы. Модуль Pathlib упрощает работу с путями к файлам. В Pathlib можно просто передать путь или название файла объекту `Path()`, используя слеш, независимо от ОС. Pathlib занимается всем остальным.
+
+Python
+
+| | |
+|---|---|
+|1|pathlib.Path.home() / 'python' / 'samples' / 'test_me.py'|
+
+Объект `Path()` конвертирует `/` в слеш соответствующий операционной системе. `pathlib.Path` может представлять путь Windows или Posix. Кроме того, Pathlib решает многие кросс-функциональные баги, легко обрабатывая пути.
+
+## Получение информации о пути в Pathlib
+
+Во время работы с путями зачастую требуется найти родительскую директорию файла/папки или получить символические ссылки. У класса Path есть несколько удобных для этого методов, различные части пути доступны как свойства, что включают следующее:
+
+- `drive`: [строка](https://python-scripts.com/string "Создание строки в питоне"), что представляет название жесткого диска. К примеру, `PureWindowsPath('c:/Program Files/CSV').drive` вернет `"C:"`;
+- `parts`: возвращает [кортеж](https://python-scripts.com/lists-tuples-dictionaries#tuple "Создать кортеж в питоне"), что дает доступ к компонентам пути;
+- `name`: компонент пути без директории;
+- `parent`: последовательность обеспечивает доступ к логическим предкам пути;
+- `stem`: финальный компонент пути без суффикса;
+- `suffix`: разрешение файла финального компонента;
+- `anchor`: часть пути перед директорией. `/` используется для создания дочерних путей и имитации поведения `os.path.join`;
+- `joinpath`: совмещает путь с предоставленными аргументами;
+- `match(pattern)`: возвращает `True/False`, основываясь на совпадении пути с предоставленным шаблоном поиска.
+
+Например, у нас есть данный путь `"/home/projects/pyscripts/python/sample.md"`:
+
+- `path`: — возвращает _PosixPath(‘/home/projects/pyscripts/python/sample.md’);_
+- `path.parts`: — возвращает _(‘/’, ‘home’, ‘projects’, ‘pyscripts’, ‘python’);_
+- `path.name`: — возвращает _‘sample.md’;_
+- `path.stem`: — возвращает _‘sample’;_
+- `path.suffix`: — возвращает _‘.md’;_
+- `path.parent`: — возвращает _PosixPath(‘/home/projects/pyscripts/python’);_
+- `path.parent.parent`: — возвращает _PosixPath(‘/home/projects/pyscripts’);_
+- `path.match('*.md')`: возвращает _True;_
+- `PurePosixPath('/python').joinpath('edited_version')`: возвращает _(‘home/projects/pyscripts/python/edited_version._
+
+
+## Альтернатива для модуля glob
+
+Помимо модулей `os`, `os.path` в Python также доступен модуль `glob`, что предоставляет путь связанных утилит. Функция `glob.glob` модуля `glob` используется для **нахождения файлов**, соответствующих шаблону.
+
+Python
+
+| | |
+|---|---|
+|1<br><br>2<br><br>3<br><br>4|from glob import glob<br><br>top_xlsx_files = glob('*.xlsx') # Все файлы с расширением .xlsx<br><br>all_xlsx_files = glob('**/*.xlsx', recursive=True)|
+
+Pathlib предоставляет свою реализацию `glob`:
+
+Python
+
+| | |
+| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
+| 1<br><br>2<br><br>3<br><br>4 | from pathlib import Path<br><br>top_xlsx_files = Path.cwd().glob('*.xlsx')<br><br>all_xlsx_files = Path.cwd().rglob('*.xlsx') |
+
+Функциональность **glob** доступна с объектами `Path`. Следовательно, модуль Pathlib упрощают сложные задачи.
+
+## Чтение и запись файлов с использованием Pathlib
+
+Следующие методы используются для выполнения основных операций, таких как **чтение и запись файлов**:
+
+- `read_text`: Файл открывается в **текстовом режиме** для чтения содержимого файла и его закрытия после чтения;
+- `read_bytes`: Используется для открытия файла в **бинарном режиме**, возвращения содержимого в бинарном форме и последующего закрытия файла;
+- `write_text`: Используется для открытия файла, записи туда текста и последующего закрытия;
+- `write_bytes`: Используется для записи бинарных данных в файл и закрытия файла по завершении процесса.
+
+Давайте испытаем модуль Pathlib используя популярные файловые операции. Следующий пример используется для **чтения содержимого файла**:
+
+Python
+
+| | |
+|---|---|
+|1<br><br>2|path = pathlib.Path.cwd() / 'Pathlib.md'<br><br>path.read_text()|
+
+Метод `read_text()` для объекта `Path` используется для чтения содержимого файла. В примере ниже данные записываются в файл, в текстовом режиме:
+
+Python
+
+| | |
+| ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
+| 1<br><br>2<br><br>3<br><br>4 | from pathlib import Path<br><br>p = Path('sample_text_file')<br><br>p.write_text('Образец данных для записи в файл') | \ No newline at end of file
diff --git a/PYTHON/PAUSE в PYTHON.md b/PYTHON/PAUSE в PYTHON.md
new file mode 100644
index 0000000..9a611f0
--- /dev/null
+++ b/PYTHON/PAUSE в PYTHON.md
@@ -0,0 +1,13 @@
+Иногда нужно приостановить выполнение программы:
+
+➡️ Использование time.sleep():
+
+```python
+import time
+
+print("Начало")
+time.sleep(2) # Пауза в 2 секунды
+print("Конец")
+```
+
+
diff --git a/PYTHON/PEP8.md b/PYTHON/PEP8.md
new file mode 100644
index 0000000..7ce650b
--- /dev/null
+++ b/PYTHON/PEP8.md
@@ -0,0 +1,37 @@
+***Отступы*** - 4 пробела
+Макс длина строки = 79 символов (можно разбить " \\ " или операторы конкатенации)
+
+***Пробелы*** - операторы и операнды, скобки НЕ отделять!
+
+***Именование*** - верблюд и змея
+
+***Комментарии*** - # и \`\`\` ... \`\`\`
+
+***Импорты*** - в алфавитном порядке, лучше без " * "
+группы разделяйте пустой строкой
+
+***Пробелы*** вокруг операторов
+
+***Название функций и методов*** - глаголы с резделением " _ "
+
+***Название переменных*** - подробно !
+
+***Расположение функций и классов*** - разделяйте пустыми строками, функции должны быть определены перед их использованием.
+
+***Тройные кавычки*** - документация кода
+
+---------------------------
+_Полезные инструменты:_
+
+***Линтеры***:
+
+- *pylint
+- *flake8
+- *PyCharm
+
+
+***Автоматические форматировщики:***
+
+- *Black
+- *autopep8
+- *YAPF
diff --git a/PYTHON/PROGRAMS.md b/PYTHON/PROGRAMS.md
new file mode 100644
index 0000000..b4d3d53
--- /dev/null
+++ b/PYTHON/PROGRAMS.md
@@ -0,0 +1,160 @@
+```sh
+mkdir weather
+cd weather
+
+true > weather
+chmod +x weather
+ls
+
+nvim weather
+#---------------
+#!/usr/bin/env python3.11
+print("Hello World !")
+#---------------
+./weather
+-> Hello World !
+
+# создать ссылку чтобы запускалось везде:
+sudo ln -s $(pwd)/weather /usr/local/bin/
+# удалить:
+sudo rm /usr/local/bin/weather
+
+#################
+true > gps_coordinates.py
+true > weather_api_service.py
+true > printer.py
+
+ls -l
+
+gps_coordinates.py
+printer.py
+weather
+weather_api_service.py
+
+mv printer.py weather_formatter.py
+
+nvim weather
+```
+
+```python
+#!/usr/bin/env python3.11
+
+from gps_coordinates import get_gps_coordinates
+from weather_api_service import get_weather
+from weather_formatter import format_weather
+
+def main():
+ coordinates = get_gps_coordinates()
+ weather = get_weather(coordinates)
+ print(format_weather(weather))
+
+if __name__ == "__main__":
+ main()
+```
+`nvim gps_coordinates.py`
+```python
+def get_gps_coordinates():
+ """ Returns current coordinates using MackBook GPS """
+ pass
+
+##########################
+# Структура программы со слоями:
+
+from abc import get_abc
+
+def main():
+ abc = get_abc()
+ print("Что то там")
+
+if __name__ == "__main__":
+ main()
+
+##########################
+# Просто кортеж:
+
+def abc() -> tuple[float, int]:
+ """Return"""
+ return (0.0, 890)
+
+# Просто список:
+
+def abc() -> dict[str, int]:
+ """Return"""
+ return {'latitude': 10.0, 'longitide': 890}
+
+####################################################
+# Именованный кортеж:
+
+from typing import NamedTuple
+
+class Coordinates(NamedTuple):
+ latitude: float
+ longitude: float
+
+def get_coordinates() -> tuple[float, float]:
+ """Return"""
+ return Coordinates(latitude=10, longitude=20)
+
+coordinates = get_coordinates()
+ print(coordinates.latitude)
+ print(coordinates.longitude)
+
+lat, long = get_coordinates()
+print(lat, long)
+
+##########################
+# Именованный словарь:
+
+from typing import TupedDict
+
+class Coordinates(TupedDict):
+ latitude: float
+ longitude: float
+
+def get_coordinates() -> Coordinates:
+ """Return"""
+ return Coordinates(**{latitude=10, longitude=20})
+
+##########################
+# Список:
+
+from typing import Literal
+
+def get_coordinates() \
+ -> dict[Literal['latitude'] | Literal['longitude], float]:
+ """Return"""
+ return {'latitude': 10.0, 'longitude': 20.0}
+
+
+##########################
+# DataClass лучше именованного кортежа и можно менять в программе:
+from dataclasses import dataclass
+
+@dataclass
+class Coordinates:
+ latitude: float
+ longitude: float
+
+def get_coordinates() -> Coordinates:
+ """Return"""
+ return Coordinates(**{latitude=10, longitude=20})
+
+##########################
+```
+
+```python
+# Выход/завершение программы:
+import sys
+
+sys.exit([status])
+# Необязательный аргумент `status` представляет собой статус выхода. Это целочисленное значение, которое указывает на причину завершения программы. Принято считать, что статус 0 означает успешное выполнение, а любой ненулевой статус указывает на ошибку или ненормальное завершение.
+
+# Если аргумент `status` не указан, используется значение по умолчанию 0.
+
+#################
+quit()
+
+#################
+exit()
+
+``` \ No newline at end of file
diff --git a/PYTHON/PROGRAMS_py.md b/PYTHON/PROGRAMS_py.md
new file mode 100644
index 0000000..01de73d
--- /dev/null
+++ b/PYTHON/PROGRAMS_py.md
@@ -0,0 +1,150 @@
+===============================================
+### TinyDB ###
+это легковесная, чисто Python база данных без сервера, которая сохраняет данные в виде JSON-файлов.
+Подходит для небольших проектов, скриптов и приложений, где не требуется полноценная SQL-база данных.
+Подходит для разработчиков, которым нужно быстро внедрить базу данных без сложной настройки и установки.
+```python
+from tinydb import TinyDB, Query
+
+# Создание базы данных в файле db.json
+db = TinyDB('db.json')
+
+# Добавление данных
+db.insert({'name': 'Alice', 'age': 24})
+db.insert({'name': 'Bob', 'age': 30})
+
+# Поиск данных
+User = Query()
+results = db.search(User.age > 25)
+print(results)
+
+# Обновление данных
+db.update({'age': 26}, User.name == 'Alice')
+
+# Удаление данных
+db.remove(User.name == 'Bob')
+
+# Установка
+pip install tinydb
+```
+===============================================
+### Библиотека Python: Arrow ###
+
+**Arrow** — библиотека для работы с датами и временем, которая упрощает создание, обработку и форматирование временных меток.
+
+Подходит для разработчиков, которые хотят избежать сложности работы с модулями `datetime` и `timezones`, предоставляя более интуитивные и мощные инструменты для управления временем.
+```python
+import arrow
+
+# Текущее время
+now = arrow.now()
+print(now)
+
+# Создание объекта даты и времени
+date = arrow.get('2023-08-13T12:30:00')
+print(date)
+
+# Форматирование даты и времени
+formatted_date = date.format('DD MMMM YYYY HH:mm')
+print(formatted_date)
+
+# Работа с часовыми поясами
+utc_time = arrow.utcnow()
+local_time = utc_time.to('US/Pacific')
+print(local_time)
+
+# Установка
+pip install arrow
+```
+===============================================
+### REMBG ###
+```python
+# удаление фона изображения:
+pip install rembg
+
+from rembg inport remove
+from PIL import Image
+
+input_path = 'name.jpg'
+output_path = 'out.png'
+
+input = Image.open(input_path)
+output = remove(input)
+output,save(output_path)
+
+# фото в ч/б:
+from PIL import Image
+
+img = Image.open('girl.png')
+baw = img.convert('L') # L - оттенки серого
+baw.save('baw_girl.png')
+```
+===============================================
+### PANDAS ###
+Обработка CSV-файлов с помощью pandas позволяет эффективно управлять, анализировать и преобразовывать данные из таблиц, что полезно для работы с большими объемами данных.
+```python
+pip install pandas
+
+import pandas as pd
+
+# Чтение CSV-файла
+df = pd.read_csv('data.csv')
+
+# Вывод первых 5 строк таблицы
+print(df.head())
+
+# Фильтрация данных: выбор строк, где значение в столбце 'age' больше 30
+filtered_df = df[df['age'] > 30]
+
+# Запись отфильтрованных данных в новый CSV-файл
+filtered_df.to_csv('filtered_data.csv', index=False)
+```
+Этот скрипт читает CSV-файл, отображает первые 5 строк таблицы, фильтрует строки по значению столбца и сохраняет отфильтрованные данные в новый файл. pandas упрощает обработку табличных данных и позволяет работать с большими массивами данных в удобной форме.
+
+===============================================
+```python
+# Объединение нескольких CSV-файлов:
+import pandas as pd
+
+# Чтение нескольких CSV-файлов и их объединение в один DataFrame
+files = ['data1.csv', 'data2.csv', 'data3.csv']
+df_list = [pd.read_csv(file) for file in files]
+combined_df = pd.concat(df_list)
+
+# Сохранение объединенных данных в новый CSV-файл
+combined_df.to_csv('combined_data.csv', index=False)
+```
+В этом примере несколько CSV-файлов объединяются в одну таблицу, что упрощает работу с данными из разных источников. pandas позволяет легко манипулировать такими данными и проводить различные операции, такие как фильтрация, группировка и сортировка.
+
+===============================================
+### % заряда батареи ###
+
+```python
+# заряд батареи:
+import psutil
+
+battery psutil.sensors_battery()
+persent = int(battery.persent)
+print(f'Заряд батареи: {persent}%)
+```
+===============================================
+### Скриншот ###
+```python
+import pyautogui
+
+pyautogui.screenshot(
+ 'path_to_file.jpg',
+ region=(0, 0, 300, 400)
+)
+# функционал - большой !
+```
+===============================================
+### MAX элемент СПИСКА
+который входит самое большое кол-во раз:
+```python
+my_list = [5, 9, 0, 5, 3, 6, 3, 5, 0, 4, 3]
+
+mpv = max(set(my_list), key=my_list.count)
+print(mpv)
+```
+=============================================== \ No newline at end of file
diff --git a/PYTHON/PROGRESS BAR.md b/PYTHON/PROGRESS BAR.md
new file mode 100644
index 0000000..d9d054d
--- /dev/null
+++ b/PYTHON/PROGRESS BAR.md
@@ -0,0 +1,138 @@
+#
+
+![](https://habrastorage.org/getpro/habr/post_images/466/0af/83f/4660af83f239a7d8e1f14a02fb8ed13d.gif)
+Зачем нужны индикаторы прогресса?
+
+Индикаторы прогресса (progress bar) — визуальное отображение процесса работы. Они избавляют нас от необходимости беспокоиться о том, не завис ли скрипт, дают интуитивное представление о скорости его выполнения и подсказывают, сколько времени осталось до завершения.
+
+Человек ранее не использовавший индикаторы прогресса может предположить, что их внедрение может сильно усложнить код. К счастью, это не так. Небольшие примеры ниже покажут, как быстро и просто начать отслеживать прогресс в консоли или в интерфейсе быстро набирающей популярность графической библиотеки [PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI).
+
+# Используем Progress
+
+Первым у нас идёт модуль [Progress](https://github.com/verigak/progress/)_._
+
+Всё, что от вас потребуется, это указать количество ожидаемых итераций, тип индикатора и вызывать функцию при каждой итерации:
+
+```python
+import time
+from progress.bar import IncrementalBar
+
+mylist = [1,2,3,4,5,6,7,8]
+
+bar = IncrementalBar('Countdown', max = len(mylist))
+
+for item in mylist:
+ bar.next()
+ time.sleep(1)
+
+bar.finish()
+```
+Результат работы:
+![](https://habrastorage.org/getpro/habr/post_images/f89/560/66b/f8956066bc8771df24f3b195b73e9fb1.gif)
+
+Есть индикаторы на любой вкус:
+
+![](https://habrastorage.org/getpro/habr/post_images/466/0af/83f/4660af83f239a7d8e1f14a02fb8ed13d.gif)
+# Используем tqdm
+
+> Быстрый и расширяемый индикатор прогресса для Python и CLI
+
+Всего один вызов функции понадобится для получения результата аналогичного предыдущему:
+
+```python
+import time
+from tqdm import tqdm
+
+mylist = [1,2,3,4,5,6,7,8]
+
+for i in tqdm(mylist):
+ time.sleep(1)
+```
+Получаем:
+![](https://habrastorage.org/getpro/habr/post_images/77a/31b/de3/77a31bde39a1237549def40677d7d653.gif)
+
+Само собой, в комплекте идёт куча настроек и опций.
+
+# Используем alive-progress
+
+Ещё один [вариант](https://github.com/rsalmei/alive-progress) синтаксиса, побольше дефолтных анимаций, чем в предыдущих примерах:
+
+```python
+from alive_progress import alive_bar
+import time
+
+mylist = [1,2,3,4,5,6,7,8]
+
+with alive_bar(len(mylist)) as bar:
+ for i in mylist:
+ bar()
+ time.sleep(1)
+```
+Результат:
+
+![](https://habrastorage.org/getpro/habr/post_images/c60/529/2a0/c605292a0763b239ae950fc4fff0bfec.gif)
+
+# GUI индикатор прогресса для скрипта
+
+Иногда возникает необходимость предоставить конечному пользователю графический индикатор.
+
+![](https://habrastorage.org/getpro/habr/post_images/f8a/fd7/70b/f8afd770b2c1c74dfac23dd492c686f5.gif)
+
+Сколько кода нужно, чтобы достигнуть такого результата? Немного:
+```python
+import PySimpleGUI as sg
+import time
+
+mylist = [1,2,3,4,5,6,7,8]
+
+for i, item in enumerate(mylist):
+ sg.one_line_progress_meter('This is my progress meter!', i+1, len(mylist), '-key-')
+ time.sleep(1)
+```
+
+# Индикатор в приложении PySimpleGUI
+
+Рассмотрим реализацию индикатора в [PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI).
+
+![](https://habrastorage.org/getpro/habr/post_images/580/d8d/0a3/580d8d0a3e852ec2ef74db199755f2f6.gif)
+
+Вот как это сделать:
+
+```python
+import PySimpleGUI as sg
+import timemylist = [1,2,3,4,5,6,7,8]
+
+progressbar = [
+ [sg.ProgressBar(len(mylist), orientation='h', size=(51, 10), key='progressbar')]]
+outputwin = [
+ [sg.Output(size=(78,20))]
+]
+
+layout = [
+ [sg.Frame('Progress',layout= progressbar)],
+ [sg.Frame('Output', layout = outputwin)],
+ [sg.Submit('Start'),sg.Cancel()]
+]
+
+window = sg.Window('Custom Progress Meter', layout)
+progress_bar = window['progressbar']
+
+while True:
+ event, values = window.read(timeout=10)
+ if event == 'Cancel' or event is None:
+ break
+ elif event == 'Start':
+ for i,item in enumerate(mylist):
+ print(item)
+ time.sleep(1)
+ progress_bar.UpdateBar(i + 1)
+
+window.close()
+```
+# Заключение
+
+Как видите, нет ничего сложного в добавлении информации о прогрессе выполнения: кода немного, а отзывчивость повышается очень сильно. Используйте индикаторы, чтобы больше никогда не гадать, завис ли процесс или нет!
+
+==========================================
+
+![[telegram-cloud-photo-size-2-5337133182321027005-y.jpg]]
diff --git a/PYTHON/PYINSTALLER.md b/PYTHON/PYINSTALLER.md
new file mode 100644
index 0000000..8ba9c87
--- /dev/null
+++ b/PYTHON/PYINSTALLER.md
@@ -0,0 +1,164 @@
+`pip install pyinstaller`
+
+`pyinstaller main.py`
+- cоздастся папка `dist` с исполняемым файлом
+
+`pyinstaller --onefile main.py`
+- одним файлом
+
+`pyinstaller --noconsole --onefile main.py`
+- будет создан исполняемый файл, работающий в фоновом режиме
+
+================================
+
+# Превращаем код на Python в исполняемый EXE-файл
+
+Отредактировано 20 дек 2023
+
+Как скомпилировать Python-код в файл EXE, чтобы защитить свой код или наглядно показать работу вашей программы.
+
+Обычно код на Питон запускают двумя способами: через терминал или через текстовый редактор / IDE.
+
+При этом стоит отметить, что файлик Python — это всего лишь скрипт, который сохранён в расширении .py. Да, это очевидно, но в этом и кроется проблема: иногда нужно скрыть исходный код, а поделиться программкой (в виде альфа-версии, например) очень хочется.
+
+Одно из решений, которое закрывают боль с исходным кодом — это конвертация файла Python в исполняемый exe-файлик.
+
+В этой статье мы научимся создавать «экзешники» при помощи двух библиотек: Pyinstaller and auto-py-to-exe.
+
+Перед тем, как продолжить чтение статьи, скачайте этот репозиторий. Он здорово поможет вам понять процесс, если вы будете повторять все шаги вместе с нами.
+
+## Почему возникает потребность конвертировать Python-код в EXE
+
+1. Код могут украсть. Об этом мы уже говорили выше. Куда проще достать исходный код непосредственно из py-файла, чем из скомпилированного приложение. Py-файл достаточно просто открыть!
+2. Хочется поделиться кодом с другими юзерами. Дело не только в том, что ваш код могут украсть. Зачастую код содержит зависимости, которыми должен владеть другой юзер перед запуском вашего Python-кода. Но что, если он вообще ничего не смыслит в программировании? Не заставишь ведь его сперва выучить программирование, чтобы оценить ваше новое суперприложение. Проще создать исполняемый файл.
+3. Исполняемый файл можно запускать автоматически, по расписанию. Этот пункт актуален, если вы решили написать утилиту для вашего ПК, и эта утилита должна запускаться либо в определенное время, либо вместе с запуском ОС.
+
+## Конвертация кода Python в файл .exe
+
+Для начала создадим скрипт на Python. В качестве примера мы используем открытый код бронирования жилья AitBnb в Нью-Йорке. Если у вас уже есть готовый код, можете сразу использовать его.
+
+```python
+import pandas as pd
+from pandas.api.types import CategoricalDtype
+
+def perform_validation(filename:str):
+ """
+ A function to validate inputs for NYC Airbnb Open data.
+ """
+ path_to_data = "../.."
+ data = pd.read_csv(f"{path_to_data}{filename}")
+
+ # Convert neighbourhood_group to type category
+ neighbourhood_group_to_cat = CategoricalDtype(
+ categories=["Manhattan", "Brooklyn", "Queens", "Bronx", "Staten Island"],
+ ordered=False
+ )
+
+ data["neighbourhood_group"] = data["neighbourhood_group"].astype(neighbourhood_group_to_cat)
+
+ # Convert room_type to type category
+ room_type_to_cat = CategoricalDtype(
+ categories=["Entire home/apt", "Private room", "Shared room"],
+ ordered=False
+ )
+
+ data["room_type"] = data["room_type"].astype(room_type_to_cat)
+
+ # Convert last_review to datetime
+ data["last_review"] = pd.to_datetime(data["last_review"])
+
+ # Minimum nights a person can stay is one night
+ assert data["minimum_nights"].min() >= 1
+
+ # Minimum number of reviews is 0
+ assert data["number_of_reviews"].min() >= 0
+
+ # Minimum number of reviews per month
+ assert data["reviews_per_month"].min() >= 0.00
+
+ # Minimum amount of listings per host
+ assert data["calculated_host_listings_count"].min() >= 1
+
+ # Number of days when listing is available for books
+ # Could be 0 if tennant has long term booking
+ assert data["availability_365"].min() >= 0
+
+ # Save validated data
+ data.to_csv("validated_ab_nyc_2019.csv", index=False)
+
+if __name__ == "__main__":
+ # User inputs filename
+ filename = input("Enter filename: ")
+
+ # Ensure it's a string
+ if not filename.isalpha():
+ filename = str(filename)
+
+ # Automated validation
+ perform_validation(filename)
+```
+
+Вы не забыли скачать библиотеки по ссылке по введении? Сделайте это сейчас.
+
+](https://tproger.ru/articles/secure-app-development)
+
+Вот структура файлов на GitHub:
+
+```
+# Directory structure
+| AB_NYC_2019.csv
+|
++---auto_py_to_exe
+| | requirements.txt
+| | validation.py
++---pyinstaller
+ | requirements.txt
+ | validation.py
+```
+
+Каждая из директорий должна обладать своей виртуальной средой. Убедитесь, что среды развёрнуты и в директории pyinstaller, и в auto-py-to-exe.
+
+Сохраните ваш код и скопируйте его в обе директории.
+
+## Что делать в Pyinstaller
+
+Ещё раз убедитесь, что виртуальная среда развёрнута. Да, мы настаиваем. Проверьте, установлены ли все зависимости между библиотеками и кодом.
+
+Всё в порядке? Запускайте команду:
+
+```
+pyinstaller --onefile validation.py
+```
+
+Процесс должен запуститься, и в конце консоль сообщит: «Completed successfully». Значит, всё прошло успешно. ?
+
+![Превращаем код на Python в исполняемый EXE-файл 1](https://res.cloudinary.com/dyd911kmh/image/upload/v1661780977/executable_file_being_created_980fc79c16.png)
+
+В папке с библиотекой и кодом должны появиться две новые директории под названиями build и dist. В папке dist вы найдёте заветный exe-файл. Запустите его и проверьте работу приложения.
+
+## Что делать в Auto-py-to-exe
+
+Также скиньте файлик с кодом в папку с библиотекой и проверьте зависимости между кодом и библиотекой.
+
+Запустите команду:
+
+```
+auto-py-to-exe
+```
+
+Команда запустит графический интерфейс.
+
+![Превращаем код на Python в исполняемый EXE-файл 2](https://res.cloudinary.com/dyd911kmh/image/upload/v1661780977/graphical_user_interface_75a54c6f4b.png)
+
+Запускаем компилятор Auto-py-to-exe // DataCamp
+
+1. В строчке «Script Location» нажмите «Browse» и выберите py-скрипт.
+2. В строчке «Onefile» выберите, скомпилируется код в единый файл или в папку. В папке будет содержаться exe-файл и все зависимости кода.
+3. В строчке «Console Window» выберите настройки отображения exe-приложения, когда оно будет запущено. Если выбрать «Console Based», запустится и программа, и консоль, в которой будут показаны процессы. Опция «Window Based» скрывает консоль.
+4. Нажмите кнопку «Convert .py to .exe». Готово!
+
+## Запускаем exe-файл
+
+В обоих случаях, после компиляции и запуска exe-файла, тот запустит консоль и попросит вас ввести имя файла. Придумайте его и укажите расширение, в котором всё должно работать. К примеру, `Tproger-is-the-best-it-media-in-the-world.csv`. Тогда консоль закроется, а в папке появится заветный файлик с расширением .csv.
+
+И всё — вы великолепны! \ No newline at end of file
diff --git a/PYTHON/PYTHON_tuturial.md b/PYTHON/PYTHON_tuturial.md
new file mode 100644
index 0000000..f223a47
--- /dev/null
+++ b/PYTHON/PYTHON_tuturial.md
@@ -0,0 +1,2910 @@
+
+https://habr.com/ru/articles/728568/
+# Ядро планеты Python. Интерактивный учебник
+
+Туториал
+
+Добрый день! Меня зовут Михаил Емельянов, недавно я опубликовал на «Хабре» небольшую [статью](https://habr.com/ru/articles/697760/) с примерным путеводителем начинающего Python-разработчика. Пользуясь этим материалом как своего рода оглавлением книги, я написал первые четыре главы мини-учебника «Ядро планеты Python», где постарался коротко, но достаточно ёмко раскрыть специфику, удобство, красоту и силу этого прекрасного языка.
+
+
+
+Оригинал учебника лежит на [GitHub](https://github.com/amaargiru/pycore), вы вольны сколько угодно дополнять и переделывать его. Самое главное — учебник написан на Jupiter Notebook, а это значит, что вы можете интерактивно редактировать код, мгновенно добавляя новые сущности или проясняя непонятные моменты.
+
+
+
+![Core of the planet Python](https://habrastorage.org/r/w1560/getpro/habr/post_images/58f/2c5/a2c/58f2c5a2c6142c8bee312e39ec2f4f00.jpg)
+
+
+
+Погружаясь в Python, не забывайте про прекрасную официальную документацию [docs.python.org](https://docs.python.org/). Изучив её, хотя бы по диагонали, и постепенно углубляясь в нужные разделы, вы сможете убедиться, что многие «хаки», «открытия» и прочие неочевидные вещи уже давно разжеваны, описаны и имеют подробные примеры применения.
+
+
+
+Также я бы рекомендовал для изучения базового синтаксиса Python на полную катушку использовать [leetcode.com](https://leetcode.com/problemset/all/?difficulty=EASY&page=1&status=NOT_STARTED). Если отфильтровать задачи по уровню «Easy», а потом добавить дополнительную сортировку по столбцу «Acceptance», то перед вами предстанет не волчий оскал соревновательной платформы, а ванильный букварь с плавно нарастающим уровнем задачек.
+
+
+
+## 1. Структуры данных
+
+
+
+![Data structures](https://habrastorage.org/r/w1560/getpro/habr/post_images/190/f06/61d/190f0661deb5799fa48e2b28385d98a4.png)
+
+
+
+Как известно, программирование = структуры данных + алгоритмы (у Никлауса Вирта даже книжка такая есть). Начнем с данных, а потом плавненько перейдем к методам их обработки.
+
+
+
+### Список (list)
+
+
+
+Список — самая универсальная и популярная структура данных в Python. Если вы пока точно не определились, какая структура понадобится в вашем проекте, просто возьмите список, с него достаточно просто мигрировать на что-нибудь более специализированное.
+Список представляет собой упорядоченную изменяемую коллекцию объектов произвольных типов. Внутреннее строение списка — массив (точнее, vector) указателей, т. е. список является динамическим массивом.
+
+
+
+```
+a = [] # Создаем пустой список
+a: list[int] = [10, 20]
+b: list[int] = [30, 40]
+a.append(50) # Добавляем значение в конец спискаb.insert(2, 60) # Вставляем значение по определенному индексу
+print(a, b)
+
+a += bprint(f"Add: {a}")
+a.reverse()b = list(reversed(a)) # reversed() возвращает итератор, а не списокprint(f"Reverse: {a}, {b}")
+
+b = sorted(a) # Возвращает новый отсортированный список
+a.sort() # Модифицирует исходный список и не возвращает ничего
+print(f"Sort: {a}, {b}")
+
+
+s: str = "A whole string"list_of_chars: list = list(s)print(list_of_chars)list_of_words: list = s.split()print(list_of_words)i: int = list_of_chars.index("w") # Возвращает индекс первого вхождения искомого элемента или вызывает исключение ValueErrorprint(i)list_of_chars.remove("w") # Удаляет первое вхождение искомого элемента или вызывает исключение ValueErrore = list_of_chars.pop(9) # Удаляет и возвращает значение, расположенное по индексу. pop() (без аргумента) удалит и вернет последний элемент спискаprint(list_of_chars, e)a.clear() # Очистка списка
+```
+
+```
+[10, 20, 50] [30, 40, 60]
+Add: [10, 20, 50, 30, 40, 60]
+Reverse: [60, 40, 30, 50, 20, 10], [10, 20, 50, 30, 40, 60]
+Sort: [10, 20, 30, 40, 50, 60], [10, 20, 30, 40, 50, 60]
+['A', ' ', 'w', 'h', 'o', 'l', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g']
+['A', 'whole', 'string']
+2
+['A', ' ', 'h', 'o', 'l', 'e', ' ', 's', 't', 'i', 'n', 'g'] r
+```
+
+### Кортеж (tuple)
+
+Кортеж — тоже список, только неизменяемый (immutable) и хэшируемый (hashable). Кортеж, содержащий те же данные, что и список, занимает меньше места:
+
+```
+a = [2, 3, "Boson", "Higgs", 1.56e-22]b = (2, 3, "Boson", "Higgs", 1.56e-22)print(f"List: {a.__sizeof__()} bytes")print(f"Tuple: {b.__sizeof__()} bytes")
+```
+
+```
+List: 104 bytes
+Tuple: 64 bytes
+```
+
+### Именованный кортеж (named tuple)
+
+В соответствии с названием, имеет именованные поля. Удобно!
+
+```
+from collections import namedtuplerectangle = namedtuple('rectangle', 'length width')r = rectangle(length = 1, width = 2)print(r)print(r.length)print(r.width)print(r._fields)
+```
+
+```
+rectangle(length=1, width=2)
+1
+2
+('length', 'width')
+```
+
+### Словарь (dict)
+
+Словарь — вторая по частоте использования структура данных в Python. dict — реализация хеш-таблицы, поэтому в качестве ключа нельзя брать нехешируемый объект, например, список (тут-то нам и может пригодиться кортеж). Ключом словаря может быть любой неизменяемый объект: число, строка, datetime и даже функция. Такие объекты имеют метод **__hash__()**, который однозначно сопоставляет объект с некоторым числом. По этому числу словарь ищет значение для ключа.
+
+Списки, словари и множества (которые мы рассмотрим чуть ниже) изменяемы и не имеют метода хеширования, при попытке подставить их в словарь возникнет ошибка.
+
+```
+d = {} # Создаем пустой словарьd: dict[str, str] = {"Italy": "Pizza", "US": "Hot-Dog", "China": "Dim Sum"} # Непосредственное создание словаряk = ["Italy", "US", "China"]v = ["Pizza", "Hot-Dog", "Dim Sum"]d = dict(zip(k, v)) # Создание словаря из двух коллекций при помощи zipk = d.keys() # Коллекция ключей. Отражает изменения в основном словареv = d.values() # Коллекция значений. Тоже отражает изменения в основном словареk_v = d.items() # Кортежи ключ-значение, которые тоже отражают изменения в основном словареprint(d)print(k)print(v)print(k_v)print(f"Mapping: {k.mapping['Italy']}")d.update({"China": "Dumplings"}) # Добавление значение. При совпадении ключа старое значение будет перезаписаноprint(f"Replace item: {d}")c = d["China"] # Читаем значениеprint(f"Read item: {c}")try: v = d.pop("Spain") # Удаляет значение или вызывает исключение KeyErrorexcept KeyError: print("Dictionary key doesn't exist")# Примеры dict comprehension (более подробно comprehension будет рассмотрено ниже)b = {k: v for k, v in d.items() if "a" in k} # Вернет новый словарь, отфильтрованный по значению ключаprint(b)c = {k: v for k, v in d.items() if len(v) >= 7} # Вернет новый словарь, отфильтрованный по длине значенийprint(c)d.clear() # Очистка словаря
+```
+
+
+
+```
+{'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dim Sum'}
+dict_keys(['Italy', 'US', 'China'])
+dict_values(['Pizza', 'Hot-Dog', 'Dim Sum'])
+dict_items([('Italy', 'Pizza'), ('US', 'Hot-Dog'), ('China', 'Dim Sum')])
+Mapping: Pizza
+Replace item: {'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dumplings'}
+Read item: Dumplings
+Dictionary key doesn't exist
+{'Italy': 'Pizza', 'China': 'Dumplings'}
+{'US': 'Hot-Dog', 'China': 'Dumplings'}
+```
+
+### Решение проблемы вычисления хеша при работе со словарем
+
+Любая хеш-таблица, в том числе и питоновский словарь, должна уметь решать проблему вычисления хеша. Для этого используются техники **open addressing** или **chaining**. Python [использует](https://stackoverflow.com/questions/9010222/why-can-a-python-dict-have-multiple-keys-with-the-same-hash) open addressing.
+
+Новый словарь инициализируется с 8 пустыми слотами.
+
+Интерпретатор сначала пытается добавить новую запись по адресу, зависящему от хеша ключа.
+
+```
+addr = hash(key) & mask,
+```
+
+где
+
+```
+mask = PyDictMINSIZE - 1
+```
+
+Если этот адрес занят, то интерпретатор проверяет (при помощи ==) хеш и ключ. Если оба совпадают, то, значит, запись уже существует. Тогда начинается зондирование свободных слотов, которое идет в псевдослучайном порядке (порядок зависит от значения ключа). Новая запись будет добавлена по первому свободному адресу.
+
+Чтение из словаря происходит аналогично, интерпретатор начинает поиск с позиции addr и идет по тому же псевдослучайному пути, пока не прочитает нужную запись.
+
+### Defaultdict
+
+Если попытаться прочитать из обычного словаря значение ключа, которого там нет, то будет выброшено исключение KeyError (исключения будут рассмотрены ниже). Defaultdict позволяет не писать обработчик исключений, а просто воспринимает чтение несуществующего ключа как команду записать в этот ключ и вернуть значение по умолчанию; например, defaultdict(int) вернет 0.
+
+```
+from collections import defaultdictdd = defaultdict(int)print(dd[10]) # Печать int, будет выведен ноль, значение по умолчаниюdd = {} # "Обычный" пустой словарь# print(dd[10]) # вызовет исключение KeyError
+```
+
+```
+0
+```
+
+
+### Счетчик (counter)
+
+Счетчик подсчитывает передаваемые ему объекты. Иногда очень удобно просто бухнуть в счетчик какой-нибудь список и сразу получить структуру данных с подсчитанными элементами.
+
+```
+from collections import Countershirts_colors = ["red", "white", "blue", "white", "white", "black", "black"]c = Counter(shirts_colors)print(c)c["blue"] += 1print(f"After shopping: {c}")
+```
+
+```
+Counter({'white': 3, 'black': 2, 'red': 1, 'blue': 1})
+After shopping: Counter({'white': 3, 'blue': 2, 'black': 2, 'red': 1})
+```
+
+Объяснение работы Counter() при помощи defaultdict():
+
+```
+from collections import defaultdictshirts_colors = ["red", "white", "blue", "white", "white", "black", "black"]d = defaultdict(int)for shirt in shirts_colors: d[shirt] += 1print(d)
+```
+
+
+
+```
+defaultdict(<class 'int'>, {'red': 1, 'white': 3, 'blue': 1, 'black': 2})
+```
+
+
+
+### Множество (set)
+
+
+
+Третья по распространенности питоновская структура данных. Когда-то, когда Python был молод, множества представляли собой несколько редуцированные словари, но со временем их судьбы (и реализации) стали расходиться. Однако, множество всё-таки является хеш-таблицей с соответствующим быстродействием на разных типах операций.
+
+
+
+```
+big_cities: set["str"] = {"New-York", "Los Angeles", "Ottawa"}american_cities: set["str"] = {"Chicago", "New-York", "Los Angeles"}big_cities |= {"Sydney"} # Добавить значение (или add())american_cities |= {"Salt Lake City", "Seattle"} # Сложить множества (или update())print(big_cities, american_cities)union_cities: set["str"] = big_cities | american_cities # Или union()intersected_cities: set["str"] = big_cities & american_cities # Или intersection()dif_cities: set["str"] = big_cities - american_cities # Или difference()symdif_cities: set["str"] = big_cities ^ american_cities # Или symmetric_difference()issub: bool = big_cities <= union_cities # Или issubset()issuper: bool = american_cities >= dif_cities # Или issuperset()print(union_cities)print(intersected_cities)print(dif_cities)print(symdif_cities)print(issub, issuper)big_cities.add("London")big_cities.remove("Ottawa") # Удаляет значение, если оно имеется или выбрасывает KeyErrorbig_cities.discard("Los Angeles") # Удаляет значение без выбрасывания KeyErrorbig_cities.pop() # Возвращает и удаляет случайное значение (порядок в set неопределен) или выбрасывает KeyErrorbig_cities.clear() # Очищает множество
+```
+
+
+
+```
+{'New-York', 'Los Angeles', 'Sydney', 'Ottawa'} {'New-York', 'Seattle', 'Chicago', 'Los Angeles', 'Salt Lake City'}
+{'Ottawa', 'Salt Lake City', 'Chicago', 'New-York', 'Seattle', 'Sydney', 'Los Angeles'}
+{'New-York', 'Los Angeles'}
+{'Ottawa', 'Sydney'}
+{'Seattle', 'Ottawa', 'Chicago', 'Salt Lake City', 'Sydney'}
+True False
+```
+
+
+
+### Иммутабельное множество (frozen set)
+
+
+
+Frozen set — то же множество, только иммутабельное и хешируемое. Напоминает разницу между списком и кортежем, не правда ли?
+
+
+
+```
+a = frozenset({"New-York", "Los Angeles", "Ottawa"})
+```
+
+
+
+### Массив (array, bytes, bytearray)
+
+
+
+Я перешел на Python с языков, более приближенных к «железу» (C, C#, даже на ассемблере когда-то писал за деньги :) и сначала немного удивлялся, что обычный массив, в котором всё так удобно лежит на своих местах, используется относительно редко. Массив в Python не является структурой данных, выбираемой по умолчанию и используется только в случаях, когда решающую роль начинают играть размер структуры и скорость её обработки. Но, с другой стороны, если вы смотрите в сторону NumPy и Pandas (немного затронуты ниже), то массивы — ваше всё.
+
+
+
+Массив хранит переменные определенного типа, поэтому, в отличие от списка, не требует создания нового объекта для каждой новой переменной и выигрывает у списка в размерах и скорости доступа. Можно сказать, что это тонкая обёртка над Си-массивами.
+
+
+
+Следует различать array («просто» массив), bytes (иммутабельный массив, содержащий только байты, наследие str из Python 2) и bytearray (мутабельный байтовый массив).
+
+
+
+```
+from array import arraya1 = array("l", [1, 2, 3, -4])a2 = array("b", b"1234567890")b = bytes(a2)print(a1)print(a2[0])print(b)print(a1.index(-4)) # Возвращает индекс элементы или выбрасывает ValueError
+```
+
+
+
+```
+array('l', [1, 2, 3, -4])
+49
+b'1234567890'
+3
+```
+
+
+
+```
+# Созданиеb1 = bytes([1, 2, 3, 4]) # Целые числа должны быть в диапазоне от 0 to 255b2 = "The String".encode('utf-8')b3 = (-1024).to_bytes(4, byteorder='big', signed=True) # byteorder = "big"/"little"/"sys.byteorder", signed = False/Trueb4 = bytes.fromhex('FEADCA') # Для большей читаемости hex-значения могут быть разделены пробеламиb5 = bytes(range(10,30,2))print(b1, b2, b3, b4, b5)# Преобразованиеc: list = list(b"\xfc\x00\x00\x00\x00\x01")s: str = b'The String'.decode("utf-8")b: int = int.from_bytes(b"\xfc\x00", byteorder='big', signed=False) # byteorder = "big"/"little"/"sys.byteorder", signed = False/Trues2: str = b"\xfc\x00\x00\x00\x00\x01".hex(" ")print(c, s, b, s2)with open("1.bin", "wb") as file: # Байтовая запись в файл file.write(b1)with open("1.bin", "rb") as file: # Чтение из файла b6 = file.read()print(b6)
+```
+
+
+
+```
+b'\x01\x02\x03\x04' b'The String' b'\xff\xff\xfc\x00' b'\xfe\xad\xca' b'\n\x0c\x0e\x10\x12\x14\x16\x18\x1a\x1c'
+[252, 0, 0, 0, 0, 1] The String 64512 fc 00 00 00 00 01
+b'\x01\x02\x03\x04'
+```
+
+
+
+### Односвязный список
+
+
+
+[Односвязный список](https://en.wikipedia.org/wiki/Linked_list#Singly_linked_list) представляет набор связанных узлов, каждый из которых хранит собственные данные и ссылку на следующий узел. В практике применим редко, но его любят использовать интервьюеры на собеседованиях, чтобы кандидат мог блеснуть своими алгоритмическими знаниями. В Python встроенной реализации не имеет, можно или использовать deque (в основе которого лежит двусвязный список), или написать свою реализацию.
+
+
+
+### Двусвязный список (Deque)
+
+
+
+Ссылки в каждом узле [двусвязного списка](https://en.wikipedia.org/wiki/Linked_list#Doubly_linked_list) указывают на предыдущий и на последующий узел в списке. Можно или использовать deque, или написать свою реализацию.
+
+
+
+```
+from collections import dequed = deque([1, 2, 3, 4], maxlen=1000)d.append(5) # Add element to the right side of the dequed.appendleft(0) # Add element to the left side of the deque by appending elements from iterabled.extend([6, 7]) # Extend the right side of the dequed.extendleft([-1, -2]) # Extend the left side of the dequeprint(d)a = d.pop() # Remove and return an element from the right side of the deque. Can raise an IndexErrorb = d.popleft() # Remove and return an element from the left side of the deque. Can raise an IndexErrorprint(a, b)print(d)
+```
+
+
+
+```
+deque([-2, -1, 0, 1, 2, 3, 4, 5, 6, 7], maxlen=1000)
+7 -2
+deque([-1, 0, 1, 2, 3, 4, 5, 6], maxlen=1000)
+```
+
+
+
+### Queue
+
+
+
+Queue реализует FIFO со множественными поставщиками данных и множественными потребителями. Может быть особенно полезен при многопоточности, позволяя корректно обмениваться информацией между потоками. Также существуют LifoQueue для реализации LIFO и PriorityQueue для реализации очереди с приоритетом.
+
+
+
+```
+from queue import Queueq = Queue(maxsize=1000)q.put("eat", block=True, timeout=10)q.put("sleep") # По умолчанию block=True, timeout=Noneq.put("code")q.put_nowait("repeat") # Эквивалент put("repeat", block=False). Если свободный слот не будет предоставлен немедленно, будет вызвано исключение queue.Fullprint(q.queue)a = q.get(block=True, timeout=10) # Удалить и возвратить элемент из FIFOb = q.get() # По умолчанию block=True, timeout=Nonec = q.get_nowait() # Эквивалент get(False)print(a, b, c, q.queue)
+```
+
+
+
+```
+deque(['eat', 'sleep', 'code', 'repeat'])
+eat sleep code deque(['repeat'])
+```
+
+
+
+### Бинарное дерево (binary tree)
+
+
+
+Иерархическая структура данных, в которой каждый узел имеет не более двух потомков. Встроенной реализации не имеет, нужно писать свою. Как правило, используются деревья с дополнительными свойствами, рассмотренные ниже.
+
+
+
+### Куча (heap)
+
+
+
+Бинарное дерево, удовлетворяющее свойство кучи: если B является узлом-потомком узла A, то ключ(A) ≥ ключ(B). Куча является максимально эффективной реализацией абстрактного типа данных, который называется очередью с приоритетом и поддерживающего две обязательные операции — добавить элемент и извлечь минимум (или максимум, в зависимости от реализации).
+
+
+
+В Python min-куча (наименьшее значение всегда лежит в корне) реализована на базе списка при помощи встроенного модуля heapq. Если вам нужна max-куча, с максимальным значением в корне, можете воспользоваться советами со [Stackoverflow](https://stackoverflow.com/questions/2501457/what-do-i-use-for-a-max-heap-implementation-in-python).
+
+
+
+```
+import heapqh = [211, 1, 43, 79, 12, 5, -10, 0]heapq.heapify(h) # Превращаем список в кучуprint(h)heapq.heappush(h, 2) # Добавляем элементprint(h)m = heapq.heappop(h) # Извлекаем минимальный элементprint(h, m)
+```
+
+
+
+```
+[-10, 0, 5, 1, 12, 211, 43, 79]
+[-10, 0, 5, 1, 12, 211, 43, 79, 2]
+[0, 1, 5, 2, 12, 211, 43, 79] -10
+```
+
+
+
+Пробежимся коротенько по остальным структурам данных, которые в Python не имеют встроенной реализации, но, тем не менее, могут весьма пригодиться в реальном проекте.
+
+
+
+### Би-дерево (B-tree)
+
+
+
+Сбалансированное дерево, оптимизированное для доступа к относительно медленным элементам памяти (например, дисковым структурам или индексам баз данных); как ветви, так и листья представляют собой списки (для того, чтобы можно было считать такой список в один проход для дальнейшего быстрого разбора в ОЗУ). Нужно писать свою реализацию. Либо — воспользоваться встроенной в Python поддержкой базы данных sqlite3, эта БД как раз реализована на би-дереве.
+
+
+
+### Красно-черное дерево
+
+
+
+Самобалансирующееся двоичное дерево поиска, позволяющее быстро выполнять основные операции: добавление, удаление и поиск узла. Сбалансированность достигается за счёт введения дополнительного признака узла дерева — «цвета». Этот атрибут может принимать одно из двух возможных значений — «чёрный» или «красный». Листовые узлы КЧ деревьев не содержат данных, поэтому не требуют выделения памяти — достаточно просто записать в узле-предке нулевой указатель на потомка. Нужно писать свою реализацию.
+Возможно, вы читали о том, что при собеседовании в FAANG претендентов «заставляют крутить красно-черное дерево на доске». Это «кружение» и есть балансировка, после операции вставки или удаления элемента дерево нужно отбалансировать, с примерным объемом кода вы можете ознакомиться [здесь](https://blog.boot.dev/python/red-black-tree-python/) или [здесь](https://codereview.stackexchange.com/questions/244971/red-black-tree-implementation-in-python).
+
+
+
+### АВЛ-дерево
+
+
+
+В АВЛ-деревьях операции вставки и удаления работают медленнее, чем в красно-черных деревьях (при том же количестве листьев красно-чёрное дерево может быть выше АВЛ-дерева, но не более чем в 1,388 раза). Поиск же в АВЛ-дереве выполняется быстрее (максимальная разница в скорости поиска составляет 39 %). Нужно писать свою реализацию.
+
+
+
+### Префиксное дерево
+
+
+
+[Префиксное дерево](https://en.wikipedia.org/wiki/Trie) (или trie) — структура данных, позволяющая хранить ассоциативный массив, ключами которого являются строки. Нужно писать свою реализацию.
+
+
+
+### Таблица выбора структуры данных
+
+
+
+В квадратных скобках показан худший случай.
+
+
+
+|Структура|Реализация|Применение|Индексация|Поиск|Вставка|Удаление|Память|
+|---|---|---|---|---|---|---|---|
+|Динамический массив|list||1|n|n|n|n|
+|Хэш таблица|dict, set|||1 <br>[n]|1 <br>[n]|1 <br>[n]|n|
+|Массив|array, bytes, bytearray|Для хранения однотипных данных|1|n|n|n|n|
+|Односвязный список|— (~deque)||n|n|1|1|n|
+|Двусвязный список|deque|FIFO, LIFO|n|n|1|1|n|
+|Бинарное дерево|-||logn <br>[n]|logn <br>[n]|logn <br>[n]|logn <br>[n]|n|
+|Куча|heapq|Очередь с приоритетом||1 <br>(find min)|logn|logn <br>(del min)|n|
+|B-tree (Би-дерево)|~sqlite|Для памяти с медленным доступом|logn|logn|logn|logn|n|
+|КЧ дерево|-||logn|logn|logn|logn|n|
+|АВЛ дерево|-||logn|logn|logn|logn|n|
+|Префиксное дерево|-|T9, <br>алгоритм [Ахо–Корасик](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm), <br>алгоритм [LZW](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch)||key|key|key||
+
+
+
+### Перечисление (Enum, IntEnum)
+
+
+
+Удобные конструкции для определения заранее известных перечислений.
+
+
+
+```
+from enum import Enum, autoimport randomclass Currency(Enum): euro = 1 us_dollar = 2 yuan = auto()local_currency = Currency.us_dollarprint(local_currency)local_currency = Currency["us_dollar"] # Может вызвать исключение KeyErrorprint(local_currency)local_currency = Currency(2) # Может вызвать исключение ValueErrorprint(local_currency)print(local_currency.name)print(local_currency.value)list_of_members = list(Currency)member_names = [e.name for e in Currency]member_values = [e.value for e in Currency]random_member = random.choice(list(Currency))print(list_of_members, "\n", member_names, "\n", member_values, "\n", random_member)
+```
+
+
+
+```
+Currency.us_dollar
+Currency.us_dollar
+Currency.us_dollar
+us_dollar
+2
+[<Currency.euro: 1>, <Currency.us_dollar: 2>, <Currency.yuan: 3>]
+ ['euro', 'us_dollar', 'yuan']
+ [1, 2, 3]
+ Currency.euro
+```
+
+
+
+### Целочисленный диапазон (range)
+
+
+
+range() возвращает иммутабельную последовательность чисел, которая часто используется как задатчик диапазона для цикла for.
+
+
+
+```
+r1: range = range(11) # Возвращает последовательность чисел от 0 до 10r2: range = range(5, 21) # Возвращает последовательность чисел от 5 до 20r3: range = range(20, 9, -2) # Возвращает последовательность чисел от 20 до 10 с шагом 2print("To exclusive: ", end="")for i in r1: print(f"{i} ", end="")print("\nFrom inclusive to exclusive: ", end="")for i in r2: print(f"{i} ", end="")print("\nFrom inclusive to exclusive with step: ", end="")for i in r3: print(f"{i} ", end="")print(f"\nFrom = {r3.start}")print(f"To = {r3.stop}")
+```
+
+
+
+```
+To exclusive: 0 1 2 3 4 5 6 7 8 9 10
+From inclusive to exclusive: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
+From inclusive to exclusive with step: 20 18 16 14 12 10
+From = 20
+To = 9
+```
+
+
+
+### Классы данных (dataclass)
+
+
+
+Декоратор, автоматически создающий методы init(), repr() и eq(). Нужен для создания классов, главной задачей которых является хранение данных. Аннотации типов обязательны. Существует более продвинутая альтернатива под названием [attrs](https://pypi.org/project/attrs/).
+
+
+
+```
+from dataclasses import dataclassfrom decimal import *from datetime import datetime@dataclassclass Transaction: value: Decimal issuer: str = "Default Bank" dt: datetime = datetime.now()t1 = Transaction(value=1000_000, issuer="Deutsche Bank", dt = datetime(2022, 1, 1, 12))t2 = Transaction(1000)print(t1)print(t2)
+```
+
+
+
+```
+Transaction(value=1000000, issuer='Deutsche Bank', dt=datetime.datetime(2022, 1, 1, 12, 0))
+Transaction(value=1000, issuer='Default Bank', dt=datetime.datetime(2022, 9, 6, 17, 50, 36, 162897))
+```
+
+
+
+Dataclass может быть сделан иммутабельным с директивой _frozen=True_.
+
+
+
+```
+from dataclasses import dataclass@dataclass(frozen=True)class User: name: str account: int
+```
+
+
+
+### Бинарная запаковка (struct)
+
+
+
+Запаковка (и распаковка, разумеется) данных в байтовые последовательности с предопределенными размерами каждого элемента данных, их порядка в структуре, а также порядка байт для многобайтовых типов данных. Позволяет превращать Python-овский int в, например, short int или long int ([подробности про систему типов языка Си](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%82%D0%B8%D0%BF%D0%BE%D0%B2_%D0%A1%D0%B8)).
+При работе со структурами вам нужно будет знать, что такое little-endian и big-endian, а также не забывать, что размер типа данных в Си бывает разным.
+
+
+
+```
+from struct import pack, unpack, iter_unpackb = pack(">hhll", 1, 2, 3, 4)print(b)t = unpack(">hhll", b)print(t)i = pack("ii", 1, 2) * 5print(i)print(list(iter_unpack('ii', i)))
+```
+
+
+
+```
+b'\x00\x01\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04'
+(1, 2, 3, 4)
+b'\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00'
+[(1, 2), (1, 2), (1, 2), (1, 2), (1, 2)]
+```
+
+
+
+## Строка (string)
+
+
+
+Строки в Python 3 — иммутабельные последовательности, использующие кодировку Unicode.
+
+
+
+```
+se: str = "" # Пустая строкаsi: str = str(12345) # Создает строку из числаsj: str = " ".join(["Follow", "the", "white", "rabbit"]) # Собирает строку из кусочков, используя указанный сепараторprint(f"Joined string: {sj}")is_contains: bool = "rabbit" in sj # Проверка наличия подстрокиis_startswith = sj.startswith("Foll")is_endswith = sj.endswith("bbit")print(f"is_contains = {is_contains}, is_startswith = {is_startswith}, is_endswith = {is_endswith}")sr: str = sj.replace("rabbit", "sheep") # Замена подстроки. Можно указать количество замен: sr: str = sj.replace("rabbit", "sheep", times)print(f"After replace: {sr}")i1 = sr.find("rabbit") # Возвращает стартовый индекс первого вхождения или -1. Есть еще rfind(), начинающий искать с конца строкиi2 = sr.index("sheep") # Возвращает стартовый индекс первого вхождения или выкидывает ValueError. Есть еще rindex(), начинающий искать с конца строкиprint(f"Start index of 'rabbit' is {i1}, start index of 'sheep' is {i2}")d = str.maketrans({"a" : "x", "b" : "y", "c" : "z"})st = "abc".translate(d)print(f"Translate string: {st}")sr = sj[::-1] # Реверс через slice с отрицательным шагомprint(f"Reverse string: {sr}")
+```
+
+
+
+```
+Joined string: Follow the white rabbit
+is_contains = True, is_startswith = True, is_endswith = True
+After replace: Follow the white sheep
+Start index of 'rabbit' is -1, start index of 'sheep' is 17
+Translate string: xyz
+Reverse string: tibbar etihw eht wolloF
+```
+
+
+
+### Datetime
+
+
+
+Для работы с датами и временем в _datetime_ есть типы _date_, _time_, _datetime_ и _timedelta_. Все они хешируемы и иммутабельны.
+
+
+
+### Конструкторы
+
+
+
+```
+from datetime import date, time, datetime, timedeltad: date = date(year=1964, month=9, day=2)t: time = time(hour=12, minute=30, second=0, microsecond=0, tzinfo=None, fold=0)dt: datetime = datetime(year=1964, month=9, day=2, hour=10, minute=30, second=0)td: timedelta = timedelta(weeks=1, days=1, hours=12, minutes=13, seconds=14)print (f"{d}\n {t}\n {dt}\n {td}")
+```
+
+
+
+```
+1964-09-02
+ 12:30:00
+ 1964-09-02 10:30:00
+ 8 days, 12:13:14
+```
+
+
+
+### Now
+
+
+
+Получение текущей даты или даты/времени.
+
+
+
+```
+from datetime import date, datetimeimport pytzimport timed: date = date.today()dt1: datetime = datetime.today()dt2: datetime = datetime.utcnow()dt3: datetime = datetime.now(pytz.timezone('US/Pacific'))t1 = time.time() # Эпоха Unixt2 = time.ctime()print (f"{d}\n {dt1}\n {dt2}\n {dt3}\n {t1}\n {t2}")
+```
+
+
+
+```
+2022-09-27
+ 2022-09-27 09:47:02.430474
+ 2022-09-27 04:47:02.430474
+ 2022-09-26 21:47:02.430474-07:00
+ 1664254022.4304743
+ Tue Sep 27 09:47:02 2022
+```
+
+
+
+### Timezone
+
+
+
+Часовые пояса.
+
+
+
+```
+from datetime import date, time, datetime, timedelta, tzinfofrom dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginarytz1: tzinfo = UTC # Часовой пояс UTCtz2: tzinfo = tzlocal() # Местный часовой поясtz3: tzinfo = gettz() # Местный часовой поясtz4: tzinfo = gettz("America/Chicago") # Или, например, "Asia/Kolkata". Полный список: en.wikipedia.org/wiki/List_of_tz_database_time_zoneslocal_dt = datetime.today()utc_dt = local_dt.astimezone(UTC) # Конвертация местного часового пояса в часовой пояс UTCprint (f"{tz1}\n {tz2}\n {tz3}\n {tz4}\n {local_dt}\n {utc_dt}")
+```
+
+
+
+```
+tzutc()
+ tzlocal()
+ tzlocal()
+ tzfile('US/Central')
+ 2022-09-27 09:19:35.399362
+ 2022-09-27 04:19:35.399362+00:00
+```
+
+
+
+## 2. Обработка данных
+
+
+
+![Data management](https://habrastorage.org/r/w1560/getpro/habr/post_images/26e/edf/fd8/26eedffd8bf1081be95015e2dccecfc7.png)
+
+
+
+### Срез (slice)
+
+
+
+Самый простой метод обработки данных, просто возвращает ту часть данных, местоположение которой (индексы) удовлетворяет определенным условиям.
+
+
+
+```
+a:str = "Pack my box with five dozen liquor jugs"start, stop = 8, 21b:str = a[start:stop] # Значения от start до stop-1c:str = a[start:] # Значения от start до конца структурыd:str = a[:stop] # Значения от начала до stop-1e:str = a[:] # Полная копия структурыprint(b, "\n", c, "\n", d, "\n", e, "\n")
+```
+
+
+
+```
+box with five
+ box with five dozen liquor jugs
+ Pack my box with five
+ Pack my box with five dozen liquor jugs
+```
+
+
+
+Значения start и stop могут быть отрицательными, это будет означать, что отсчет ведется от конца структуры. Можно также использовать значение step, чтобы на выход среза попали не все подряд данные из входной структуры.
+
+
+
+```
+a:str = "Step on no pets"b:str = a[-4:] # «Хвостик»c:str = a[::-1] # Реверс входной строкиd:str = a[4::-1] # Первые четыре значения, реверсированыe:str = a[::2] # Каждый второй символprint(b, "\n", c, "\n", d, "\n", e, "\n")
+```
+
+
+
+```
+pets
+ step on no petS
+ petS
+ Se nn es
+```
+
+
+
+### Сортировка (sort, sorted)
+
+
+
+В сортировке всё самое интересное спрятано под капотом (мы ненадолго вернемся к этой теме чуть ниже, в разделе «Алгоритмы»), пока рассмотрим только Python-специфичный синтаксис.
+Надо различать методы sort() и sorted(), первый сортирует данные [in-place](https://en.wikipedia.org/wiki/In-place_algorithm), второй порождает новую структуру.
+
+
+
+```
+a: list = [5, 2, 3, 1, 4]b: list = sorted(a)print(a, b)a.sort()print(a)
+```
+
+
+
+```
+[5, 2, 3, 1, 4] [1, 2, 3, 4, 5]
+[1, 2, 3, 4, 5]
+```
+
+
+
+И sort(), и sorted() имеют параметр key для указания функции, которая будет вызываться на каждом элементе. Если вам больше по нраву сортировка при помощи функции, принимающей два аргумента (или вы привыкли к cmp в Python 2), присмотритесь к functools.cmp_to_key().
+
+
+
+```
+# Регистрозависимое сравнение строкdinos: str = "Dinosaurs were Big and small"a = sorted(dinos.split())print(a)# Регистронезависимое сравнение строкdinos: str = "Dinosaurs were Big and small"b = sorted(dinos.split(), key=str.lower)print(b)
+```
+
+
+
+```
+['Big', 'Dinosaurs', 'and', 'small', 'were']
+['and', 'Big', 'Dinosaurs', 'small', 'were']
+```
+
+
+
+Сложносочиненные структуры данных можно сортировать по key=lambda el: el[1] или даже, например по key=lambda el: (el[1], el[0]).
+
+
+
+### Comprehension
+
+
+
+Comprehension, которое переводится то как включение в список, то как абстракция списков ([Википедия](https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D0%BE%D0%B5_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)), то вообще никак не переводится — способ компактного описания операций обработки списков (а применительно к Python — еще и словарей, и множеств).
+
+
+
+Проще говоря, если вам нужно получить из списка другой список, включающий только те значения, которые удовлетворяют какому-то определенному условию, или вычисляемые из первого списка по каким-то определенным правилам, то comprehension — претендент на решение этой задачи № 1.
+
+
+
+```
+# Примеры Comprehensiona = [i+1 for i in range(10)] # listb = {i for i in range(10) if i > 5} # setc = (2*i+5 for i in range(10)) # iterd = {i: i**2 for i in range(10)} # dictprint(a,"\n", b, "\n", list(c), "\n", d)
+```
+
+
+
+```
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ {8, 9, 6, 7}
+ [5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
+ {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
+```
+
+
+
+Тут главное не перегнуть палку. Если запись comprehension становится слишком сложной и нечитаемой, возможно, стоит развернуть логику в «нормальный» цикл или в другой более удобочитаемый алгоритм. Comprehension соблазняет записывать «однострочниками» достаточно сложные выражения, но не забывайте, что программист примерно 90 % времени читает код, и только 10 % пишет, так что если выражение будет плохочитаемым, вы усложните жизнь и себе, и свои коллегам.
+
+
+
+Есть более-менее [удачные](https://leetcode.com/problems/flipping-an-image/discuss/2378360/python-1-liner-988-speed-97-mem) «однострочники», есть быстрые, но [плохочитаемые](https://leetcode.com/problems/reverse-string-ii/discuss/2281269/python-fast-beats-984-and-short-almost-1-line-solution-with-python-38-features-pep572), написанные из спортивного интереса (это ссылки на решенные мной задачки на leetcode), желательно использовать comprehension в меру; лучше написать понятный развернутый алгоритм, чем непонятный, но обложенный пояснениями (если нет особых требований к производительности, само собой).
+
+
+
+Еще немного про list comprehension:
+
+
+
+```
+# new_list = [expression for member in iterable (if conditional)]fruits: list = ["Lemon", "Apple", "Banana", "Kiwi", "Watermelon", "Pear"]e_fruits = [fruit for fruit in fruits if "e" in fruit]# ☝ условиеprint(e_fruits)upper_fruits = [fruit.upper() for fruit in fruits]# ☝ выражениеprint(upper_fruits)# Пример разбиения списка на фрагменты одинаковой длиныchunk_len = 2chunk_fruits = [fruits[i:i + chunk_len] for i in range(0, len(fruits), chunk_len)]print(chunk_fruits)
+```
+
+
+
+```
+['Lemon', 'Apple', 'Watermelon', 'Pear']
+['LEMON', 'APPLE', 'BANANA', 'KIWI', 'WATERMELON', 'PEAR']
+[['Lemon', 'Apple'], ['Banana', 'Kiwi'], ['Watermelon', 'Pear']]
+```
+
+
+
+Dict comprehension, включение в словарь:
+
+
+
+```
+# new_dict = {expression for member in iterable (if conditional)}d: dict = {"Italy": "Pizza", "US": "Hot-Dog", "China": "Dim Sum", "South Korea": "Kimchi"}print(d)a: dict = {k: v for k, v in d.items() if "i" in v} # Вернет новый словарь, отфильтрованный по значениюprint(a)b: dict = {k: v for k, v in d.items() if "i" in k} # Вернет новый словарь, отфильтрованный по ключуprint(b)c: dict = {k: v for k, v in d.items() if len(v) >= 7} # Вернет новый словарь, отфильтрованный по длине значенийprint(c)
+```
+
+
+
+```
+{'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dim Sum', 'South Korea': 'Kimchi'}
+{'Italy': 'Pizza', 'China': 'Dim Sum', 'South Korea': 'Kimchi'}
+{'China': 'Dim Sum'}
+{'US': 'Hot-Dog', 'China': 'Dim Sum'}
+```
+
+
+
+Попробуйте самостоятельно поиграться с set comprehension. Не забывайте, что set «переваривает» только уникальные значения, поэтому в результате вы можете получить не совсем то, на что рассчитывали.
+
+
+
+Попробуйте также освоить nested (вложенный) comprehension, используя конструкции вида [[func(y) for y in x] for x in n]. Для примера создайте двумерный массив, содержащий случайные значения, среднее значение которых плавно нарастает ближе к правому нижнему углу (если не получится, готовый пример есть чуть ниже, в коде, иллюстрирующем применение matplotlib).
+
+
+
+### Операции над строками. lower(), upper(), capitalize() и title()
+
+
+
+```
+s: str = "camelCase string"print(s.lower())print(s.upper())print(s.capitalize())print(s.title())
+```
+
+
+
+```
+camelcase string
+CAMELCASE STRING
+Camelcase string
+Camelcase String
+```
+
+
+
+### strip()
+
+
+
+```
+s: str = " ~~##A big blahblahblah##~~ "s = s.strip() # Strips all whitespace characters from both endsprint(s)s = s.strip("~#") # Strips all passed characters from both endsprint(s)s = s.lstrip(" A") # Strips all passed characters from left endprint(s)s = s.rstrip("habl") # Strips all passed characters from right endprint(s)
+```
+
+
+
+```
+~~##A big blahblahblah##~~
+A big blahblahblah
+big blahblahblah
+big
+```
+
+
+
+### split()
+
+
+
+```
+s1: str = "Follow the white rabbit, Neo"c1 = s1.split() # Splits on one or more whitespace charactersprint(c1)c2 = s1.split(sep=", ", maxsplit=1) # Splits on "sep" str at most "maxsplit" timesprint(c2)s2: str = "Beware the Jabberwock, my son!\n The jaws that bite, the claws that catch!"c3 = s2.splitlines(keepends=False) # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n.print(c3)# split() vs rsplit()c4 = s2.split(maxsplit=2)c5 = s2.rsplit(maxsplit=2)print(c4, c5)
+```
+
+
+
+```
+['Follow', 'the', 'white', 'rabbit,', 'Neo']
+['Follow the white rabbit', 'Neo']
+['Beware the Jabberwock, my son!', ' The jaws that bite, the claws that catch!']
+['Beware', 'the', 'Jabberwock, my son!\n The jaws that bite, the claws that catch!'] ['Beware the Jabberwock, my son!\n The jaws that bite, the claws', 'that', 'catch!']
+```
+
+
+
+### ord(), chr()
+
+
+
+```
+s1: str = "abcABC!"for ch in s1: print(f"{ch} -> {ord(ch)}") # Returns an integer representing the Unicode characternums = [72, 101, 108, 108, 111, 33]for num in nums: print(f"{num} -> {chr(num)}")
+```
+
+
+
+```
+a -> 97
+b -> 98
+c -> 99
+A -> 65
+B -> 66
+C -> 67
+! -> 33
+72 -> H
+101 -> e
+108 -> l
+108 -> l
+111 -> o
+33 -> !
+```
+
+
+
+### Regex
+
+
+
+Регулярные выражения — отдельная область знаний, и весьма-весьма непростая область. Тут, пожалуй, самое время для бородатой шутки про то, что если вы решили свою проблему при помощи регулярных выражений — теперь у вас две проблемы.
+
+
+
+Регулярки похожи на вхождение в воду на пляже острова Гуам в сторону Марианской впадины — даже когда вы думаете, что погрузились _реально_ глубоко, то, скорее всего, вы просто не видите впередилежащей бездны. Но — знать регулярные выражения, хотя бы на начальном уровне, необходимо для решения целого класса задач, а то, что вёрткие регулярки периодически поворачиваются к вам своими, кхм… новыми гранями, придется простить, переварить и принять.
+
+
+
+Вот [здесь](https://habr.com/ru/post/349860/) есть грамотное и методически выдержанное введение в тему, пока же окинем взглядом основные возможности регулярных выражений:
+
+
+
+```
+import res1: str = "123 abc ABC 456"m1 = re.search("[aA]", s1) # Ищет первое вхождение паттерна, при неудаче возвращает Noneprint(m1, m1.group(0))m2 = re.fullmatch("[aA]", s1) # Проверка, подходит ли строка под шаблонprint(m2)c1: list = re.findall("[aA]", s1) # Найти в строке все непересекающиеся шаблоныprint(c1)def replacer(s): return chr(ord(s[0]) + 1) # Следующий символ из алфавитаs2 = re.sub("\w", replacer, s1) # Вы можете использовать функцию вместо шаблонаprint(s2)c2 = re.split("\d", s1)print(c2)iter = re.finditer("\D", s1) # Итератор по непересекающимся шаблонамfor ch in iter: print(ch.group(0), end= "")
+```
+
+
+
+```
+<re.Match object; span=(4, 5), match='a'> a
+None
+['a', 'A']
+234 bcd BCD 567
+['', '', '', ' abc ABC ', '', '', '']
+ abc ABC
+```
+
+
+
+### Match Object
+
+
+
+```
+import rem3 = re.match(r"(\w+) (\w+)", "John Connor, leader of the Resistance")s3: str = m3.group(0) # Возвращает полное совпадениеs4: str = m3.group(1) # Возвращает часть в первых скобкахt1: tuple = m3.groups()start: int = m3.start() # Возвращает начальный индекс совпаденияend: int = m3.end() # Возвращает конечный индекс совпаденияt2: tuple[int, int] = m3.span() # Кортеж (start, end)print (f"{s3}\n {s4}\n {t1}\n {start}\n {end}\n {t2}\n")
+```
+
+
+
+```
+John Connor
+ John
+ ('John', 'Connor')
+ 0
+ 11
+ (0, 11)
+```
+
+
+
+### Создание переменных datetime
+
+
+
+Python использует Unix Epoch: "1970-01-01 00:00 UTC"
+
+
+
+```
+from datetime import datetimefrom dateutil.tz import tzlocaldt1: datetime = datetime.fromisoformat("2021-10-04 00:05:23.555+00:00") # Может вызвать ValueErrordt2: datetime = datetime.strptime("21/10/04 17:30", "%d/%m/%y %H:%M") # Подробнее про форматы - https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codesdt3: datetime = datetime.fromordinal(100_000) # 100000-й день от 1.1.0001dt4: datetime = datetime.fromtimestamp(20_000_000.01) # Время в секундах с начала Unix Epochtz = tzlocal()dt5: datetime = datetime.fromtimestamp(20_000_000.01, tz) # С учетом часового поясаprint (f"{dt1}\n {dt2}\n {dt3}\n {dt4}\n {dt5}")
+```
+
+
+
+```
+2021-10-04 00:05:23.555000+00:00
+ 2004-10-21 17:30:00
+ 0274-10-16 00:00:00
+ 1970-08-20 16:33:20.010000
+ 1970-08-20 16:33:20.010000+05:00
+```
+
+
+
+### Преобразование переменных datetime
+
+
+
+```
+from datetime import datetimedt1: datetime = datetime.today()s1: str = dt1.isoformat()s2: str = dt1.strftime("%d/%m/%y %H:%M") # https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codesi: int = dt1.toordinal()a: float = dt1.timestamp() # Секунды с начала Unix Epochprint (f"{dt1}\n {s1}\n {s2}\n {i}\n {a}")
+```
+
+
+
+```
+2022-09-06 17:50:38.041159
+ 2022-09-06T17:50:38.041159
+ 06/09/22 17:50
+ 738404
+ 1662468638.041159
+```
+
+
+
+### Арифметика datetime
+
+
+
+```
+from datetime import date, time, datetime, timedeltafrom dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginaryd: date = date.today()dt1: datetime = datetime.today()dt2: datetime = datetime(year=1981, month=12, day=2)td1: timedelta = timedelta(days=5)td2: timedelta = timedelta(days=1)d = d + td1 # date = date ± timedeltadt3 = dt1 - td1 # datetime = datetime ± timedeltatd3 = dt1 - dt2 # timedelta = datetime - datetimetd4 = 10 * td1 # timedelta = const * timedeltac: float = td1/td2 # timedelta/timedeltaprint (f"{d}\n {dt3}\n {td3}\n {td4}\n {c}")
+```
+
+
+
+```
+2022-09-11
+ 2022-09-01 17:50:38.132916
+ 14888 days, 17:50:38.132916
+ 50 days, 0:00:00
+ 5.0
+```
+
+
+
+### bisect и бинарный поиск
+
+
+
+Бинарный поиск существенно быстрее, чем обычный (см. раздел «Алгоритмы»), но требует предварительной сортировки коллекции, по которой осуществляется поиск.
+
+
+
+```
+import bisecta: list[int] = [12, 6, 8, 19, 1, 33]a.sort()print(f"Sorted: {a}")print(bisect.bisect(a, 20)) # Найти индекс для потенциальной вставкиbisect.insort(a, 15) # Вставка значения в отсортированную последовательностьprint(a)# Бинарный поискdef binary_search(a, x, lo=0, hi=None): if hi is None: hi = len(a) pos = bisect.bisect_left(a, x, lo, hi) return pos if pos != hi and a[pos] == x else -1print(binary_search(a, 15))
+```
+
+
+
+```
+Sorted: [1, 6, 8, 12, 19, 33]
+5
+[1, 6, 8, 12, 15, 19, 33]
+4
+```
+
+
+
+### Функциональное программирование (Map, Filter, Reduce, Partial)
+
+
+
+На случай, если начиная с этого момента и до конца текущего жизненного цикла вы собираетесь к месту и не месту использовать приёмы функционального программирования, чтобы сделать свой код «воистину крутым», просто процитирую вам Джоэля Граса, автора книги «Data Science: Наука о данных с нуля»: «В первом издании этой книги были представлены функции partial, map, reduce и filter языка Python. На своем пути к просветлению я понял, что этих функций лучше избегать, и их использование в книге было заменено включениями в список, циклами и другими, более Python'овскими конструкциями». Такие дела…
+
+
+
+```
+import functools# Преобразует все входящие значения при помощи указанной функцииiter1 = map(lambda x: x + 1, range(10))print(list(iter1))# Передает в выходной итератор только значения, удовлетворяющие условиюiter2 = filter(lambda x: x > 5, range(10))print(list(iter2))# Применяет указанную функцию ко всей последовательности входных данных, сводя их к единственному значениюa = functools.reduce(lambda out, x: out + x, range(10))print(a)
+```
+
+
+
+```
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+[6, 7, 8, 9]
+45
+```
+
+
+
+```
+import functoolsdef sum(a,b): return a + badd_const = functools.partial(sum, 10)print(add_const(5))
+```
+
+
+
+```
+15
+```
+
+
+
+Если вам не сразу станет понятно, как работает функция partial (и зачем она нужна), не расстраивайтесь, вы не одиноки :). Вот, пожалуйста, тема на Stackoverflow: «[I am not able to get my head on how the partial works](https://stackoverflow.com/questions/15331726/how-does-functools-partial-do-what-it-does)». Там, кстати, есть совет, как partial могут быть полезны при организации pipe с включением функций, имеющих разное количество аргументов.
+
+
+
+### Any, All
+
+
+
+any() вернет True, если хотя бы один элемент итерируемой коллекции истинен, all() вернет True только в случае истинности всех элементов коллекции.
+
+
+
+```
+animals = ["Squirrel", "Beaver", "Fox"]sentence = "Bison likes squirrels and beavers"any_animal: bool = any(animal.lower() in sentence.lower() for animal in animals)print(any_animal)all_animal: bool = all(animal.lower() in sentence.lower() for animal in animals)print(all_animal)
+```
+
+
+
+```
+True
+False
+```
+
+
+
+### Файлы
+
+
+
+Файловые операции стоят немного особняком от остальных методов обработки данных, как подразумевающие не сиюминутную торопливую обработку информации, а взаимодействие с неким постоянным энергонезависимым хранилищем данных. Так что если вам нужно сохранить данные _на завтра_, или, наоборот, нужно прочитать данные, которые вам предоставили _неделю назад_, то вам, очевидно, нужно будет работать с файлами. В файлах же осядет информация, которую мы передаем базам данных, но эту тему мы рассмотри ниже.
+
+
+
+```
+f = open("f.txt", mode='r', encoding="utf-8", newline=None)print(f.read())
+```
+
+
+
+```
+Hello from file!
+```
+
+
+
+На всякий случай, если вы испытываете программистский зуд даже небольшой степени выраженности, напоминаю — обязательно прогоняйте в IDE все непонятные куски кода, не надо на них _смотреть_, их надо видоизменять, корректировать, дорабатывать; только когда концы свяжутся, только когда вы поймете, как функционирует этот кусочек кода, только тогда промелькнёт маленькая искорка и ваша квалификация как программиста немного подрастёт.
+
+
+
+Режимы (mode):
+"r" — чтение (поведение по умолчанию)
+"w" — запись (информация, ранее присутствующая в файле, будет стёрта)
+"x" — эксклюзивное создание и запись; если файл уже существует, будет выброшено исключение FileExistsError
+"a" — открытие с последующим добавлением в конец файла
+"w+" — чтение и запись
+"r+" — чтение и запись с начала файла
+"a+" — чтение и запись с конца файла
+"t" — текстовый режим ("rt", "wt" и т. д.; поведение по умолчанию)
+"b" — двоичный режим ("rb", "wb", "xb" и т. д.)
+
+
+
+encoding=None — будет использована кодировка по умолчанию (зависит от системы, см. getpreferredencoding()). Если нет специальных требований, просто используйте везде encoding="utf-8"; без этого, например, русский текст запишется в текстовый файл в виде человеконечитаемой последовательности.
+
+
+
+newline=None — при чтении системные символы конца строки будут конвертированы в "\n"; при записи, наоборот, "\n" будут конвертированы в системные символы конца строки.
+
+
+
+Возможные исключения при работе с файлами:
+_FileNotFoundError_ при чтении в режиме "r" или "r+".
+_FileExistsError_ при записи в режиме "x".
+_IsADirectoryError_, _PermissionError_ — в любом режиме.
+
+
+
+### Чтение из файла
+
+
+
+Открывает файл и возвращает файловый объект.
+Для работы с файлами лучше использовать менеджеры контекста (рассмотрены ниже), т. е. конструкции вида "with open...". Даже если что-то пойдет не так, как задумано (например, вы не обработаете исключение во время работы с файлом), менеджер контекста «зачистит хвосты», и ваша оплошность не отразится, например, на файловой системе.
+
+
+
+```
+with open("f.txt", encoding="utf-8") as f: chars = f.read(5) # Reads chars/bytes or until EOF print(chars) f.seek(0) # Moves to the start of the file. Also seek(offset) and seek(±offset, anchor), where anchor is 0 for start, 1 for current position and 2 for end lines: list[str] = f.readlines() # Also readline() print(lines)
+```
+
+
+
+```
+Hello
+['Hello from file!']
+```
+
+
+
+### Запись в файл
+
+
+
+```
+with open("f.txt", "w", encoding="utf-8") as f: f.write("Hello from file!") # Или f.writelines(<collection>)
+```
+
+
+
+### JSON
+
+
+
+Человекочитаемый формат для хранения и передачи данных.
+
+
+
+```
+import jsond: dict = {1: "Lemon", 2: "Apple", 3: "Banana!"}object_as_string: str = json.dumps(d, indent=2)print(object_as_string)restored_object = json.loads(object_as_string)# Write object to JSON filewith open("1.json", 'w', encoding='utf-8') as file: json.dump(d, file, indent=2)# Read object from JSON filewith open("1.json", encoding='utf-8') as file: restored_from_file = json.load(file)print(restored_from_file)
+```
+
+
+
+```
+{
+ "1": "Lemon",
+ "2": "Apple",
+ "3": "Banana!"
+}
+{'1': 'Lemon', '2': 'Apple', '3': 'Banana!'}
+```
+
+
+
+### Pickle
+
+
+
+Бинарный формат для хранения и передачи данных.
+
+
+
+```
+import pickled: dict = {1: "Lemon", 2: "Apple", 3: "Banana!"}# Запись объекта в бинарный файлwith open("1.bin", "wb") as file: pickle.dump(d, file)# Чтение объекта из файлаwith open("1.bin", "rb") as file: restored_from_file = pickle.load(file)print(restored_from_file)
+```
+
+
+
+```
+{1: 'Lemon', 2: 'Apple', 3: 'Banana!'}
+```
+
+
+
+### Protocol Buffers
+
+
+
+Если вы хотите передавать и хранить данные, используя универсальную структуру, одинаково хорошо понимаемую всеми языками программирования (как JSON) и занимающую мало места (как Pickle), то можно посмотреть в сторону Protocol Buffers ([Wikipedia](https://en.wikipedia.org/wiki/Protocol_Buffers), [примеры для Python](https://developers.google.com/protocol-buffers/docs/pythontutorial)). Есть еще альтернативы, например, [FlatBuffers](https://google.github.io/flatbuffers/), [Apache Avro](https://avro.apache.org/) или [Thrift](https://thrift.apache.org/).
+
+
+
+### Пути (Paths)
+
+
+
+При работе с файлами не обойтись без манипулирования файловыми путями.
+
+
+
+```
+from os import getcwd, path, listdirfrom pathlib import Paths1: str = getcwd() # Возвращает текущую рабочую директориюprint(s1)s2: str = path.abspath("f.txt") # Возвращает полный путьprint(s2)s3: str = path.basename(s2) # Возвращает имя файлаs4: str = path.dirname(s2) # Возвращает путь без файлаt1: tuple = path.splitext(s2) # Возвращает кортеж из пути и имени файлаprint(s3, s4, t1)p = Path(s2)st = p.stat()print(st)b1: bool = p.exists()b2: bool = p.is_file()b3: bool = p.is_dir()print(b1, b2, b3)c: list = listdir(path=s1) # Возвращает список имен файлов, находящихся по указанному путиprint(c)s5: str = p.stem # Возвращает имя файла без расширенияs6: str = p.suffix # Возвращает расширение файлаt2: tuple = p.parts # Возвращает все элементы пути как отдельные строкиprint(s5, s6, t2)
+```
+
+
+
+```
+c:\Works\amaargiru\pycore
+c:\Works\amaargiru\pycore\f.txt
+f.txt c:\Works\amaargiru\pycore ('c:\\Works\\amaargiru\\pycore\\f', '.txt')
+os.stat_result(st_mode=33206, st_ino=2251799814917120, st_dev=3628794147, st_nlink=1, st_uid=0, st_gid=0, st_size=16, st_atime=1662468638, st_mtime=1662468638, st_ctime=1661089564)
+True True False
+['.git', '.gitignore', '.pytest_cache', '01_python.ipynb', '01_python.md', '02_postgre.md', '03_architecture.md', '04_algorithms.ipynb', '04_algorithms.md', '05_admin_devops.md', '06_pytest_mock.ipynb', '06_pytest_mock.md', '07_fastapi.md', '08_flask.md', '1.bin', '1.json', 'compose_readme.bat', 'coupling_vs_cohesion.svg', 'f.txt', 'gitflow.svg', 'graph_for_dfs.jpg', 'pycallgraph3.png', 'readme.md']
+f .txt ('c:\\', 'Works', 'amaargiru', 'pycore', 'f.txt')
+```
+
+
+
+### Простейшие вычисления — Sum, Count, Min, Max
+
+
+
+```
+a: list[int] = [1, 2, 3, 4, 5, 2, 2]s = sum(a)print(s)c = a.count(2) # Вернет количество вхожденийprint(c)mn = min(a)print(mn)mx = max(a)print(mx)
+```
+
+
+
+```
+19
+3
+1
+5
+```
+
+
+
+Присмотритесь к [встроенным функциям](https://docs.python.org/3/library/functions.html), там есть еще кое-что, касающееся элементарной математики.
+
+
+
+### Базовая математика
+
+
+
+```
+from math import pia: float = pi ** 2 # Or pow(pi, 2)print(f"Power: {a}")b: float = round(pi, 2)print(f"Round: {b}")c: int = round(256, -2)print(f"Int round: {c}")d: float = abs(-pi)print(f"Abs: {d}")e: float = abs(10+10j) # Or e: float = abs(complex(real=10, imag=10))print(f"Complex abs: {e}")
+```
+
+
+
+```
+Power: 9.869604401089358
+Round: 3.14
+Int round: 300
+Abs: 3.141592653589793
+Complex abs: 14.142135623730951
+```
+
+
+
+### Побитовые операции
+
+
+
+```
+a: int = 0b01010101b: int = 0b10101010print(f"And: 0b{a&b:08b}")print(f"Or: 0b{a|b:08b}")print(f"Xor: 0b{a^b:08b}")print(f"Left shift: 0b{a << 4:08b}")print(f"Right shift: 0b{b >> 4:08b}")print(f"Not: 0b{~a:08b}")
+```
+
+
+
+```
+And: 0b00000000
+Or: 0b11111111
+Xor: 0b11111111
+Left shift: 0b10101010000
+Right shift: 0b00001010
+Not: 0b-1010110
+```
+
+
+
+### Подсчет битов
+
+
+
+```
+a: int = 4242print(f"{a} in binary format: 0b{a:b}")c = a.bit_count() # Returns the number of ones in the binary representation of the absolute value of the integerprint(f"Bit count: {c}")
+```
+
+
+
+```
+4242 in binary format: 0b1000010010010
+Bit count: 4
+```
+
+
+
+### Fractions
+
+
+
+```
+from fractions import Fractionf = Fraction("0.2").as_integer_ratio()print(f)
+```
+
+
+
+```
+(1, 5)
+```
+
+
+
+### Евклидово расстояние между двумя точками
+
+
+
+```
+import mathp1 = (0.22, 1, 12)p2 = (-0.12, 3, 7)print(math.dist(p1, p2))
+```
+
+
+
+```
+5.39588732276722
+```
+
+
+
+### NumPy
+
+
+
+Мини-язык для манипулирования массивами. На удачных сценариях работает в сотни раз быстрее встроенных функций. Еще более быстрая альтернатива работает на GPU, называется [CuPy](https://github.com/cupy/cupy) и опять-таки [обещает](https://medium.com/rapids-ai/single-gpu-cupy-speedups-ea99cbbb0cbb) стократный прирост производительности, только уже по сравнению с NumPy. Так что если вам нужен какой-нибудь быстрый [FFT](https://en.wikipedia.org/wiki/Fast_Fourier_transform) или еще какой числогрыз, то вы знаете, что делать. Если вы дружите с английским, то изучайте [официальный мануал](https://numpy.org/doc/stable/), если нет — на «Хабре» есть [перевод](https://habr.com/ru/post/352678/) (как всегда, читайте комментарии, там немало полезного).
+
+
+
+Небольшое отступление.
+
+
+
+Во-первых, тут мы переходим границу между встроенной функциональность языка и внешними библиотеками. Надо понимать, что успех Python во многом основан именно на богатстве его экосистемы (хотя, впрочем, то же самое можно сказать и про JavaScript, и про C#); сам язык предоставляет богатую, но всё же ограниченную функциональность, в то время как функционал внешних библиотек практически безграничен; это как бесконечно разнообразные кубики Лего. Соответственно, очень часто для решения задачи не нужно реализовывать алгоритм с нуля на чистом Python'е, достаточно подобрать нужную библиотеку.
+
+
+
+Во-вторых, популярность разных библиотек Python (в том числе и конкурирующих) сильно разнится. Например, NumPy — очень популярная библиотека, но в мире существуют буквально миллионы Python-разработчиков, которые _никогда_ не работали с NumPy, просто в силу своего круга функциональных обязанностей.
+
+
+
+Для начинающего разработчика это представляет собой довольно нешуточную проблему — как конкретно двигаться вперед, какие библиотеки изучать, ведь знания чистого Python, как правило, недостаточно для формирования актуального резюме.
+
+
+
+Дам вам небольшой совет. Ежегодно компания JetBrains (делающая среди прочего очень классную IDE PyCharm) проводит всемирный опрос Python-разработчиков, а потом выкладывает полученные результаты в виде так называемого [Python Developers Survey Results](https://lp.jetbrains.com/python-developers-survey-2021/). Например, если вы почитаете результаты последнего исследования, то найдете там довольно чёткие ориентиры: скажем, в разделе «Data science frameworks and libraries» в топе находятся NumPy, Pandas (рассмотрен ниже) и Matplotlib; в тестировании с большим отрывом лидирует pytest (смотри ниже), в других областях вперед вырываются Flask (Django на втором месте с крошечным отрывом), SQLAlchemy (vs Django ORM) и PostgreSQL (vs SQLite), про них мы тоже еще поговорим. Так что в целом, общее направление развития определить можно.
+
+
+
+Однако, вернемся к NumPy. Не забывайте, что в основе NumPy лежат массивы, а все данные в массиве должны быть одинакового типа (просто на случай, если вы уже познали пр-р-рел-л-лесть списков Python). Создание массивов:
+
+
+
+```
+import numpy as npa1 = np.array([1, 2, 3, 4, 5], float) # Получение массива из спискаprint(a1[0:2])a2 = np.zeros(5) # Массив, заполненный нулямиprint(a2)a3 = np.arange(0, 6, 1) # Использование диапазона, np.arange(from_inclusive, to_exclusive, step_size)print(a3)a4 = np.random.randint(6, size=10) # Создание массива, содержащего случайные значения, np.random.randint(low_inclusive, high_exclusive=None, size=None, dtype=int)print(a4)a5 = np.random.randint(6, size=(2, 5)) # Создание многомерного массива, содержащего случайные значенияprint(a5)print(a5.shape) # Число строк и столбцов в массивеprint(a5.dtype) # Тип переменныхprint(1 in a5) # Проверка наличия элемента
+```
+
+
+
+```
+[1. 2.]
+[0. 0. 0. 0. 0.]
+[0 1 2 3 4 5]
+[2 2 4 0 0 0 0 4 0 5]
+[[1 0 3 5 0]
+ [3 1 4 2 2]]
+(2, 5)
+int32
+True
+```
+
+
+
+Базовые математические операции ([полный список](https://numpy.org/doc/stable/reference/routines.math.html)):
+
+
+
+```
+import numpy as npa1 = np.array([1, 2, 3, 4, 5])a2 = np.array([6, 7, 8, 9, 10])a3 = a1 + 1print(a3)a4 = a1 + a2print(a4)a5 = a1 ** 3print(a5)a6 = a1 ** a2print(a6)
+```
+
+
+
+```
+[2 3 4 5 6]
+[ 7 9 11 13 15]
+[ 1 8 27 64 125]
+[ 1 128 6561 262144 9765625]
+```
+
+
+
+Вообще, можно сказать, что быстрые математические операции над многомерными массивами — это главная «фишка» NumPy. Вы просто говорите: возьми такие-то массивы и проделай над ними такую-то операцию. Далее все эти данные «проваливаются» в высокоскоростное ядро NumPy, где к ним уже можно применить всю мощь вашего процессора, которая раньше была вам недоступна (ну, или доступна не полностью) из-за ограничений Python-интерпретатора. Так что, если вы пытаетесь в цикле итерировать массив NumPy, по факту передавая данные на нижний уровень небольшими порциями (например, объектами row), то имейте в виду, что тем самым используете возможности NumPy недостаточно эффективно; попробуйте решить задачу без итерирования.
+
+
+
+### Sum, Min, Max
+
+
+
+```
+import numpy as npa1 = np.random.randint(6, size=(2, 10)) # NumPy поддерживает несколько десятков видов распределений, например, Пуассона и Стьюдентаprint(a1)s = np.sum(a1) # Сумма всех элементовprint(s)mn = a1.min(axis=0) # Наименьшие числа в каждом столбцеprint(mn)mx = a1.max(axis=1) # Наибольшие числа в каждой строкеprint(mx)amin = a1.argmin(axis=0) # Индексы минимальных элементов в каждом столбцеprint(amin)amax = a1.argmax(axis=1) # Индексы максимальных элементов в каждой строкеprint(amax)uniq = np.unique(a1) # Извлечение уникальных элементовprint(uniq)
+```
+
+
+
+```
+[[3 0 4 1 0 5 4 0 1 3]
+ [0 3 4 0 0 1 4 0 5 4]]
+42
+[0 0 4 0 0 1 4 0 1 3]
+[5 5]
+[1 0 0 1 0 1 0 0 0 0]
+[5 8]
+[0 1 3 4 5]
+```
+
+
+
+В качестве домашнего задания попробуйте самостоятельно применить prod(), mean(), var(), std(), median(), cov() и corrcoef().
+
+
+
+Форматирование массивов:
+
+
+
+```
+import numpy as npa = np.random.randint(6, size=(3, 5))print(a)a1 = a.reshape((5, 3)) # Форматирование. Если есть возможность, создается новый view на те же самые данныеprint(a1)a.shape = (5, 3) # Форматирование in-placeprint(a)print(a.shape)a = a[:, :, np.newaxis] # Увеличение размерности массива с 2 до 3print(a)print(a.shape)a = a.flatten() # Конвертация в одномерный массивprint(a)print(a.shape)
+```
+
+
+
+```
+[[5 5 5 1 1]
+ [0 2 0 5 5]
+ [0 2 5 4 5]]
+[[5 5 5]
+ [1 1 0]
+ [2 0 5]
+ [5 0 2]
+ [5 4 5]]
+[[5 5 5]
+ [1 1 0]
+ [2 0 5]
+ [5 0 2]
+ [5 4 5]]
+(5, 3)
+[[[5]
+ [5]
+ [5]]
+
+ [[1]
+ [1]
+ [0]]
+
+ [[2]
+ [0]
+ [5]]
+
+ [[5]
+ [0]
+ [2]]
+
+ [[5]
+ [4]
+ [5]]]
+(5, 3, 1)
+[5 5 5 1 1 0 2 0 5 5 0 2 5 4 5]
+(15,)
+```
+
+
+
+Копирование массивов:
+
+
+
+```
+import numpy as npimport copya = np.random.randint(10, size=(4, 4))print(a)# Неглубокая (shallow) копияa1 = np.copy(a)# Глубокая (deep) копияa2 = copy.deepcopy(a)# Копирование ссылкиa3 = aa[0, 0] = 10print(a[0, 0] == a1[0, 0])print(a[0, 0] == a2[0, 0])print(a[0, 0] == a3[0, 0])
+```
+
+
+
+```
+[[5 3 1 4]
+ [0 8 7 0]
+ [1 7 4 7]
+ [5 3 5 2]]
+False
+False
+True
+```
+
+
+
+NumPy очень мощный инструмент, не зря же он стоит на первом месте в списке «Data science frameworks and libraries» обзора, который мы упоминали чуть выше. Но углубляться в эту тему очень уж глубоко в рамках нашего достаточно поверхностного очерка, пожалуй, не стоит; вряд ли прямо сейчас вам кровь из носу нужно освоить скалярное, тензорное и внешнее произведение матриц или познать (вспомнить?) специфику линейной алгебры. Думаю, даже если мы сейчас начнем описывать транспонирование или выбор оси, по которой будет произведена конкатенация массивов, то это уже будет, что называется, «не в коня корм».
+К тому же, изучая тонкости употребления NumPy, начинает появляться соблазн упоминания SciPy, предоставляющего еще более широкий функционал, а после первого "import scipy" у нас начнётся уже полное непотребство. Давайте пока пройдем мимо этой кроличьей норы, для первого знакомства она слишком глубока.
+
+
+
+Единственное, что еще можно освоить в конце ознакомительного курса NumPy — взаимодействие с внешним миром. Изучите для начала load/save/savez (бинарники) и loadtxt/savetxt (человекочитаемый формат).
+
+
+
+### Pandas
+
+
+
+Библиотека обработки и анализа данных. Работа с данными строится поверх библиотеки NumPy.
+В первом, грубом приближении pandas можно воспринимать как связку «Excel + VisualBasic-скрипты», только более гибкую и удобную. Библиотека создает своеобразный мостик между профессиями Python-программиста, дата-сайентиста и аналитика, позволяя сосредоточиться в большей степени именно на очистке и анализе данных, на читабельности отчетов, а не на программировании. Pandas также поддерживает широкий спектр «красивостей» при выводе информации, позволяя, например, добавлять в выводимые данные градиентную подсветку (heatmap) или визуализировать отклонение от среднего (bar chart).
+
+
+
+Для того чтобы как следует «распробовать» pandas, по-хорошему надо загрузить какой-нибудь развесистый набор данных, но мы, пожалуй, не будем погружаться в глубины глубин, просто поиграем небольшим самодельным датасетом.
+
+
+
+```
+import pandas as pds = pd.Series([0, 1, 4, 7, 8, 10, 12])print(s)print(s[2])
+```
+
+
+
+```
+0 0
+1 1
+2 4
+3 7
+4 8
+5 10
+6 12
+dtype: int64
+4
+```
+
+
+
+Series — базовая структура данных pandas. Вы можете воспринимать её как упорядоченный словарь или как строку Excel, смотря по тому, какая аналогия вам ближе.
+
+
+
+```
+import pandas as pds = pd.Series([0, 1, 4, 7, 8, 10, 12], index=["a", "b", "c", "d", "x", "y", "z"]) # Индексы Series можно задавать вручнуюprint(s)print(s["x"])print(s[["x", "y", "z"]]) # Выборкаprint(s[s > 5]) # Фильтрацияprint(s.max()) # Математика, примерно как в NumPyprint(s.sum())
+```
+
+
+
+```
+a 0
+b 1
+c 4
+d 7
+x 8
+y 10
+z 12
+dtype: int64
+8
+x 8
+y 10
+z 12
+dtype: int64
+d 7
+x 8
+y 10
+z 12
+dtype: int64
+12
+42
+```
+
+
+
+При объединении нескольких Series получается DataFrame, вторая базовая структура данных pandas, которую в первом приближении можно рассматривать как лист Excel.
+
+
+
+```
+import pandas as pdfrom pandas import DataFrames1 = pd.Series([0, 1, 4, 7, 8, 10, 12])s2 = pd.Series([0, 100, 200, 300, 600, 900, 1200])df = pd.DataFrame([s1, s2])print(df)print(df[1])print(df[2][0])print(df.iloc[0][2:4])
+```
+
+
+
+```
+ 0 1 2 3 4 5 6
+0 0 1 4 7 8 10 12
+1 0 100 200 300 600 900 1200
+0 1
+1 100
+Name: 1, dtype: int64
+4
+2 4
+3 7
+Name: 0, dtype: int64
+```
+
+
+
+Давайте сделаем что-то более похожее на реальный анализ данных. При помощи формулы [ИМТ](https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81_%D0%BC%D0%B0%D1%81%D1%81%D1%8B_%D1%82%D0%B5%D0%BB%D0%B0) выясним, кто из знаменитостей не следит за собой и обзавелся лишним весом:
+
+
+
+```
+from pandas import DataFrameimport matplotlib.pyplot as pltdef bmi(row): return row["weight"] / row["height"] ** 2if __name__ == '__main__': celebs: dict = {"Britney Spears": {"height": 1.63, "weight": 57}, "Melanie Griffith": {"height": 1.73, "weight": 63}, "Kylie Minogue": {"height": 1.52, "weight": 46}, "Hulk Hogan": {"height": 1.98, "weight": 137}} df = DataFrame(celebs) # Создаем DataFrame df.loc["bmi"] = df.apply(lambda row: bmi(row), axis=0) # Добавлем новую строку с ИМТ df = df.sort_values(by="bmi", ascending=True, axis=1) # Сортируем print(df) df.loc["bmi"].plot.bar() # Визуализация plt.show()
+```
+
+
+
+```
+ Kylie Minogue Melanie Griffith Britney Spears Hulk Hogan
+height 1.520000 1.730000 1.630000 1.980000
+weight 46.000000 63.000000 57.000000 137.000000
+bmi 19.909972 21.049818 21.453574 34.945414
+```
+
+
+
+![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/501/fe2/953/501fe29532c00ff9b3e59fd7754455e7.png)
+
+
+
+На самом деле Халк, конечно, не толстяк, а профессиональный спортсмен, к которым формула ИМТ малоприменима, но крошка Кайли действительно вырывается вперед, даже с учетом своего небольшого роста.
+
+
+
+### Matplotlib/Seaborn
+
+
+
+Библиотеку визуализации matplotlib мы уже слегка задействовали в примере выше. Прямо здесь и прямо сейчас глубоко погружаться в разбор возможностей matplotlib/seaborn, наверное, особого смысла не имеет; все вы видели примеры иллюстраций в научной и бизнес-литературе и, разумеется, все эти графики и иллюстрации можно повторить при помощи рассматриваемых библиотек.
+
+
+
+Давайте просто для затравки нарисуем пару симпатичных визуализаций, чтобы наглядно показать полезность качественного оформления результатов проделанной работы.
+
+
+
+Тепловая карта (heatmap), наглядно показывающая достижения отдельных членов команды:
+
+
+
+```
+from random import randrangeimport numpy as npimport matplotlib.pyplot as pltimport uuidtargets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig, ax = plt.subplots()im = ax.imshow(harvest)ax.set_xticks(np.arange(len(robots)), labels=robots)ax.set_yticks(np.arange(len(targets)), labels=targets)plt.setp(ax.get_xticklabels(), rotation=60, ha="right", rotation_mode="anchor")for i in range(len(targets)): for j in range(len(robots)): text = ax.text(j, i, harvest[i, j], ha="center", va="center", color="w")ax.set_title("Targets destroyed")fig.tight_layout()plt.rcParams['figure.figsize'] = [4, 4]plt.rcParams['figure.dpi'] = 200plt.show()
+```
+
+
+
+![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/eba/76c/412/eba76c412c65b8139db64f7a7aebfc19.png)
+
+
+
+Аналогичная тепловая карта, визуализированная при помощи seaborn:
+
+
+
+```
+from random import randrangeimport numpy as npimport matplotlib.pyplot as pltimport uuidimport seaborn as snssns.set_theme()targets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig, ax = plt.subplots()im = ax.imshow(harvest)ax.set_title("Targets destroyed")plt.rcParams['figure.figsize'] = [4, 4]sns.heatmap(harvest, annot=True, fmt="d", linewidths=.5, ax=ax, xticklabels=robots, yticklabels=targets)plt.setp(ax.get_xticklabels(), rotation=60, ha="right", rotation_mode="anchor")plt.xticks(rotation=60)plt.show()
+```
+
+
+
+![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/fce/04a/0d5/fce04a0d5530e395ba123a3d045d9f2a.png)
+
+
+
+А вот так будет выглядеть «Доска почёта» при отрисовке в 3D:
+
+
+
+```
+import uuidfrom random import randrangeimport matplotlib.cm as cmimport matplotlib.colors as colorsimport matplotlib.pyplot as pltimport numpy as nptargets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig = plt.figure(figsize=(5, 5))ax = fig.add_subplot(projection='3d')ax.set_xticks(np.arange(len(robots)), labels=robots)ax.set_yticks(np.arange(len(targets)), labels=targets)plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")plt.setp(ax.get_yticklabels(), ha="left", rotation_mode="anchor")ax.set_title("Our team")xx, yy = np.meshgrid(range(len(targets)), range(len(robots)))x1d, y1d = xx.ravel(), yy.ravel()harvest1d = harvest.ravel()# Setup color schemeoffset = harvest1d + np.abs(harvest1d.min())fracs = offset.astype(float) / offset.max()norm = colors.Normalize(fracs.min(), fracs.max())colors = cm.jet(norm(fracs))ax.bar3d(x1d, y1d, np.zeros_like(x1d + y1d), 0.7, 0.7, harvest1d, color=colors)plt.show()
+```
+
+
+
+![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/a70/52e/4eb/a7052e4eb1eba178f4ef91a78351ff7e.png)
+
+
+
+### Нейронные сети
+
+
+
+Углубиться достаточно глубоко в тематику глубокого обучения в рамках нашего несколько неглубокого формата, конечно, не получится (каким-нибудь томом «Введение в Deep Learning» вполне можно нанести себе существенные травмы, если неудачно уронить со стола), так что скользнём буквально по верхушкам, рассмотрев основные понятия.
+
+
+
+Глубинная нейронная сеть (deep neural network) — это, формально выражаясь, _многослойная_ искусственная нейронная сеть, использующая алгоритмы машинного обучения для моделирования высокоуровневых абстракций с применением нелинейных преобразований. В ходу есть еще такие термины, как «поверхностное машинное обучение», «слабый ИИ», «сильный ИИ»; существует классификация понятий «глубокое обучение», «машинное обучение» и «искусственный интеллект» (вот, например, [вариант](https://learn.microsoft.com/en-us/azure/machine-learning/concept-deep-learning-vs-machine-learning) от Microsoft, такую же структуру-матрёшку демонстрирует Франсуа Шолле в своей книге «Глубокое обучение на Python»), но сейчас, по крайней мере при неформальном общении, всё чаще ставится знак равенства между «машинным обучением» и «искусственным интеллектом»; причём в работе, как правило, используется «машинное обучение», а при подготовке презентаций — «искусственный интеллект» :) Дело, по всей видимости, в том, что глубокое обучение стало самой многообещающей и динамично развивающейся областью машинного обучения, а искусственным интеллектом уже давно именовали всю эту область знаний как популяризаторы науки, так и журналисты.
+
+
+
+В общем, при первоначальном знакомстве с TensorFlow или PyTorch можете смело всем говорить, что занимаетесь AI, а если захотите углубиться — уж терминологией-то овладеете.
+Что касается сути работы нейронных сетей, то здесь можно выделить следующие основные понятия:
+
+
+
+**Датасет** — маркированные данные, используемые для обучения сети и для последующей проверки качества этого обучения. Вариант простого датасета — коллекция изображений одинакового размера с рукописными цифрами и буквами, где про каждое изображение точно известно, какай именно символ в нём содержится; такой датасет нужен для разработки систем распознавания рукописного текста (handwritten text recognition, HTR). Создание качественных датасетов — большая, тяжелая работа, поэтому сейчас идет активная работа по созданию моделей, способных работать с большими объёмами _немаркированных_ данных. Как правило, датасет разбивается на две части — для обучения сети и для проверки качества проведенного обучения.
+
+
+
+**Искусственная нейронная сеть** — программный или аппаратный аналог биологической нейронной сети; в самом простом варианте, в сети прямого распространения, это последовательно соединенные слои нейронов. Первые реализации нейронных сетей получили практическое воплощение еще в 60-х годах XX века, хотя существенный прогресс и практическое внедрение приходятся примерно на последние лет пятнадцать.
+
+
+
+**Архитектура** искусственной нейронной сети — определяет общие принципы её построения, вот [здесь](https://www.asimovinstitute.org/neural-network-zoo/) есть хорошее введение в тему, наглядно показано, чем, например, GAN отличается от LSTM.
+
+
+
+**Обучение** нейронной сети — если в двух словах, то это нахождение коэффициентов связи между нейронами. Применительно к машинному обучению заменяет процесс собственно программирования. Когда мы говорим о DALL-E 2, способном создавать изображения по текстовым описаниям или об AlphaGo, обыгрывающем профессиональных игроков в го — мы говорим в первую очередь об обученных нейронных сетях, создатели которых проявили бездну изобретательности, чтобы все коэффициенты связи были на своём месте. Дообучение — частичная корректировка коэффициентов связи между нейронами при модификации старого или добавлении нового функционала.
+
+
+
+Зачем нужно машинное обучение? Сильный ИИ, который будет (скрестим пальцы) решать все наши проблемы в режиме реального времени, еще за горизонтом, а прямо сейчас нейронные сети решают задачи, которые не по зубам классическим алгоритмам. Можно ли при помощи «обычного» программирования решить, например, задачу распознавания отсканированного текста или перевода с одного языка на другой? Да, можно, и такие небезуспешные попытки неоднократно предпринимались. Но, учитывая прорыв в развитии математической базы машинного обучения, наметившийся в последние 10-15 лет, помноженный на гигантский прирост производительности даже обычных повседневных вычислительных устройств, вроде смартфонов, фактически и распознавание, и перевод сейчас реализуют только при помощи машинного обучения. Для некоторых же классов задач, таких как распознавание изображений и видео с последующей классификацией объектов или беспилотная транспортировка, решения на базе классических алгоритмов никогда не заходили дальше вялотекущих концептов.
+
+
+
+Мало-помалу машинное обучение делает нашу жизнь лучше. Со временем, надеюсь, каждый из нас сможет воспользоваться плодами работы искусственного интеллекта, хоть мы и величаем его «слабым». И речь идет не только о громких проектах, вроде автопилота «Теслы» (хотя и это крайне немаловажно), но в первую очередь о постоянном сканировании эксабайтных потоков информации, порождаемых современной цифровой цивилизацией — видео с уличных камер, сканов МРТ и КТ, телеметрии с фитнес-браслетов, отчетов об исследовании лекарственных средств и пищевых добавок. И если всё это пройдет перед, может быть, пока не очень умными, но зато неустанными глазами предварительно должным образом обученной нейронной сети, то кратно уменьшится неверных диагнозов, людей, умерших от инсультов или замерзших на улицах, а также лекарств с тяжелыми побочными эффектами.
+
+
+
+Но помните, однако — даже если вы полны энтузиазма и готовы хоть прямо сейчас нырнуть во все эти модные, интересные и актуальные сущности вроде бигдаты или ИИ — чем дальше вы будете углубляться в эту тематику, тем меньше в вас будет программиста, тем больше исследователя, учёного, математика. Где-то в самом конце этого длинного-предлинного коридора сидит вовсе не Нео из «Матрицы», а эдакий себе [Григорий Перельман](https://en.wikipedia.org/wiki/Grigori_Perelman), уставший человек с ручкой и листом бумаги, иногда этой ручкой на этой бумаге пишущий вещи, которые потом приходится два года разжевывать лучшим математикам мира. Крепко подумайте, как глубоко вы хотите и можете зайти, взвесьте свои силы.
+
+
+
+## 3. Потоки данных
+
+
+
+![Data Flows](https://habrastorage.org/r/w1560/getpro/habr/post_images/4b1/afd/231/4b1afd231fcd5e11f79e1347cff43e7e.png)
+
+
+
+### Itertools
+
+
+
+Методы модуля itertools возвращают _итераторы_.
+
+
+
+Итератор — механизм поэлементного обхода данных, который использует метод next() для получения следующего значения последовательности. Подробнее создание итераторов будет рассмотрено ниже, в разделе «ООП / Утиная типизация». В «нормальные» данные итераторы перегоняются при помощи for, next или list().
+
+
+
+Itertools содержит множество готовых итераторов, которые могут быть бесконечными (порождаются при помощи count, cycle или repeat), конечными (accumulate, chain, takewhile и другие) и комбинаторными (product, combinations, combinations_with_replacement, permutations). Лучше изучить их все, хотя бы поверхностно, потому что даже относительно редко употребляемый метод, например, какой-нибудь zip_longest(), иногда весьма и весьма пригождается, идеально ложась на поставленную задачу.
+
+
+
+Пример работы с бесконечными итераторами:
+
+
+
+```
+from itertools import count, repeat, cycle# Итератор, возвращающий равномерно распределенные значенияi1 = count(start=0, step=.1)print(next(i1))print(next(i1))print(next(i1))# Итератор, циклично и бесконечно возвращающий элементы итерируемого объектаi2 = cycle([1, 2])print(next(i2))print(next(i2))print(next(i2))# Итератор, возвращающий один и тот же объект бесконечно, если не указано значение аргумента timesi3 = repeat("Wow!", times=3)print(list(i3))
+```
+
+
+
+```
+0
+0.1
+0.2
+1
+2
+1
+['Wow!', 'Wow!', 'Wow!']
+```
+
+
+
+Применение некоторых конечных итераторов:
+
+
+
+```
+from itertools import accumulate, chain, compress, dropwhile, takewhile, pairwiseimport operator# Итератор, возвращающий накопленный результат выполнения указанной функции (по умолчанию — сложение)i1 = accumulate([1, 2, 3, 4])i2 = accumulate([1, 2, 3, 4], initial=10)print(list(i1), list(i2))i3 = accumulate([ -3, -2, -1, 1, 2, 3, 4], operator.mul)print(list(i3))# Можно использовать свою функциюdef myfunc(accumulated, current): return accumulated + 2 * currenti4 = accumulate([1, 2, 3, 4], func=myfunc)print(list(i4))# Можно использовать лямбду (подробнее рассмотрены ниже)i5 = accumulate([1, 2, 3, 4], lambda accumulated, current: accumulated + 2 * current)print(list(i5))# Итератор, возвращающий только те элементы входной последовательности,# которые имеют соответствующий элемент, равный True или 1 в последовательности selectorsi6 = compress("ABCDEF", [1, 1, 1, 0, 0, 1])print(list(i6))# Итератор, отбрасывающий элементы входной последовательности, если результат выполнения функции равен True.# Как только предикат становится False, то отбрасывание прекращается (предикат больше не применяется)i7 = dropwhile(lambda x: x<5, [1, 4, 6, 4, 1, 1, 1, 0])print(list(i7))# takewhile, в отличие от dropwhile, наоборот, возвращает элементы входной последовательности,# если результат выполнения функции равен Truei8 = takewhile(lambda x: x<5, [1, 4, 6, 0, 4, 1, 2, 1])print(list(i8))# Итератор, формирующий из нескольких входных последовательностей одну общуюi2 = chain(["A", "B", "C"],["D", "E", "F"],["G", "H", "I"])print(list(i2))# Кстати, такой же трюк можно провернуть при помощи обычной sum(), задав ей начальный параметр [] (т. е. пустой список)a = sum([["A", "B", "C"],["D", "E", "F"],["G", "H", "I"]], [])print(a)# Возвращает элементы входной коллекции попарноi6 = pairwise([1, 2, 3, 4, 5])print(list(i6))
+```
+
+
+
+```
+[1, 3, 6, 10] [10, 11, 13, 16, 20]
+[-3, 6, -6, -6, -12, -36, -144]
+[1, 5, 11, 19]
+[1, 5, 11, 19]
+['A', 'B', 'C', 'F']
+[6, 4, 1, 1, 1, 0]
+[1, 4]
+['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
+['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
+[(1, 2), (2, 3), (3, 4), (4, 5)]
+```
+
+
+
+Комбинаторика
+
+
+
+```
+from itertools import product, combinations, combinations_with_replacement, permutations# Создает множество, содержащее все упорядоченные пары элементов из входных множествa = product("abc", "xyz")print(list(a))b = product([0, 1], repeat=3)print(list(b))# Возвращает подпоследовательности длины r из элементов входного итерируемого объекта, повторяющиеся элементы не допускаютсяc = combinations("abc", r=2)print(list(c))# Выдает перестановки элементов итерируемого объектаd = permutations("abc", r=2)print(list(d))# Возвращает подпоследовательности длины r из элементов входного итерируемого объекта, повторяющиеся элементы допустимыe = combinations_with_replacement("abc", r=2)print(list(e))
+```
+
+
+
+```
+[('a', 'x'), ('a', 'y'), ('a', 'z'), ('b', 'x'), ('b', 'y'), ('b', 'z'), ('c', 'x'), ('c', 'y'), ('c', 'z')]
+[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
+[('a', 'b'), ('a', 'c'), ('b', 'c')]
+[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')]
+[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'), ('c', 'c')]
+```
+
+
+
+### Enumerate
+
+
+
+Иногда, при переборе объектов в цикле for, нужно получить не только сам объект, но и его порядковый номер. Разумеется, это можно сделать, создав дополнительную переменную, которая будет инкрементироваться на каждом шаге цикла. Однако, гораздо удобнее это делать с помощью итератора enumerate, введенным в [PEP-279](https://peps.python.org/pep-0279/). Enumerate — синтаксический сахар («introduces… to simplify a commonly used looping idiom»), позволяющий проще и нагляднее работать с объектами, поддерживающими итерацию. Метод __next__() enumerate возвращает кортеж, содержащий значение индекса и соответствующее этому индексу значение.
+
+
+
+В документации работа enumerate упрощенно объясняется через генератор:
+
+
+
+```
+def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1
+```
+
+
+
+На самом деле enumerate — не генератор, а итератор:
+
+
+
+```
+import collectionsimport typese = enumerate("abcdef")print(isinstance(e, enumerate))print(isinstance(e, collections.Iterable))print(isinstance(e, collections.Iterator))print(isinstance(e, types.GeneratorType))
+```
+
+
+
+```
+True
+True
+True
+False
+```
+
+
+
+Enumerate реализован не на Python, а на C, и в его [исходном коде](https://github.com/python/cpython/blob/master/Objects/enumobject.c#L289), разумеется, нет ключевого слова yield.
+
+
+
+Примеры использования enumerate:
+
+
+
+```
+values = ["a", "b", "c", "d"]for count, value in enumerate(values): print(count, value)print("\n")for count, value in enumerate(values, start=10 ): print(count, value)
+```
+
+
+
+```
+0 a
+1 b
+2 c
+3 d
+
+10 a
+11 b
+12 c
+13 d
+```
+
+
+
+### Генератор (generator)
+
+
+
+Любая функция, содержащая ключевое слово _yield_, вернет генератор. Генератор не хранит в памяти все необходимые элементы, а просто содержит метод для вычисления очередного элемента; результат может создаваться на основе математического алгоритма или брать элементы из другого источника данных (коллекция, файл, сетевое подключение и т. д.), при необходимости модифицируя их.
+
+
+
+Пройти генератор в цикле можно только один раз, на каждом шаге возможно вычислить только следующий элемент, но не предыдущий. Элемент генератора нельзя извлечь по индексу, будет выброшена ошибка, т. к. генератор не поддерживает метод __getitem__().
+
+
+
+Бесконечный генератор:
+
+
+
+```
+def count(start, step): current = start while True: yield current current += stepc = count(100, 10)print(next(c))print(next(c))print(next(c))
+```
+
+
+
+```
+100
+110
+120
+```
+
+
+
+Конечный генератор.
+Также, как и конечный итератор, конечный генератор можно превратить в список при помощи list() (вы можете попробовать превратить в list и бесконечный генератор, но процесс рискует несколько затянуться :):
+
+
+
+```
+def count(start, stop, step): current = start while current <= stop: yield current current += stepc = count(100, 200, 10)print(next(c))print(next(c))print(next(c))print(list(c))
+```
+
+
+
+```
+100
+110
+120
+[130, 140, 150, 160, 170, 180, 190, 200]
+```
+
+
+
+Следует разделять итераторы и генераторы. Итератор — объект, который использует метод __next__() для получения следующего значения последовательности. Генератор — функция, которая позволяет отложено создавать результат при итерации.
+
+
+
+#### Объявление генератора
+
+
+
+Объявить генератор можно несколькими методами. Первый метод — объявить функцию с yield, как было показано выше.
+Второй метод — использовать _генераторное выражение_ (generator expression):
+
+
+
+```
+r = range(1, 11)squares = (n**2 for n in r)print(list(squares))
+```
+
+
+
+```
+[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
+```
+
+
+
+Можно объединить генераторы или делегировать часть функционала генератора другому генератору при помощи конструкции _yield from_:
+
+
+
+```
+def subg(): yield 'World'def generator(): yield 'Hello' yield from subg() yield '!'for i in generator(): print(i, end = ' ')
+```
+
+
+
+```
+Hello World !
+```
+
+
+
+До широкого распространения asyncio конструкция yield from использовалась для создания [корутин на базе генераторов](https://docs.python.org/3.7/library/asyncio-task.html#asyncio.coroutine).
+
+
+
+### Декораторы
+
+
+
+Что такое декораторы?
+
+
+
+Декоратор в широком смысле – паттерн проектирования, когда один объект изменяет поведение другого. Декораторы — это, по сути, своеобразные «обёртки», которые дают нам возможность делать что-либо до или после того, что сделает декорируемая функция, не изменяя её. Можно сказать, что декоратор является просто синтаксическим сахаром для конструкции вида:
+
+
+
+```
+my_function = my_decorator(my_function)
+```
+
+
+
+```
+def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrappeddef makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped# Разумеется, при последовательном применении нескольких декораторов играет роль порядок декорирования.@makebold@makeitalicdef hello(): return "Hello, world!"print(hello())
+```
+
+
+
+```
+<b><i>Hello, world!</i></b>
+```
+
+
+
+Декоратор, подсчитывающий время работы оборачиваемой функции:
+
+
+
+```
+import timedef perf_counter(function): def counted(*args): start_time = time.perf_counter_ns() res = function(*args) print(f"{time.perf_counter_ns() - start_time} ns") return res return counted@perf_counterdef slow_sum(x, y): time.sleep(1) return x + yprint(slow_sum(1, 2))
+```
+
+
+
+```
+1002478400 ns
+3
+```
+
+
+
+### LRU Cache
+
+
+
+Декоратор, кеширующий значения, возвращаемые функцией. Все аргументы функции должны быть хэшируемы.
+
+
+
+```
+import functoolsdef recursion_sum(n): if n == 1: return n print(n, end=" ") return n + recursion_sum(n - 1)recursion_sum(5)print("\n")recursion_sum(9)print("\n")@functools.lru_cachedef recursion_sum2(n): if n == 1: return n print(n, end=" ") return n + recursion_sum2(n - 1)recursion_sum2(5)print("\n")recursion_sum2(9)
+```
+
+
+
+```
+5 4 3 2
+
+9 8 7 6 5 4 3 2
+
+5 4 3 2
+
+9 8 7 6
+
+45
+```
+
+
+
+Размер кеша по умолчанию 128 значений. Ограничение можно отменить при помощи 'maxsize=None'.
+
+
+
+Пока мы не ушли далеко от тема кеша, погуглите заодно модуль weakref и WeakValueDictionary, позволяющие организовать более гибкую работу с кешем.
+
+
+
+### Параметризованный декоратор
+
+
+
+В декоратор можно передать и позиционные, и именованные аргументы — args и kwargs соответственно. Синтаксис декораторов с аргументами немного отличается — декоратор с аргументами должен возвращать функцию, которая принимает функцию и возвращает другую функцию:
+
+
+
+```
+def text_wrapper(wrap_text): def wrapped(function): def wrapper(*args, **kwargs): result = function(*args, **kwargs) return f"{wrap_text}\n{result}\n{wrap_text}" return wrapper return wrapped@text_wrapper('============')def my_decorated_function(text): return textprint(my_decorated_function('Hello, world!'))
+```
+
+
+
+```
+============
+Hello, world!
+============
+```
+
+
+
+### Контекстный менеджер
+
+
+
+Код, размещенный внутри оператора with выполняется с особенностью: как до, так и после срабатывают события входа в блок with и выхода из него. Объект, который определяет логику событий, называется контекстным менеджером.
+
+
+
+На уровне класса события определены методами __enter__ и __exit__.
+__enter__ срабатывает в тот момент, когда ход исполнения программы переходит внутрь with. Метод может вернуть значение. Оно будет доступно расположенному внутри блока with коду.
+__exit__ срабатывает в момент выхода блока, в т.ч. и в случае исключения. В этом случае в метод будет передана тройка значений (exc_class, exc_instance, traceback).
+
+
+
+Самый распространённый контекстный менеджер – класс, порожденный функцией open. Он гарантирует, что файл будет закрыт даже в том случае, если внутри блока возникнет ошибка.
+
+
+
+Желательно побыстрее выходить из контекстного менеджера, освобождая контекст и ресурсы.
+
+
+
+```
+with open('file.txt') as f: data = f.read()process_data(data)
+```
+
+
+
+В примере выше мы вышли из блока with сразу же после прочтения файла. Обработка данных происходит в основном блоке программы.
+
+
+
+Контекстные менеджеры можно использовать для временной замены параметров, переменных окружения, транзакций БД.
+
+
+
+Напишем свой контекстный менеджер для подключения к БД SQLite:
+
+
+
+```
+import sqlite3class db_conn: def __init__(self, db_name): self.db_name = db_name # Открываем подключение к БД def __enter__(self): self.conn = sqlite3.connect(self.db_name) return self.conn # Закрываем подключение к БД def __exit__(self, exc_type, exc_value, exc_traceback): self.conn.close() if exc_value: raiseif __name__ == "__main__": db = "test_context_connect.db" with db_conn(db) as conn: cursor = conn.cursor()
+```
+
+
+
+### Контекстный менеджер на базе contextlib
+
+
+
+Перепишем наш контекстный менеджер для подключения к БД SQLite при помощи contextlib:
+
+
+
+```
+import sqlite3from contextlib import contextmanager# Схема конструирования следующая: всё, что написано до оператора yield - вызывается в рамках функции __enter__,# всё что после – в рамках __exit__.@contextmanagerdef db_conn(db_name): # Открываем подключение к БД conn = sqlite3.connect(db_name) yield conn # Закрываем подключение к БД conn.close()if __name__ == "__main__": db = "test_contextlib_connect.db" with db_conn(db) as conn: cursor = conn.cursor()
+```
+
+
+
+## 4. ООП
+
+
+
+![OOP](https://habrastorage.org/r/w1560/getpro/habr/post_images/7ec/81b/9a6/7ec81b9a69c879e5ff97b2f1a10ad093.png)
+
+
+
+### Классы и объекты
+
+
+
+Тут, конечно, было бы к месту кратенькое, минут на сорок, введеньице в тему классов и объектов, но в наш текущий формат такая мощная врезка не совсем укладывается. Объясню максимально просто, на доступных примерах из киновселенной «Чужих»:
+объект — это один конкретный ксеноморф;
+класс — это Королева ксеноморфов. Класс либо рожает ксеноморфа, либо может вступить в бой сам (@staticmethod);
+метапрограммирование — это такая Супер-Королева, размером с «Сулако», которая рожает других Королев;
+наследование — это ксеноморф из «Воскрешения», помните, миленький такой, взявший лучшее и от собственной генетической программы и от генов Рипли.
+
+
+
+Мы попробуем вернуться к теме объектов с чуть более серьезным настроением позже, в главе «Архитектура», но, вообще, в объектно-ориентированном программировании нет ничего особо сложного; просто до него лучше дойти, предварительно немного погрязнув в поддержке обычного процедурного подхода, когда зачастую стоит выбор — попробовать подлечить этот кусок кода или уже усыпить и переписать всё по новой? Когда-то давно, когда я писал относительно несложные программы на ассемблере для микроконтроллеров, то читая Страуструпа, слегка недоумевал — зачем всё это? Чтобы осознать потребность в обуви, надо походить босиком.
+
+
+
+### Магические методы
+
+
+
+Специальные (называемые также magic или dunder) методы класса — перегрузка, позволяющая классам определять собственное поведение по отношению к операторам языка.
+Магические они потому, что почти никогда не вызываются явно. Их вызывают встроенные функции или синтаксические конструкции. Например, функция len() вызывает метод __len__() переданного объекта. Метод __add__(self, other) вызывается автоматически при сложении оператором +.
+
+
+
+Примеры магических методы:
+
+
+
+__init__: конструктор класса
+__add__: сложение с другим объектом
+__eq__: проверка на равенство с другим объектом
+__cmp__: сравнение (больше, меньше, равно)
+__iter__: при подстановке объекта в цикл
+
+
+
+```
+print(dir(int), "\n")class A: # An empty class ...a = A()print(dir(a), "\n")print(repr(a), "\n")print(str(a))
+```
+
+
+
+```
+['__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_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
+
+['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
+
+<__main__.A object at 0x000001A1FFF3AF20>
+
+<__main__.A object at 0x000001A1FFF3AF20>
+```
+
+
+
+Особенностью метода __init__ является то, что он не должен ничего возвращать. При попытке возврата данных будет сгенерировано исключение.
+__repr__ (representation) возвращает более-менее машино-читаемое представление объекта, полезное для отладки.
+_Иногда_ repr может содержать достаточно информации для восстановления объекта.
+__str__ возвращает человеко-читаемое сообщение. Если __str__ не определён, то str использует repr.
+
+
+
+```
+class Person: # A simple class with init, repr and str methods def __init__(self, name: str): self.name: str = name def __repr__(self): return f"Person '{self.name}'" def __str__(self): return f"{self.name}" def say_hi(self): print("Hi, my name is", self.name)p = Person("Charlie")p.say_hi()print(repr(p))print(str(p))
+```
+
+
+
+```
+Hi, my name is Charlie
+Person 'Charlie'
+Charlie
+```
+
+
+
+### @property
+
+
+
+Декоратор [@property](https://docs.python.org/3/library/functions.html?highlight=property#property) используется для определения методов, доступных как поля. Таким образом операции чтения/записи поля можно обрамить дополнительной логикой, например, проверкой допустимых значений входного аргумента.
+
+
+
+```
+import mathclass Circle: def __init__(self, radius, max_radius): self._radius = radius self.max_radius = max_radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value <= self.max_radius: self._radius = value else: raise ValueError @property def area(self): return 2 * self.radius * math.picircle = Circle(10, 100)circle.radius = 20 # OK# circle.radius = 101 # Raises ValueErrorprint(circle.area)
+```
+
+
+
+```
+125.66370614359172
+```
+
+
+
+### @staticmethod
+
+
+
+Обычный метод (т. е. не помеченный декораторами @staticmethod или @classmethod) имеет доступ к свойствам конкретного экземпляра класса.
+
+
+
+@staticmethod — метод, принадлежащий классу, а не экземпляру класса. Можно вызывать без создания экземпляра, т. к. метод не имеет доступа к свойствам экземпляра. При помощи @staticmethod помечают функционал, логически связанный с классом, но не требующий доступа к свойствам экземпляра.
+
+
+
+### @classmethod, cls, self
+
+
+
+Если метод не должен иметь доступа к свойствам конкретного экземпляра класса (как @staticmethod), но должен иметь доступ к другим методам и переменным класса, то следует использовать @classmethod.
+
+
+
+```
+class B(object): def foo(self, x): print(f"Run foo({self}, {x})") @classmethod def class_foo(cls, x): print(f"Run class_foo({cls}, {x})") @staticmethod def static_foo(x): print(f"Run static_foo({x})")b = B()b.foo(1)b.class_foo(1)b.static_foo(1)
+```
+
+
+
+```
+Run foo(<__main__.B object at 0x000001A1FFF3A980>, 1)
+Run class_foo(<class '__main__.B'>, 1)
+Run static_foo(1)
+```
+
+
+
+У @classmethod первым параметром должен быть cls (класс), а у обычного метода — self (экземпляр класса).
+Для @staticmethod не требуется ни cls, ни self.
+
+
+
+### __dict__
+
+
+
+Каждый класс и каждый объект имеет атрибут __dict__. Это «системный», определённый интерпретатором атрибут, его не нужно создавать вручную. __dict__ — словарь, который хранит пользовательские атрибуты, и в котором ключом является _имя атрибута_, значением, соответственно, _значение атрибута_.
+
+
+
+```
+class Supercriminal: publisher = 'DC Comics'Riddler = Supercriminal()print(Supercriminal.__dict__)print(Riddler.__dict__)Riddler.name = 'Edward Nygma'print(Riddler.__dict__) # Values from object __dict__print(Riddler.publisher) # Value from class __dict__
+```
+
+
+
+```
+{'__module__': '__main__', 'publisher': 'DC Comics', '__dict__': <attribute '__dict__' of 'Supercriminal' objects>, '__weakref__': <attribute '__weakref__' of 'Supercriminal' objects>, '__doc__': None}
+{}
+{'name': 'Edward Nygma'}
+DC Comics
+```
+
+
+
+Каждый раз при запросе пользовательского атрибута Python последовательно обыскивает сам объект, класс объекта и классы, от которых унасаледован класс объекта.
+
+
+
+### __slots__
+
+
+
+Если вы припомните разницу между списком и кортежем, а также между множеством и иимутабельным множеством, то заметите, что создатели Python пытаются предоставлять разработчикам выбор между удобством и скоростью (тут Си с ассемблерными вставками слегка напрягается, но потом опадает, как будто хотел что-то сказать, но благоразумно передумал). К списку таких же особенностей языка, заточенных на увеличение производительности и уменьшение занимаемой памяти, относится и __slots__.
+
+
+
+Вот [официальная документация](https://docs.python.org/3/reference/datamodel.html?highlight=slots#object.__slots__) по __slots__, а вот [дополнительные разъяснения](https://stackoverflow.com/questions/472000/usage-of-slots/28059785#28059785) от одного из разработчиков официальной документации. При выборе «slots или не slots» не забывайте также про существование [PEP 412 – Key-Sharing Dictionary](https://peps.python.org/pep-0412/), который внёс некоторый раздрай в некогда однозначное отношение к __slots__.
+
+
+
+__dict__, рассмотренный чуть выше – изменяемая структура, и вы можете на лету добавлять и удалять поля из класса, что удобно, но порой медленно. Вы можете разменять удобство на скорость и размер занимаемой памяти, создав __slots__ — жестко заданный список предопределенных атрибутов, резервирующий память, создание которого _запрещает дальнейшее создание __dict__ и __weakref___. Слоты можно использовать, когда у класса может быть очень много полей, например, в ORM, либо когда критична производительность, потому что доступ к списку работает быстрее, чем поиск в словаре.
+
+
+
+```
+class Clan: __slots__ = ["first", "second"]clan = Clan()clan.first = "Joker"clan.second = "Lex Luthor"# clan.third = "Green Goblin" # Raises AttributeError# print(clan.__dict__) # Raises AttributeError
+```
+
+
+
+Слоты используются, скажем, в библиотеках requests (например, __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"]) или ORM peewee (__slots_\_ = ('stack', '_sql', '_values', 'alias_manager', 'state')).
+
+
+
+Наследование __slots__ имеет определенную специфику и будет рассмотрено ниже.
+
+
+
+Чтобы было понятно, о каком приросте производительности и снижении потребления памяти идёт речь, сделаем простое сравнение:
+
+
+
+```
+import timeitimport pympler.asizeof # В нашем случае sys.getsizeof - не лучший вариант, берем стороннее решениеclass NotSlotted(): passclass Slotted(): __slots__ = 'foo'not_slotted = NotSlotted()slotted = Slotted()def get_set_delete_fn(obj): def get_set_delete(): obj.foo = "Never Ending Song of Love" del obj.foo return get_set_deletens = min(timeit.repeat(get_set_delete_fn(not_slotted)))s = min((timeit.repeat(get_set_delete_fn(slotted))))print(ns, s, f'{(ns - s) / s * 100} %')print(pympler.asizeof.asizeof(not_slotted), 'bytes')print(pympler.asizeof.asizeof(slotted), 'bytes')
+```
+
+
+
+```
+0.10838449979200959 0.08712740009650588 24.39772066187959 %
+280 bytes
+40 bytes
+```
+
+
+
+Я в Python 3.10 в Windows вижу 24 % разницы.
+
+
+
+На всякий случай напоминаю еще раз — прогняйте все непонятные примеры кода в IDE, их можно и нужно анализировать, корректировать и видоизменять. Попробуйте, например, самостоятельно посмотреть потребление памяти объектов с __dict__ и __slots__. А заодно на практике испытайте давно напрашивающийся, и наконец появившийся в Python 3.10 [симбиоз](https://docs.python.org/3/library/dataclasses.html#module-contents) между __slots__ и dataclass.
+
+
+
+### Утиная типизация
+
+
+
+[Утиная типизация](https://en.wikipedia.org/wiki/Duck_typing) (duck types) — постулирование реализации интерфейса классом не через явное объявление, а через реализацию методов интерфейса. Так, каждый класс, реализующий методы __next__() и __iter__(), автоматически становится итератором, несмотря на отсутствие явного объявления (что-нибудь вроде [iterator](https://habr.com/ru/users/iterator/)) или, скажем, наследования от класса Iterator.
+
+
+
+### Iterator
+
+
+
+Итератор — класс, реализующий методы __next__() и __iter__().
+Метод __next__() должен возвращать следующее значение итератора или выкидывать исключение StopIteration, чтобы сигнализировать о том, что итератор исчерпал доступные значения.
+Метод __iter__() должен возвращать "self".
+
+
+
+```
+class LimitCounter: def __init__(self, max_value: int): self.count = 0 self.max_value = max_value def __next__(self): self.count += 1 if self.count <= self.max_value: return self.count else: raise StopIteration def __iter__(self): return selflimit_counter = LimitCounter(2)print(next(limit_counter))print(next(limit_counter))# print(next(limit_counter)) # Raises StopIteration
+```
+
+
+
+```
+1
+2
+```
+
+
+
+### Comparable
+
+
+
+Начиная с Python 3.4, для того, чтобы экземпляры метода можно было сравнивать между собой, достаточно определить методы __lt__ (меньше) и __eq__ (равно), а также задействовать декоратор @functools.total_ordering.
+
+
+
+```
+from functools import total_ordering@total_orderingclass Person: def __init__(self, firstname: str, lastname: str): self.firstname: str = firstname self.lastname: str = lastname def _is_valid_operand(self, other): return hasattr(other, "lastname") and hasattr(other, "firstname") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.lastname, self.firstname) == (other.lastname, other.firstname) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.lastname, self.firstname) < (other.lastname, other.firstname)Finn = Person("Finn", "the Human")Jake = Person("Jake", "the Dog")print(Finn != Jake)
+```
+
+
+
+```
+True
+```
+
+
+
+### Hashable
+
+
+
+Хешируемые объекты должны реализовывать методы __hash__() и __eq__(). Хеш объекта должен быть неизменен в течении всего жизненного цикла. Хешируемые объекты можно использовать как ключи в словарях и как элементы множеств, так как эти структуры используют хеш-таблицу для внутреннего представления данных.
+
+
+
+Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do. That is why Python automatically makes classes unhashable if you only implement eq().
+
+
+
+```
+class Hero: def __init__(self, name: str, level: int): self.name: str = name self.level: int = level def _is_valid_operand(self, other): return hasattr(other, "name") and hasattr(other, "level") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.name, self.level) == (other.name, other.level) def __hash__(self): return hash((self.name, self.level))Finn = Hero("Finn the Human", 10_000)Jake = Hero("Jake the Dog", 10_000)print(hash(Finn))print(hash(Jake))
+```
+
+
+
+```
+-8707075988359731747
+-2276052447712954388
+```
+
+
+
+### Sortable
+
+
+
+Для возможности применения к последовательностям объектов таких методов как sort() или max() необходимо, как и в случае Comparable, определить методы __lt__ (меньше) и __eq__ (равно), а также задействовать декоратор @functools.total_ordering.
+
+
+
+Для более предсказумого поведения объекта в условиях различного контекста вы можете определить полное множество функций сравнения (__lt()__, __gt()__, __le__() и __ge__()).
+
+
+
+Для примера создадим класс студентов, которых можно будет сортировать не по имени, а по среднему баллу.
+
+
+
+```
+from functools import total_orderingfrom statistics import mean@total_orderingclass Student: def __init__(self, name: str, grades: list[int]): self.name: str = name self.grades: list[int] = grades def _is_valid_operand(self, other): return hasattr(other, "name") and hasattr(other, "grades") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return mean(self.grades) == mean(other.grades) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return mean(self.grades) < mean(other.grades) # определим str для человеко-читаемой репрезентации объекта def __str__(self): return self.name + " " + str(mean(self.grades))Melissa = Student("Melissa Andrew", [4, 3, 4, 5, 4])Peter = Student("Peter Shining Jr.", [3, 3, 4, 5, 3])Joe = Student("Just Joe", [5, 5, 4, 5, 5])print([str(stud) for stud in sorted([Peter, Melissa, Joe], reverse=True)])
+```
+
+
+
+```
+['Just Joe 4.8', 'Melissa Andrew 4', 'Peter Shining Jr. 3.6']
+```
+
+
+
+### Callable
+
+
+
+Для возможности вызова объекта в качестве функции необходимо реализовать метод __call__. Типы, поддерживающие возможность их вызова в качестве функции, могут принимать набор аргументов.
+
+
+
+```
+class Counter: def __init__(self): self.i = 0 def __call__(self): self.i += 1 return self.icounter = Counter()print(counter())print(counter())print(counter())
+```
+
+
+
+```
+1
+2
+3
+```
+
+
+
+@classmethod нельзя вызывать в качестве функции:
+
+
+
+```
+class Check(): @classmethod def class_method(cls): pass @staticmethod def static_method(): pass def instance_method(self): pass for attr, val in vars(Check).items(): if not attr.startswith("__"): print (attr, f"{'is' if callable(val) else 'is NOT'} callable")
+```
+
+
+
+```
+class_method is NOT callable
+static_method is callable
+instance_method is callable
+```
+
+
+
+Контекстные менеджеры, описанные в предыдущей главе, тоже, как мы теперь видим, определяются через утиную типизацию при помощи методов __enter__ и __exit__.
+
+
+
+### Утиная типизация итерируемых объектов
+
+
+
+### Iterable
+
+
+
+[Iterable](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable) — объект, который для предоставления возможности поочерёдного прохода по всем своим элементам должен реализовывать метод __iter__(), возвращающий итератор. У каждого объекта с методом __iter__() автоматически начинает работать метод __contains__().
+
+
+
+```
+class MyIterable: def __init__(self, *args): self.a = list(args) def __iter__(self): return iter(self.a)mi = MyIterable(1, 2, 3, 4)print([el for el in mi])print(1 in mi) # __contains__()
+```
+
+
+
+```
+[1, 2, 3, 4]
+True
+```
+
+
+
+### Collection
+
+
+
+[Collection](https://docs.python.org/3/library/collections.abc.html#collections.abc.Collection) — объект, предоставляющий возможность поочерёдного прохода по всем своим элементам и обладающий конечным размером.
+В дополнение к iter() должен быть реализован метод len(), возвращающий размер коллекции.
+
+
+
+```
+class MyCollection: def __init__(self, *args): self.a = list(args) def __iter__(self): return iter(self.a) def __len__(self): return len(self.a)mc = MyCollection(1, 2, 3, 4)print([el for el in mc])print(1 in mc)print(len(mc))
+```
+
+
+
+```
+[1, 2, 3, 4]
+True
+4
+```
+
+
+
+### Sequence
+
+
+
+Требует методы len() and getitem(). getitem() должен отдавать элемент с требуемым индексом или вызывать исключение IndexError.
+Автоматически будут порождены методы iter(), reversed() и contains().
+
+
+
+```
+class MySequence: def __init__(self, a): self.a = a def __len__(self): return len(self.a) def __getitem__(self, i): return self.a[i]
+```
+
+
+
+### ABC Sequence
+
+
+
+Коллекция Sequence из [Abstract Base Classes for Containers](https://docs.python.org/3/library/collections.abc.html) предоставляет расширенный интерфейс по сравнению с обычной Sequence.
+Всё так же требуя __getitem__ и __len__, предоставляет __contains__, __iter__, __reversed_\_, index и count.
+
+
+
+```
+from collections import abcclass MyAbcSequence(abc.Sequence): def __init__(self, a): self.a = a def __len__(self): return len(self.a) def __getitem__(self, i): return self.a[i]
+```
+
+
+
+### Таблица требуемых и доступных методов:
+
+
+
+```
++------------+------------+------------+------------+--------------+
+| | Iterable | Collection | Sequence | ABC Sequence |
++------------+------------+------------+------------+--------------+
+| iter() | нужен | нужен | + | + |
+| contains() | + | + | + | + |
+| len() | | нужен | нужен | нужен |
+| getitem() | | | нужен | нужен |
+| reversed() | | | + | + |
+| index() | | | | + |
+| count() | | | | + |
++------------+------------+------------+------------+--------------+
+```
+
+
+
+И вообще, потщательнее присмотритесь с collections.abc, там есть множество заготовок, которые помогут вам сэкономить немало времени. Например, если к упомянутым относительно ABC Sequence __getitem__ и __len__ добавить __setitem__, __delitem__ и insert, то в ответ вы получите коллекцию MutableSequence, которая, кроме возможностей Sequence, имеет еще методы append, reverse, extend, pop, remove и __iadd__.
+
+
+
+### Копирование объектов
+
+
+
+В Python оператор присваивания (=) не копирует объекты. Вместо этого он создает связь между существующим объектом и именем целевой переменной. Чтобы создать копии объекта в Python, необходимо использовать модуль copy. Более того, существует два способа создания копий для данного объекта с помощью модуля copy.
+
+
+
+Shallow Copy – это побитовая копия объекта. Созданный скопированный объект имеет точную копию значений в исходном объекте. Если одно из значений является ссылкой на другие объекты, копируются только адреса ссылок на них.
+
+
+
+Deep Copy – рекурсивно копирует все значения от исходного объекта к целевому, т. е. дублирует даже объекты, на которые ссылается исходный объект.
+
+
+
+```
+from copy import copy, deepcopyclass A: def __init__(self, val: list): self.val = val def change_val(self, val: list): self.val = vala = A(list("one"))# Просто копирование ссылки на объектb = a # Assignment# Создание нового объекта и копирование ссылок на объекты, найденные в изначальном объектеc = copy(a) # Shallow copy# Создание нового объекта с последующим рекурсивным копированием содержащихся внутри объектовd = deepcopy(a) # Deep Copyb.change_val(list("two"))c.change_val(list("three"))d.change_val(list("four"))print(a.val, b.val, c.val, d.val)print(id(a), id(b), id(c), id(d))print(id(a.val[1]), id(c.val[1]))
+```
+
+
+
+```
+['t', 'w', 'o'] ['t', 'w', 'o'] ['t', 'h', 'r', 'e', 'e'] ['f', 'o', 'u', 'r']
+1795295519472 1795295519472 1793149281968 1793149273808
+1795217224688 1795217321264
+```
+
+
+
+### Наследование
+
+
+
+```
+class Person: def __init__(self, name, age): self.name = name self.age = ageclass Employee(Person): def __init__(self, name, age, staff_num, email): super().__init__(name, age) self.staff_num = staff_num self.email = email
+```
+
+
+
+### Множественное наследование
+
+
+
+При множественном наследовании порядок разрешения методов (method resolution order, MRO) позволяет Питону выяснить, из какого класса-предка нужно вызывать метод, если он не обнаружен непосредственно в классе-потомке.
+
+
+
+```
+class PrivateStaffData: def __init__(self, private_email): self.private_email = private_emailclass PublicStaffData: def __init__(self, work_email): self.work_email = work_emailclass StaffData(PrivateStaffData, PublicStaffData): def __init__(self, private_email, work_email): super().__init__()print(StaffData.mro())
+```
+
+
+
+```
+[<class '__main__.StaffData'>, <class '__main__.PrivateStaffData'>, <class '__main__.PublicStaffData'>, <class 'object'>]
+```
+
+
+
+MRO строит иерархию наследования таким образом, чтобы более специфичные методы класса-потомка перекрывали менее специфичные методы класса-предка. MRO строит упорядоченный список классов, в которых будет производиться поиск метода слева направо (линеаризация класса).
+
+
+
+Для решения проблемы ромбовидной структуры (которая неявно присутствует даже в простейшем случае, так как все классы наследуются от object) линеаризация должна быть монотонной. Монотонность — свойство, которое требует соблюдения в линеаризации класса-потомка того же порядка следования классов-прародителей, что и в линеаризации класса-родителя. Линеаризация по сути является [топологической сортировкой](https://en.wikipedia.org/wiki/Topological_sorting). В ранних версиях Python использовался алгоритм DLR, сейчас в ходу [C3-линеаризация](https://en.wikipedia.org/wiki/C3_linearization).
+
+
+
+Если после удовлетворения свойства монотонности остаётся больше одного варианта линеаризации, то применяется порядок локального старшинства (local precedence ordering), т. е. порядок соблюдения для классов-родителей в линеаризации класса-потомка того же порядка, что и при его объявлении. Например, если класс объявлен как D(A, B, C), то в линеаризации D класс A должен стоять раньше B, а класс B — раньше C.
+
+
+
+Если разрешение всех конфликтов при линеаризации невозможно, то остается три пути:
+1 — переменой мест классов-предков в объявлении класса-потомка (но это помогает далеко не всегда);
+2 — пересмотр иерархии наследования;
+3 — определение своей собственной линеаризации через метаклассы при помощи метода mro(cls). Но при данном подходе надо быть готовым к тому, что будет использован менее специфичный метод класса-родителя вместо более специфичного метода класса-потомка.
+
+
+
+При задании своей собственной линеаризации Python отключает встроенные проверки.
+
+
+
+### Наследование классов со __slots__
+
+
+
+При одиночном наследовании __slots__ нормально наследуется, но это не предотвращает создание __dict__:
+
+
+
+```
+class SlotsClass: __slots__ = 'foo', 'bar'class ChildSlotsClass(SlotsClass): ...obj = ChildSlotsClass()print(obj.__slots__)obj.something_new = "underwater stones"print(obj.__dict__)
+```
+
+
+
+```
+('foo', 'bar')
+{'something_new': 'underwater stones'}
+```
+
+
+
+Для ограничения дочернего класса слотами нужно в нём снова присвоить значение атрибуту __slots__, родительские поля дублировать не нужно.
+
+
+
+```
+class SlotsClass: __slots__ = 'foo', 'bar'class ChildSlotsClass(SlotsClass): __slots__ = 'baz'obj = ChildSlotsClass()# obj.something_new = "underwater stones" # Raises AttributeError: 'ChildSlotsClass' object has no attribute 'something_new'
+```
+
+
+
+Множественное же наследование классов с _непустыми_ __slots__ невозможно.
+
+
+
+### Метапрограммирование
+
+
+
+Что такое класс? Это, в принципе, просто кусок кода, описывающий, как создать объект. Но в Python класс — это нечто большее, классы также являются объектами; как только используется ключевое слово class, Python исполняет команду и создаёт объект:
+
+
+
+```
+class A: ...
+```
+
+
+
+В памяти будет создан объект с именем A.
+
+
+
+Классы, как и другие объекты, можно создавать на ходу:
+
+
+
+```
+def custom_class(name): if name == "foo": class Foo: ... return Foo # Возвращает именно класс, а не экземпляр else: class Bar: ... return BarMyClass = custom_class("foo")print(MyClass) # Функция возвращает класс, а не экземплярprint(my_class := MyClass()) # Можно создать экземпляр класса
+```
+
+
+
+```
+<class '__main__.custom_class.<locals>.Foo'>
+<__main__.custom_class.<locals>.Foo object at 0x000001F0ECF97610>
+```
+
+
+
+Но это не очень удобно, так как нам до сих пор приходится писать весь код класса.
+
+
+
+Основная цель метаклассов — автоматически изменять класс в момент создания, генерируя классы в соответствии с текущим контекстом.
+Сами по себе метаклассы достаточно просты и работают примерно следующим образом:
+перехватывают создание класса,
+изменяют класс,
+возвращают модифицированный класс.
+
+
+
+Но обычно логику работы метаклассов насыщают вещами вроде [интроспекции](https://en.wikipedia.org/wiki/Type_introspection#Python) или манипуляцией наследованием, поэтому конечный код выглядит достаточно громоздко.
+
+
+
+Здесь неплохо было бы добавить еще пару страниц про ньюансы создания и работы метаклассов, но позвольте переадресовать вас на вот эту [прекрасную статью](https://habr.com/ru/post/145835/).
+
+
+
+При помощи метаклассов хорошо решаются задачи, например, генерации классов для ORM. Скажем, для
+
+
+
+```
+class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField()
+```
+
+
+
+код
+
+
+
+```
+keanu = Person(name="Keanu Reeves", age=58) print(keanu.age)
+```
+
+
+
+распечатает число, взятое из БД, потому что models.Model определяет __metaclass__, который сотворит некоторую магию и превратит класс Person, который мы только что определили достаточно простым выражением, в сложную привязку к базе данных.
+
+
+
+Если вы всё еще ломаете голову, где бы вам пришить метапрограммирование в своём текущем проекте, чтобы потом упомянуть об этом в резюме, то вот вам на 147 % уместная цитата из [Тима Питерса](https://en.wikipedia.org/wiki/Tim_Peters_(software_engineer)): «[Metaclasses] are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why)», что в вольном переводе означает «Метаклассы нужны только уверенным в себе людям, которые точно знают, чего хотят от жизни, а вовсе не тебе».
+
+
+
+### @abstractmethod
+
+
+
+Абстрактный класс в Python — аналог интерфейса в других языках (например, в C#) — класс, содержащий только сигнатуры методов, без реализации. Реализация методов переложена на классы-потомки. Задача абстрактного класса соответствует задаче интерфейса — _обязать_ классы-потомки реализовывать _все_ методы, заложенные в классе-родителе.
+
+
+
+```
+import abcclass AbstractClass(metaclass=abc.ABCMeta): @abc.abstractmethod def return_anything(self): returnclass ConcreteClass(AbstractClass): def return_anything(self): return 42c = ConcreteClass()print(c.return_anything())
+```
+
+
+
+```
+42
+```
+
+
+
+Если не специфицировать return_anything() в ConcreteClass, при попытке вызвать c.return_anything() будет выброшено исключение TypeError: Can't instantiate abstract class ConcreteClass with abstract method return_anything.
+
+
+
+## Источники
+
+
+
+Официальная документация Python [docs.python.org](https://docs.python.org/), включающая [The Python Standard Library](https://docs.python.org/3/library/index.html).
+Весьма подробное руководство (совсем уж базовый синтаксис не включен): [Comprehensive Python Cheatsheet](https://github.com/gto76/python-cheatsheet).
+Руководство с включением базового синтаксиса: [Python Cheatsheet](https://github.com/wilfredinni/python-cheatsheet). Включает практические Jupiter [Notebooks](https://github.com/wilfredinni/python-cheatsheet/tree/master/jupyter_notebooks).
+Сипсок библиотек и фреймворков: [Awesome Python](https://github.com/vinta/awesome-python).
+Около-питоновские практические советы (pip, virtualenv, pyInstaller и т. д.): ["The Hitchhiker’s Guide to Python"](https://github.com/realpython/python-guide).
+Мануал для начинающих дата-сайентистов: [Joel Grus, "Data Science from Scratch"](https://github.com/joelgrus/data-science-from-scratch).
+Руководство для начинающих: ["Python Notes for Professionals"](https://goalkicker.com/PythonBook/).
+Руководство для опытных программистов: ["Python 3 Patterns, Recipes and Idioms"](https://python-3-patterns-idioms-test.readthedocs.io/en/latest/index.html).
+Архитектурные паттерны: [Harry Percival & Bob Gregory, "Architecture Patterns with Python"](https://www.cosmicpython.com/book/preface.html).
+
+Теги:
+
+- [учебник](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%B8%D0%BA])
+- [python](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[python])
+- [roadmap](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[roadmap])
+- [career](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[career])
+
+Хабы:
+
+- [Python](https://habr.com/ru/hubs/python/)
+- [Программирование](https://habr.com/ru/hubs/programming/)
+- [Карьера в IT-индустрии](https://habr.com/ru/hubs/career/) \ No newline at end of file
diff --git a/PYTHON/Password & key = PATH.md b/PYTHON/Password & key = PATH.md
new file mode 100644
index 0000000..a5dc455
--- /dev/null
+++ b/PYTHON/Password & key = PATH.md
@@ -0,0 +1,94 @@
+```python
+# Пример куска программы:
+from django.conf import settings
+
+def webhook(request):
+ ''' Обработчик вебхука от платежной системы '''
+ api_token = settigs.MY_API_KEY # ключ берем из файла settings.py
+ add_mailchimp_email_with_tag(email=request.POST.get('email',
+ audience_name='DONATES',
+ tag='DONATE')
+```
+==`vim config/settings.py`==
+```sh
+#..........
+
+MY_API_KEY = os.getenv('MY_API_KEY')
+
+#..........
+```
+
+### Уровни переменных окружения:
+
+##### - OS (операционная система)
+```sh
+sudo vim /etc/environment
+#--------------
+THIRD_VAR="123"
+# будет видна после перезагрузки
+```
+##### - USER (пользователь)
+```sh
+vim ~/.bashrc
+#--------------
+export USER_VAR=123
+# exit
+echo $USER_VAR
+```
+##### - SESSION (сессия)
+```sh
+export SESSION_VAR=777
+echo $SESSION_VAR
+# после выхода из сессии переменная пропадает
+```
+##### - APPLICATION (приложение)
+```sh
+vim setenv.sh
+#--------------
+#!bash/bin/env python3
+
+export VAR1=123
+export VAR1=12345
+export VAR1=123567hbkjbjk
+#--------------
+
+# запустим скрипт:
+. ./setenv.sh
+
+# или
+source ./setenv.sh
+
+# Также можно сразу запускать приложение с установкой переменных,
+> PYTHON_ENV=123 VAR=435 VAR2=jfhfg3 python testenv.py
+
+```
+
+```python
+# Использование модуля < python-dotenv >
+pip install python-dotenv
+
+nvim .env
+API_KEY='my_secret_key'
+# Сразу добавляем этот файл в .gitignore !!!
+
+# Программа:
+from dotenv import load_dotenv
+
+load_dotenv()
+
+key = os.getenv('API_KEY')
+print(key)
+
+####################
+# Скрытие пароля при вводе:
+from getpass import getpass
+import pyAesCrypt
+
+password = getpass(promt='Пароль для расшифровки')
+pyAesCrypt.decryptFile(
+ "secret_data.aes",
+ "secret_data.txt",
+ password
+)
+```
+
diff --git a/PYTHON/PrettyTable.md b/PYTHON/PrettyTable.md
new file mode 100644
index 0000000..f344197
--- /dev/null
+++ b/PYTHON/PrettyTable.md
@@ -0,0 +1,15 @@
+```python
+from prettytable import PrettyTable
+
+table = PrettyTable()
+table.field_names = ['Name', 'Password', 'Sex', 'Age']
+table.add_row(['Anna', 'qwerty', 'Female', 24])
+table.add_row(['Victor', '12345', 'Male', 31])
+table.add_row(['Denis', 'mcxc', 'Male', 19])
+table.add_row(['Mia', 'be6djjx', 'Female', 42])
+table.add_row(['Oleg', 'mdso98676', 'Male', 29])
+
+print(table)
+```
+
+![[telegram-cloud-photo-size-2-5413616564063822987-y.jpg]] \ No newline at end of file
diff --git a/PYTHON/Python-cheatsheet.md b/PYTHON/Python-cheatsheet.md
new file mode 100644
index 0000000..227e7cb
--- /dev/null
+++ b/PYTHON/Python-cheatsheet.md
@@ -0,0 +1,3717 @@
+---
+title: Python CheatSheet
+description: The most commonly used Python concepts are given here.
+created: 2022-10-20
+---
+
+# Python CheatSheet for Developers
+
+## Content Outlines
+
+**1. Collections:** **[`List`](#list)**__,__ **[`Dictionary`](#dictionary)**__,__ **[`Set`](#set)**__,__ **[`Tuple`](#tuple)**__,__ **[`Range`](#range)**__,__ **[`Enumerate`](#enumerate)**__,__ **[`Iterator`](#iterator)**__,__ **[`Generator`](#generator)**__.__
+**2. Types:** **[`Type`](#type)**__,__ **[`String`](#string)**__,__ **[`Regular_Exp`](#regex)**__,__ **[`Format`](#format)**__,__ **[`Numbers`](#numbers-1)**__,__ **[`Combinatorics`](#combinatorics)**__,__ **[`Datetime`](#datetime)**__.__
+**3. Syntax:** **[`Args`](#arguments)**__,__ **[`Inline`](#inline)**__,__ **[`Import`](#imports)**__,__ **[`Decorator`](#decorator)**__,__ **[`Class`](#class)**__,__ **[`Duck_Types`](#duck-types)**__,__ **[`Enum`](#enum)**__,__ **[`Exception`](#exceptions)**__.__
+**4. System:** **[`Exit`](#exit)**__,__ **[`Print`](#print)**__,__ **[`Input`](#input)**__,__ **[`Command_Line_Arguments`](#command-line-arguments)**__,__ **[`Open`](#open)**__,__ **[`Path`](#paths)**__,__ **[`OS_Commands`](#os-commands)**__.__
+**5. Data:** **[`JSON`](#json)**__,__ **[`Pickle`](#pickle)**__,__ **[`CSV`](#csv)**__,__ **[`SQLite`](#sqlite)**__,__ **[`Bytes`](#bytes)**__,__ **[`Struct`](#struct)**__,__ **[`Array`](#array)**__,__ **[`Memory_View`](#memory-view)**__,__ **[`Deque`](#deque)**__.__
+**6. Advanced:** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Introspection`](#introspection)**__,__ **[`Metaprograming`](#metaprogramming)**__,__ **[`Eval`](#eval)**__,__ **[`Coroutines`](#coroutines)**__.__
+**7. Libraries:** **[`Progress_Bar`](#progress-bar)**__,__ **[`Plot`](#plot)**__,__ **[`Table`](#table)**__,__ **[`Curses`](#curses)**__,__ **[`Logging`](#logging)**__,__ **[`Scraping`](#scraping)**__,__ **[`Web`](#web)**__,__ **[`Profile`](#profiling)**__,__
+**[`NumPy`](#numpy)**__,__ **[`Image`](#image)**__,__ **[`Audio`](#audio)**__,__ **[`PyGames`](#pygame)**__,__ **[`Pandas`](#pandas)**__,__ **[`OpenCV`](#opencv)**__,__ **[`DataFrame`](#dataframe)**__.__
+
+
+Main
+----
+```python
+if __name__ == '__main__': # Runs main() if file wasn't imported.
+ main()
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Shortcut to comment/uncomment the selected code
+----
+
+| Editor | Shortcut Block Comment | Shortcut Block Uncomment |
+| :----------: | :----------------------: | :----------------------: |
+| Eclipse | `CTRL + /` | `CTRL + /` |
+| PyDev | `CTRL + /` | `CTRL + /` |
+| PyCharm | `CTRL + /` | `CTRL + /` |
+| Notepad++ | `CTRL + K` | `CTRL + SHIFT + K` |
+| IDLE | `ALT + 3` | `ALT + 4` |
+
+**[🔼Back to Top](#content-outlines)**
+
+List
+----
+```python
+<list> = <list>[<slice>] # Or: <list>[from_inclusive : to_exclusive : ±step]
+```
+
+```python
+<list>.append(<el>) # Or: <list> += [<el>] | adds a new element to the end of the list
+<list>.extend(<collection>) # Or: <list> += <collection> | adds all elements of a collection(list,tuples,etc) to the end of the list
+```
+
+```python
+<list>.sort() # Sorts in ascending order.
+<list>.reverse() # Reverses the list in-place.
+<list> = sorted(<collection>) # Returns a new sorted list.
+<iter> = reversed(<list>) # Returns reversed iterator.
+```
+
+```python
+sum_of_elements = sum(<collection>)
+elementwise_sum = [sum(pair) for pair in zip(list_a, list_b)]
+sorted_by_second = sorted(<collection>, key=lambda el: el[1])
+sorted_by_both = sorted(<collection>, key=lambda el: (el[1], el[0]))
+flatter_list = list(itertools.chain.from_iterable(<list>))
+product_of_elems = functools.reduce(lambda out, el: out * el, <collection>)
+list_of_chars = list(<str>)
+```
+* **For details about sorted(), min() and max() see [sortable](#sortable).**
+* **Module [operator](#operator) provides functions itemgetter() and mul() that offer the same functionality as [lambda](#lambda) expressions above.**
+
+```python
+<list>.insert(<int>, <el>) # Inserts item at index and moves the rest to the right.
+<el> = <list>.pop([<int>]) # Removes and returns item at index or from the end.
+<int> = <list>.count(<el>) # Returns number of occurrences. Also works on strings.
+<int> = <list>.index(<el>) # Returns index of the first occurrence or raises ValueError.
+<list>.remove(<el>) # Removes first occurrence of the item or raises ValueError.
+<list>.clear() # Removes all items. Also works on dictionary and set.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Dictionary
+----------
+```python
+<view> = <dict>.keys() # Coll. of keys that reflects changes.
+<view> = <dict>.values() # Coll. of values that reflects changes.
+<view> = <dict>.items() # Coll. of key-value tuples that reflects chgs.
+```
+```python
+value = <dict>.get(key, default=None) # Returns default if key is missing.
+value = <dict>.setdefault(key, default=None) # Returns and writes default if key is missing.
+<dict> = collections.defaultdict(<type>) # Returns a dict with default value of type.
+<dict> = collections.defaultdict(lambda: 1) # Returns a dict with default value 1.
+```
+
+```python
+<dict> = dict(<collection>) # Creates a dict from coll. of key-value pairs.
+<dict> = dict(zip(keys, values)) # Creates a dict from two collections.
+<dict> = dict.fromkeys(keys [, value]) # Creates a dict from collection of keys.
+```
+
+```python
+<dict>.update(<dict>) # Adds items. Replaces ones with matching keys.
+value = <dict>.pop(key) # Removes item or raises KeyError.
+{k for k, v in <dict>.items() if v == value} # Returns set of keys that point to the value.
+{k: v for k, v in <dict>.items() if k in keys} # Returns a dictionary, filtered by keys.
+```
+
+### Counter
+```python
+>>> from collections import Counter
+>>> colors = ['blue', 'blue', 'blue', 'red', 'red']
+>>> counter = Counter(colors)
+>>> counter['yellow'] += 1
+Counter({'blue': 3, 'red': 2, 'yellow': 1})
+>>> counter.most_common()[0]
+('blue', 3)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Set
+---
+```python
+<set> = set() # `{}` returns a dictionary.
+```
+
+```python
+<set>.add(<el>) # Or: <set> |= {<el>}
+<set>.update(<collection> [, ...]) # Or: <set> |= <set>
+```
+
+```python
+<set> = <set>.union(<coll.>) # Or: <set> | <set>
+<set> = <set>.intersection(<coll.>) # Or: <set> & <set>
+<set> = <set>.difference(<coll.>) # Or: <set> - <set>
+<set> = <set>.symmetric_difference(<coll.>) # Or: <set> ^ <set>
+<bool> = <set>.issubset(<coll.>) # Or: <set> <= <set>
+<bool> = <set>.issuperset(<coll.>) # Or: <set> >= <set>
+```
+
+```python
+<el> = <set>.pop() # Raises KeyError if empty.
+<set>.remove(<el>) # Raises KeyError if missing.
+<set>.discard(<el>) # Doesn't raise an error.
+```
+
+### Frozen Set
+* **Is immutable and hashable.**
+* **That means it can be used as a key in a dictionary or as an element in a set.**
+```python
+<frozenset> = frozenset(<collection>)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Tuple
+-----
+**Tuple is an immutable and hashable list.**
+```python
+<tuple> = () # Empty tuple.
+<tuple> = (<el>,) # Or: <el>,
+<tuple> = (<el_1>, <el_2> [, ...]) # Or: <el_1>, <el_2> [, ...]
+```
+
+### Named Tuple
+**Tuple's subclass with named elements.**
+
+```python
+>>> from collections import namedtuple
+>>> Point = namedtuple('Point', 'x y')
+>>> p = Point(1, y=2)
+Point(x=1, y=2)
+>>> p[0]
+1
+>>> p.x
+1
+>>> getattr(p, 'y')
+2
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Range
+-----
+**Immutable and hashable sequence of integers.**
+```python
+<range> = range(stop) # range(to_exclusive)
+<range> = range(start, stop) # range(from_inclusive, to_exclusive)
+<range> = range(start, stop, ±step) # range(from_inclusive, to_exclusive, ±step_size)
+```
+
+```python
+>>> [i for i in range(3)]
+[0, 1, 2]
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Enumerate
+---------
+```python
+for i, el in enumerate(<collection> [, i_start]):
+ ...
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Iterator
+--------
+```python
+<iter> = iter(<collection>) # `iter(<iter>)` returns unmodified iterator.
+<iter> = iter(<function>, to_exclusive) # A sequence of return values until 'to_exclusive'.
+<el> = next(<iter> [, default]) # Raises StopIteration or returns 'default' on end.
+<list> = list(<iter>) # Returns a list of iterator's remaining elements.
+```
+
+### Itertools
+```python
+import itertools as it
+```
+
+```python
+<iter> = it.count(start=0, step=1) # Returns updated value endlessly. Accepts floats.
+<iter> = it.repeat(<el> [, times]) # Returns element endlessly or 'times' times.
+<iter> = it.cycle(<collection>) # Repeats the sequence endlessly.
+```
+
+```python
+<iter> = it.chain(<coll>, <coll> [, ...]) # Empties collections in order (figuratively).
+<iter> = it.chain.from_iterable(<coll>) # Empties collections inside a collection in order.
+```
+
+```python
+<iter> = it.islice(<coll>, to_exclusive) # Only returns first 'to_exclusive' elements.
+<iter> = it.islice(<coll>, from_inc, …) # `to_exclusive, +step_size`. Indices can be None.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Generator
+---------
+* **Any function that contains a yield statement returns a generator.**
+* **Generators and iterators are interchangeable.**
+
+```python
+def count(start, step):
+ while True:
+ yield start
+ start += step
+```
+
+```python
+>>> counter = count(10, 2)
+>>> next(counter), next(counter), next(counter)
+(10, 12, 14)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Type
+----
+* **Everything is an object.**
+* **Every object has a type.**
+* **Type and class are synonymous.**
+
+```python
+<type> = type(<el>) # Or: <el>.__class__
+<bool> = isinstance(<el>, <type>) # Or: issubclass(type(<el>), <type>)
+```
+
+```python
+>>> type('a'), 'a'.__class__, str
+(<class 'str'>, <class 'str'>, <class 'str'>)
+```
+
+#### Some types do not have built-in names, so they must be imported:
+```python
+from types import FunctionType, MethodType, LambdaType, GeneratorType, ModuleType
+```
+
+### Abstract Base Classes
+**Each abstract base class specifies a set of virtual subclasses. These classes are then recognized by isinstance() and issubclass() as subclasses of the ABC, although they are really not. ABC can also manually decide whether or not a specific class is its virtual subclass, usually based on which methods the class has implemented. For instance, Iterable ABC looks for method iter(), while Collection ABC looks for iter(), contains() and len().**
+
+```python
+>>> from collections.abc import Iterable, Collection, Sequence
+>>> isinstance([1, 2, 3], Iterable)
+True
+```
+
+```text
++------------------+------------+------------+------------+
+| | Iterable | Collection | Sequence |
++------------------+------------+------------+------------+
+| list, range, str | yes | yes | yes |
+| dict, set | yes | yes | |
+| iter | yes | | |
++------------------+------------+------------+------------+
+```
+
+```python
+>>> from numbers import Number, Complex, Real, Rational, Integral
+>>> isinstance(123, Number)
+True
+```
+
+```text
++--------------------+----------+----------+----------+----------+----------+
+| | Number | Complex | Real | Rational | Integral |
++--------------------+----------+----------+----------+----------+----------+
+| int | yes | yes | yes | yes | yes |
+| fractions.Fraction | yes | yes | yes | yes | |
+| float | yes | yes | yes | | |
+| complex | yes | yes | | | |
+| decimal.Decimal | yes | | | | |
++--------------------+----------+----------+----------+----------+----------+
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+String
+------
+```python
+<str> = <str>.strip() # Strips all whitespace characters from both ends.
+<str> = <str>.strip('<chars>') # Strips all passed characters from both ends.
+```
+
+```python
+<list> = <str>.split() # Splits on one or more whitespace characters.
+<list> = <str>.split(sep=None, maxsplit=-1) # Splits on 'sep' str at most 'maxsplit' times.
+<list> = <str>.splitlines(keepends=False) # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n.
+<str> = <str>.join(<coll_of_strings>) # Joins elements using string as a separator.
+```
+
+```python
+<bool> = <sub_str> in <str> # Checks if string contains a substring.
+<bool> = <str>.startswith(<sub_str>) # Pass tuple of strings for multiple options.
+<bool> = <str>.endswith(<sub_str>) # Pass tuple of strings for multiple options.
+<int> = <str>.find(<sub_str>) # Returns start index of the first match or -1.
+<int> = <str>.index(<sub_str>) # Same, but raises ValueError if missing.
+```
+
+```python
+<str> = <str>.replace(old, new [, count]) # Replaces 'old' with 'new' at most 'count' times.
+<str> = <str>.translate(<table>) # Use `str.maketrans(<dict>)` to generate table.
+```
+
+```python
+<str> = chr(<int>) # Converts int to Unicode character.
+<int> = ord(<str>) # Converts Unicode character to int.
+```
+* **Also: `'lstrip()'`, `'rstrip()'` and `'rsplit()'`.**
+* **Also: `'lower()'`, `'upper()'`, `'capitalize()'` and `'title()'`.**
+
+### Property Methods
+```text
++---------------+----------+----------+----------+----------+----------+
+| | [ !#$%…] | [a-zA-Z] | [¼½¾] | [²³¹] | [0-9] |
++---------------+----------+----------+----------+----------+----------+
+| isprintable() | yes | yes | yes | yes | yes |
+| isalnum() | | yes | yes | yes | yes |
+| isnumeric() | | | yes | yes | yes |
+| isdigit() | | | | yes | yes |
+| isdecimal() | | | | | yes |
++---------------+----------+----------+----------+----------+----------+
+```
+* **Also: `'isspace()'` checks for `'[ \t\n\r\f\v\x1c-\x1f\x85\u2000…]'`.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Regex
+-----
+```python
+import re
+<str> = re.sub(<regex>, new, text, count=0) # Substitutes all occurrences with 'new'.
+<list> = re.findall(<regex>, text) # Returns all occurrences as strings.
+<list> = re.split(<regex>, text, maxsplit=0) # Use brackets in regex to include the matches.
+<Match> = re.search(<regex>, text) # Searches for first occurrence of the pattern.
+<Match> = re.match(<regex>, text) # Searches only at the beginning of the text.
+<iter> = re.finditer(<regex>, text) # Returns all occurrences as Match objects.
+```
+
+* **Argument 'new' can be a function that accepts a Match object and returns a string.**
+* **Search() and match() return None if they can't find a match.**
+* **Argument `'flags=re.IGNORECASE'` can be used with all functions.**
+* **Argument `'flags=re.MULTILINE'` makes `'^'` and `'$'` match the start/end of each line.**
+* **Argument `'flags=re.DOTALL'` makes dot also accept the `'\n'`.**
+* **Use `r'\1'` or `'\\1'` for backreference (`'\1'` returns a character with octal code 1).**
+* **Add `'?'` after `'*'` and `'+'` to make them non-greedy.**
+
+### Match Object
+```python
+<str> = <Match>.group() # Returns the whole match. Also group(0).
+<str> = <Match>.group(1) # Returns part in the first bracket.
+<tuple> = <Match>.groups() # Returns all bracketed parts.
+<int> = <Match>.start() # Returns start index of the match.
+<int> = <Match>.end() # Returns exclusive end index of the match.
+```
+
+### Special Sequences
+```python
+'\d' == '[0-9]' # Matches decimal characters.
+'\w' == '[a-zA-Z0-9_]' # Matches alphanumerics and underscore.
+'\s' == '[ \t\n\r\f\v]' # Matches whitespaces.
+```
+
+* **By default, decimal characters, alphanumerics and whitespaces from all alphabets are matched unless `'flags=re.ASCII'` argument is used.**
+* **As shown above, it restricts all special sequence matches to the first 128 characters and prevents `'\s'` from accepting `'[\x1c-\x1f]'` (the so-called separator characters).**
+* **Use a capital letter for negation (all non-ASCII characters will be matched when used in combination with ASCII flag).**
+
+**[🔼Back to Top](#content-outlines)**
+
+Format
+------
+```python
+<str> = f'{<el_1>}, {<el_2>}' # Curly brackets can also contain expressions. This is named "Fstring"
+<str> = '{}, {}'.format(<el_1>, <el_2>) # Or: '{0}, {a}'.format(<el_1>, a=<el_2>)
+<str> = '%s, %s' % (<el_1>, <el_2>) # Redundant and inferior C style formatting.
+```
+
+### Attributes
+```python
+>>> Person = collections.namedtuple('Person', 'name height')
+>>> person = Person('Jean-Luc', 187)
+>>> f'{person.height}'
+'187'
+>>> '{p.height}'.format(p=person)
+'187'
+```
+
+### General Options
+```python
+{<el>:<10} # '<el> '
+{<el>:^10} # ' <el> '
+{<el>:>10} # ' <el>'
+{<el>:.<10} # '<el>......'
+{<el>:0} # '<el>'
+```
+* **Options can be generated dynamically: `f'{<el>:{<str/int>}[…]}'`.**
+* **Adding `'!r'` before the colon converts object to string by calling its [repr()](#class) method.**
+
+### Strings
+```python
+{'abcde':10} # 'abcde '
+{'abcde':10.3} # 'abc '
+{'abcde':.3} # 'abc'
+{'abcde'!r:10} # "'abcde' "
+```
+
+### Numbers
+```python
+{123456:10} # ' 123456'
+{123456:10,} # ' 123,456'
+{123456:10_} # ' 123_456'
+{123456:+10} # ' +123456'
+{123456:=+10} # '+ 123456'
+{123456: } # ' 123456'
+{-123456: } # '-123456'
+```
+
+### Floats
+```python
+{1.23456:10.3} # ' 1.23'
+{1.23456:10.3f} # ' 1.235'
+{1.23456:10.3e} # ' 1.235e+00'
+{1.23456:10.3%} # ' 123.456%'
+```
+
+#### Comparison of presentation types:
+```text
++--------------+----------------+----------------+----------------+----------------+
+| | {<float>} | {<float>:f} | {<float>:e} | {<float>:%} |
++--------------+----------------+----------------+----------------+----------------+
+| 0.000056789 | '5.6789e-05' | '0.000057' | '5.678900e-05' | '0.005679%' |
+| 0.00056789 | '0.00056789' | '0.000568' | '5.678900e-04' | '0.056789%' |
+| 0.0056789 | '0.0056789' | '0.005679' | '5.678900e-03' | '0.567890%' |
+| 0.056789 | '0.056789' | '0.056789' | '5.678900e-02' | '5.678900%' |
+| 0.56789 | '0.56789' | '0.567890' | '5.678900e-01' | '56.789000%' |
+| 5.6789 | '5.6789' | '5.678900' | '5.678900e+00' | '567.890000%' |
+| 56.789 | '56.789' | '56.789000' | '5.678900e+01' | '5678.900000%' |
++--------------+----------------+----------------+----------------+----------------+
+```
+
+```text
++--------------+----------------+----------------+----------------+----------------+
+| | {<float>:.2} | {<float>:.2f} | {<float>:.2e} | {<float>:.2%} |
++--------------+----------------+----------------+----------------+----------------+
+| 0.000056789 | '5.7e-05' | '0.00' | '5.68e-05' | '0.01%' |
+| 0.00056789 | '0.00057' | '0.00' | '5.68e-04' | '0.06%' |
+| 0.0056789 | '0.0057' | '0.01' | '5.68e-03' | '0.57%' |
+| 0.056789 | '0.057' | '0.06' | '5.68e-02' | '5.68%' |
+| 0.56789 | '0.57' | '0.57' | '5.68e-01' | '56.79%' |
+| 5.6789 | '5.7' | '5.68' | '5.68e+00' | '567.89%' |
+| 56.789 | '5.7e+01' | '56.79' | '5.68e+01' | '5678.90%' |
++--------------+----------------+----------------+----------------+----------------+
+```
+* **When both rounding up and rounding down are possible, the one that returns result with even last digit is chosen. That makes `'{6.5:.0f}'` a `'6'` and `'{7.5:.0f}'` an `'8'`.**
+* **This rule only effects numbers that can be represented exactly by a float (`.5`, `.25`, …).**
+
+### Ints
+```python
+{90:c} # 'Z'
+{90:b} # '1011010'
+{90:X} # '5A'
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Numbers
+-------
+```python
+<int> = int(<float/str/bool>) # Or: math.floor(<float>)
+<float> = float(<int/str/bool>) # Or: <real>e±<int>
+<complex> = complex(real=0, imag=0) # Or: <real> ± <real>j
+<Fraction> = fractions.Fraction(0, 1) # Or: Fraction(numerator=0, denominator=1)
+<Decimal> = decimal.Decimal(<str/int>) # Or: Decimal((sign, digits, exponent))
+```
+* **`'int(<str>)'` and `'float(<str>)'` raise ValueError on malformed strings.**
+* **Decimal numbers are stored exactly, unlike most floats where `'1.1 + 2.2 != 3.3'`.**
+* **Floats can be compared with: `'math.isclose(<float>, <float>)'`.**
+* **Precision of decimal operations is set with: `'decimal.getcontext().prec = <int>'`.**
+
+### Basic Functions
+```python
+<num> = pow(<num>, <num>) # Or: <num> ** <num>
+<num> = abs(<num>) # <float> = abs(<complex>)
+<num> = round(<num> [, ±ndigits]) # `round(126, -1) == 130`
+```
+
+### Math
+```python
+from math import e, pi, inf, nan, isinf, isnan # `<el> == nan` is always False.
+from math import sin, cos, tan, asin, acos, atan # Also: degrees, radians.
+from math import log, log10, log2 # Log can accept base as second arg.
+```
+
+### Statistics
+```python
+from statistics import mean, median, variance # Also: stdev, quantiles, groupby.
+```
+
+### Random
+```python
+from random import random, randint, choice # Also shuffle, gauss, triangular, seed.
+<float> = random() # A float inside [0, 1).
+<int> = randint(from_inc, to_inc) # An int inside [from_inc, to_inc].
+<el> = choice(<sequence>) # Keeps the sequence intact.
+```
+
+### Bin, Hex
+```python
+<int> = ±0b<bin> # Or: ±0x<hex>
+<int> = int('±<bin>', 2) # Or: int('±<hex>', 16)
+<int> = int('±0b<bin>', 0) # Or: int('±0x<hex>', 0)
+<str> = bin(<int>) # Returns '[-]0b<bin>'.
+```
+
+### Bitwise Operators
+```python
+<int> = <int> & <int> # And (0b1100 & 0b1010 == 0b1000).
+<int> = <int> | <int> # Or (0b1100 | 0b1010 == 0b1110).
+<int> = <int> ^ <int> # Xor (0b1100 ^ 0b1010 == 0b0110).
+<int> = <int> << n_bits # Left shift. Use >> for right.
+<int> = ~<int> # Not. Also -<int> - 1.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Combinatorics
+-------------
+* **Every function returns an iterator.**
+* **If you want to print the iterator, you need to pass it to the list() function first!**
+
+```python
+import itertools as it
+```
+
+```python
+>>> it.product([0, 1], repeat=3)
+[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1),
+ (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
+```
+
+```python
+>>> it.product('abc', 'abc') # a b c
+[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x
+ ('b', 'a'), ('b', 'b'), ('b', 'c'), # b x x x
+ ('c', 'a'), ('c', 'b'), ('c', 'c')] # c x x x
+```
+
+```python
+>>> it.combinations('abc', 2) # a b c
+[('a', 'b'), ('a', 'c'), # a . x x
+ ('b', 'c')] # b . . x
+```
+
+```python
+>>> it.combinations_with_replacement('abc', 2) # a b c
+[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x
+ ('b', 'b'), ('b', 'c'), # b . x x
+ ('c', 'c')] # c . . x
+```
+
+```python
+>>> it.permutations('abc', 2) # a b c
+[('a', 'b'), ('a', 'c'), # a . x x
+ ('b', 'a'), ('b', 'c'), # b x . x
+ ('c', 'a'), ('c', 'b')] # c x x .
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Datetime
+--------
+* **Module 'datetime' provides 'date' `<D>`, 'time' `<T>`, 'datetime' `<DT>` and 'timedelta' `<TD>` classes. All are immutable and hashable.**
+* **Time and datetime objects can be 'aware' `<a>`, meaning they have defined timezone, or 'naive' `<n>`, meaning they don't.**
+* **If object is naive, it is presumed to be in the system's timezone.**
+
+```python
+from datetime import date, time, datetime, timedelta
+from dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginary
+```
+
+### Constructors
+```python
+<D> = date(year, month, day) # Only accepts valid dates from 1 to 9999 AD.
+<T> = time(hour=0, minute=0, second=0) # Also: `microsecond=0, tzinfo=None, fold=0`.
+<DT> = datetime(year, month, day, hour=0) # Also: `minute=0, second=0, microsecond=0, …`.
+<TD> = timedelta(weeks=0, days=0, hours=0) # Also: `minutes=0, seconds=0, microsecond=0`.
+```
+* **Use `'<D/DT>.weekday()'` to get the day of the week as an int, with Monday being 0.**
+* **`'fold=1'` means the second pass in case of time jumping back for one hour.**
+* **Timedelta normalizes arguments to ±days, seconds (< 86 400) and microseconds (< 1M).**
+
+### Now
+```python
+<D/DTn> = D/DT.today() # Current local date or naive datetime.
+<DTn> = DT.utcnow() # Naive datetime from current UTC time.
+<DTa> = DT.now(<tzinfo>) # Aware datetime from current tz time.
+```
+* **To extract time use `'<DTn>.time()'`, `'<DTa>.time()'` or `'<DTa>.timetz()'`.**
+
+### Timezone
+```python
+<tzinfo> = UTC # UTC timezone. London without DST.
+<tzinfo> = tzlocal() # Local timezone. Also gettz().
+<tzinfo> = gettz('<Continent>/<City>') # 'Continent/City_Name' timezone or None.
+<DTa> = <DT>.astimezone(<tzinfo>) # Datetime, converted to the passed timezone.
+<Ta/DTa> = <T/DT>.replace(tzinfo=<tzinfo>) # Unconverted object with a new timezone.
+```
+
+### Encode
+```python
+<D/T/DT> = D/T/DT.fromisoformat('<iso>') # Object from ISO string. Raises ValueError.
+<DT> = DT.strptime(<str>, '<format>') # Datetime from str, according to format.
+<D/DTn> = D/DT.fromordinal(<int>) # D/DTn from days since the Gregorian NYE 1.
+<DTn> = DT.fromtimestamp(<real>) # Local time DTn from seconds since the Epoch.
+<DTa> = DT.fromtimestamp(<real>, <tz.>) # Aware datetime from seconds since the Epoch.
+```
+* **ISO strings come in following forms: `'YYYY-MM-DD'`, `'HH:MM:SS.mmmuuu[±HH:MM]'`, or both separated by an arbitrary character. All parts following hours are optional.**
+* **Python uses the Unix Epoch: `'1970-01-01 00:00 UTC'`, `'1970-01-01 01:00 CET'`, ...**
+
+### Decode
+```python
+<str> = <D/T/DT>.isoformat(sep='T') # Also: `timespec='auto/hours/minutes/seconds/…'`.
+<str> = <D/T/DT>.strftime('<format>') # Custom string representation.
+<int> = <D/DT>.toordinal() # Days since Gregorian NYE 1, ignoring time and tz.
+<float> = <DTn>.timestamp() # Seconds since the Epoch, from DTn in local tz.
+<float> = <DTa>.timestamp() # Seconds since the Epoch, from aware datetime.
+```
+
+### Format
+```python
+>>> dt = datetime.strptime('2015-05-14 23:39:00.00 +2000', '%Y-%m-%d %H:%M:%S.%f %z')
+>>> dt.strftime("%A, %dth of %B '%y, %I:%M%p %Z")
+"Thursday, 14th of May '15, 11:39PM UTC+02:00"
+```
+* **`'%Z'` only accepts `'UTC/GMT'` and local timezone's code. `'%z'` also accepts `'±HH:MM'`.**
+* **For abbreviated weekday and month use `'%a'` and `'%b'`.**
+
+### Arithmetics
+```python
+<D/DT> = <D/DT> ± <TD> # Returned datetime can fall into missing hour.
+<TD> = <D/DTn> - <D/DTn> # Returns the difference, ignoring time jumps.
+<TD> = <DTa> - <DTa> # Ignores time jumps if they share tzinfo object.
+<TD> = <TD> * <real> # Also: <TD> = abs(<TD>) and <TD> = <TD> ±% <TD>.
+<float> = <TD> / <TD> # How many weeks/years there are in TD. Also //.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Arguments
+---------
+### Inside Function Call
+```python
+func(<positional_args>) # func(0, 0)
+func(<keyword_args>) # func(x=0, y=0)
+func(<positional_args>, <keyword_args>) # func(0, y=0)
+```
+
+### Inside Function Definition
+```python
+def func(<nondefault_args>): ... # def func(x, y): ...
+def func(<default_args>): ... # def func(x=0, y=0): ...
+def func(<nondefault_args>, <default_args>): ... # def func(x, y=0): ...
+```
+* **Default values are evaluated when function is first encountered in the scope.**
+* **Any mutation of a mutable default value will persist between invocations.**
+
+
+Splat Operator
+--------------
+### Inside Function Call
+**Splat expands a collection into positional arguments, while splatty-splat expands a dictionary into keyword arguments.**
+```python
+args = (1, 2)
+kwargs = {'x': 3, 'y': 4, 'z': 5}
+func(*args, **kwargs)
+```
+
+#### Is the same as:
+```python
+func(1, 2, x=3, y=4, z=5)
+```
+
+### Inside Function Definition
+**Splat combines zero or more positional arguments into a tuple, while splatty-splat combines zero or more keyword arguments into a dictionary.**
+```python
+def add(*a):
+ return sum(a)
+```
+
+```python
+>>> add(1, 2, 3)
+6
+```
+
+#### Legal argument combinations:
+```python
+def f(*args): ... # f(1, 2, 3)
+def f(x, *args): ... # f(1, 2, 3)
+def f(*args, z): ... # f(1, 2, z=3)
+```
+
+```python
+def f(**kwargs): ... # f(x=1, y=2, z=3)
+def f(x, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3)
+```
+
+```python
+def f(*args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
+def f(x, *args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
+def f(*args, y, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3)
+```
+
+```python
+def f(*, x, y, z): ... # f(x=1, y=2, z=3)
+def f(x, *, y, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3)
+def f(x, y, *, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3)
+```
+
+### Other Uses
+```python
+<list> = [*<coll.> [, ...]] # Or: list(<collection>) [+ ...]
+<tuple> = (*<coll.>, [...]) # Or: tuple(<collection>) [+ ...]
+<set> = {*<coll.> [, ...]} # Or: set(<collection>) [| ...]
+<dict> = {**<dict> [, ...]} # Or: dict(**<dict> [, ...])
+```
+
+```python
+head, *body, tail = <coll.> # Head or tail can be omitted.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Inline
+------
+### Lambda
+```python
+<func> = lambda: <return_value> # A single statement function.
+<func> = lambda <arg_1>, <arg_2>: <return_value> # Also accepts default arguments.
+```
+
+### Comprehensions
+```python
+<list> = [i+1 for i in range(10)] # Or: [1, 2, ..., 10]
+<iter> = (i for i in range(10) if i > 5) # Or: iter([6, 7, 8, 9])
+<set> = {i+5 for i in range(10)} # Or: {5, 6, ..., 14}
+<dict> = {i: i*2 for i in range(10)} # Or: {0: 0, 1: 2, ..., 9: 18}
+```
+
+```python
+>>> [l+r for l in 'abc' for r in 'abc']
+['aa', 'ab', 'ac', ..., 'cc']
+```
+
+### Map, Filter, Reduce
+```python
+<iter> = map(lambda x: x + 1, range(10)) # Or: iter([1, 2, ..., 10])
+<iter> = filter(lambda x: x > 5, range(10)) # Or: iter([6, 7, 8, 9])
+<obj> = reduce(lambda out, x: out + x, range(10)) # Or: 45
+```
+* **Reduce must be imported from the functools module.**
+
+### Any, All
+```python
+<bool> = any(<collection>) # Is `bool(el)` True for any element.
+<bool> = all(<collection>) # Is True for all elements or empty.
+```
+
+### Conditional Expression
+```python
+<obj> = <exp> if <condition> else <exp> # Only one expression gets evaluated.
+```
+
+```python
+>>> [a if a else 'zero' for a in (0, 1, 2, 3)]
+['zero', 1, 2, 3]
+```
+
+### Named Tuple, Enum, Dataclass
+```python
+from collections import namedtuple
+Point = namedtuple('Point', 'x y') # Creates a tuple's subclass.
+point = Point(0, 0) # Returns its instance.
+```
+
+```python
+from enum import Enum
+Direction = Enum('Direction', 'n e s w') # Creates an enum.
+direction = Direction.n # Returns its member.
+```
+
+```python
+from dataclasses import make_dataclass
+Player = make_dataclass('Player', ['loc', 'dir']) # Creates a class.
+player = Player(point, direction) # Returns its instance.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Imports
+-------
+```python
+import <module> # Imports a built-in or '<module>.py'.
+import <package> # Imports a built-in or '<package>/__init__.py'.
+import <package>.<module> # Imports a built-in or '<package>/<module>.py'.
+```
+* **Package is a collection of modules, but it can also define its own objects.**
+* **On a filesystem this corresponds to a directory of Python files with an optional init script.**
+* **Running `'import <package>'` does not automatically provide access to the package's modules unless they are explicitly imported in its init script.**
+
+
+Closure
+-------
+**We have/get a closure in Python when:**
+* **A nested function references a value of its enclosing function and then**
+* **the enclosing function returns the nested function.**
+
+```python
+def get_multiplier(a):
+ def out(b):
+ return a * b
+ return out
+```
+
+```python
+>>> multiply_by_3 = get_multiplier(3)
+>>> multiply_by_3(10)
+30
+```
+
+* **If multiple nested functions within enclosing function reference the same value, that value gets shared.**
+* **To dynamically access function's first free variable use `'<function>.__closure__[0].cell_contents'`.**
+
+### Partial
+```python
+from functools import partial
+<function> = partial(<function> [, <arg_1>, <arg_2>, ...])
+```
+
+```python
+>>> import operator as op
+>>> multiply_by_3 = partial(op.mul, 3)
+>>> multiply_by_3(10)
+30
+```
+* **Partial is also useful in cases when function needs to be passed as an argument because it enables us to set its arguments beforehand.**
+* **A few examples being: `'defaultdict(<function>)'`, `'iter(<function>, to_exclusive)'` and dataclass's `'field(default_factory=<function>)'`.**
+
+### Non-Local
+**If variable is being assigned to anywhere in the scope, it is regarded as a local variable, unless it is declared as a 'global' or a 'nonlocal'.**
+
+```python
+def get_counter():
+ i = 0
+ def out():
+ nonlocal i
+ i += 1
+ return i
+ return out
+```
+
+```python
+>>> counter = get_counter()
+>>> counter(), counter(), counter()
+(1, 2, 3)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Decorator
+---------
+* **A decorator takes a function, adds some functionality and returns it.**
+* **It can be any [callable](#callable), but is usually implemented as a function that returns a [closure](#closure).**
+
+```python
+@decorator_name
+def function_that_gets_passed_to_decorator():
+ ...
+```
+
+### Debugger Example
+**Decorator that prints function's name every time the function is called.**
+
+```python
+from functools import wraps
+
+def debug(func):
+ @wraps(func)
+ def out(*args, **kwargs):
+ print(func.__name__)
+ return func(*args, **kwargs)
+ return out
+
+@debug
+def add(x, y):
+ return x + y
+```
+* **Wraps is a helper decorator that copies the metadata of the passed function (func) to the function it is wrapping (out).**
+* **Without it `'add.__name__'` would return `'out'`.**
+
+### LRU Cache
+**Decorator that caches function's return values. All function's arguments must be hashable.**
+
+```python
+from functools import lru_cache
+
+@lru_cache(maxsize=None)
+def fib(n):
+ return n if n < 2 else fib(n-2) + fib(n-1)
+```
+* **Default size of the cache is 128 values. Passing `'maxsize=None'` makes it unbounded.**
+* **CPython interpreter limits recursion depth to 1000 by default. To increase it use `'sys.setrecursionlimit(<depth>)'`.**
+
+### Parametrized Decorator
+**A decorator that accepts arguments and returns a normal decorator that accepts a function.**
+```python
+from functools import wraps
+
+def debug(print_result=False):
+ def decorator(func):
+ @wraps(func)
+ def out(*args, **kwargs):
+ result = func(*args, **kwargs)
+ print(func.__name__, result if print_result else '')
+ return result
+ return out
+ return decorator
+
+@debug(print_result=True)
+def add(x, y):
+ return x + y
+```
+* **Using only `'@debug'` to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can however manually check if the argument they received is a function and act accordingly.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Class
+-----
+```python
+class <name>:
+ def __init__(self, a):
+ self.a = a
+ def __repr__(self):
+ class_name = self.__class__.__name__
+ return f'{class_name}({self.a!r})'
+ def __str__(self):
+ return str(self.a)
+
+ @classmethod
+ def get_class_name(cls):
+ return cls.__name__
+```
+* **Return value of repr() should be unambiguous and of str() readable.**
+* **If only repr() is defined, it will also be used for str().**
+* **Methods decorated with `'@staticmethod'` do not receive 'self' nor 'cls' as their first arg.**
+
+#### Str() use cases:
+```python
+print(<el>)
+f'{<el>}'
+logging.warning(<el>)
+csv.writer(<file>).writerow([<el>])
+raise Exception(<el>)
+```
+
+#### Repr() use cases:
+```python
+print/str/repr([<el>])
+f'{<el>!r}'
+Z = dataclasses.make_dataclass('Z', ['a']); print/str/repr(Z(<el>))
+>>> <el>
+```
+
+### Constructor Overloading
+```python
+class <name>:
+ def __init__(self, a=None):
+ self.a = a
+```
+
+### Inheritance
+```python
+class Person:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+class Employee(Person):
+ def __init__(self, name, age, staff_num):
+ super().__init__(name, age)
+ self.staff_num = staff_num
+```
+
+### Multiple Inheritance
+```python
+class A: pass
+class B: pass
+class C(A, B): pass
+```
+
+**MRO determines the order in which parent classes are traversed when searching for a method or an attribute:**
+```python
+>>> C.mro()
+[<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]
+```
+
+### Property
+**Pythonic way of implementing getters and setters.**
+```python
+class Person:
+ @property
+ def name(self):
+ return ' '.join(self._name)
+
+ @name.setter
+ def name(self, value):
+ self._name = value.split()
+```
+
+```python
+>>> person = Person()
+>>> person.name = '\t Guido van Rossum \n'
+>>> person.name
+'Guido van Rossum'
+```
+
+### Dataclass
+**Decorator that automatically generates init(), repr() and eq() special methods.**
+```python
+from dataclasses import dataclass, field
+
+@dataclass(order=False, frozen=False)
+class <class_name>:
+ <attr_name_1>: <type>
+ <attr_name_2>: <type> = <default_value>
+ <attr_name_3>: list/dict/set = field(default_factory=list/dict/set)
+```
+* **Objects can be made sortable with `'order=True'` and immutable with `'frozen=True'`.**
+* **For object to be hashable, all attributes must be hashable and 'frozen' must be True.**
+* **Function field() is needed because `'<attr_name>: list = []'` would make a list that is shared among all instances. Its 'default_factory' argument can be any [callable](#callable).**
+* **For attributes of arbitrary type use `'typing.Any'`.**
+
+#### Inline:
+```python
+from dataclasses import make_dataclass
+<class> = make_dataclass('<class_name>', <coll_of_attribute_names>)
+<class> = make_dataclass('<class_name>', <coll_of_tuples>)
+<tuple> = ('<attr_name>', <type> [, <default_value>])
+```
+
+#### Rest of type annotations (CPython interpreter ignores them all):
+```python
+def func(<arg_name>: <type> [= <obj>]) -> <type>: ...
+<var_name>: typing.List/Set/Iterable/Sequence/Optional[<type>]
+<var_name>: typing.Dict/Tuple/Union[<type>, ...]
+```
+
+### Slots
+**Mechanism that restricts objects to attributes listed in 'slots' and significantly reduces their memory footprint.**
+
+```python
+class MyClassWithSlots:
+ __slots__ = ['a']
+ def __init__(self):
+ self.a = 1
+```
+
+### Copy
+```python
+from copy import copy, deepcopy
+<object> = copy(<object>)
+<object> = deepcopy(<object>)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Duck Types
+----------
+**A duck type is an implicit type that prescribes a set of special methods. Any object that has those methods defined is considered a member of that duck type.**
+
+### Comparable
+* **If eq() method is not overridden, it returns `'id(self) == id(other)'`, which is the same as `'self is other'`.**
+* **That means all objects compare not equal by default.**
+* **Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. False is returned if both return NotImplemented.**
+* **Ne() automatically works on any object that has eq() defined.**
+
+```python
+class MyComparable:
+ def __init__(self, a):
+ self.a = a
+ def __eq__(self, other):
+ if isinstance(other, type(self)):
+ return self.a == other.a
+ return NotImplemented
+```
+
+### Hashable
+* **Hashable object needs both hash() and eq() methods and its hash value should never change.**
+* **Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do.**
+* **That is why Python automatically makes classes unhashable if you only implement eq().**
+
+```python
+class MyHashable:
+ def __init__(self, a):
+ self._a = a
+ @property
+ def a(self):
+ return self._a
+ def __eq__(self, other):
+ if isinstance(other, type(self)):
+ return self.a == other.a
+ return NotImplemented
+ def __hash__(self):
+ return hash(self.a)
+```
+
+### Sortable
+* **With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods and the rest will be automatically generated.**
+* **Functions sorted() and min() only require lt() method, while max() only requires gt(). However, it is best to define them all so that confusion doesn't arise in other contexts.**
+* **When two lists, strings or dataclasses are compared, their values get compared in order until a pair of unequal values is found. The comparison of this two values is then returned. The shorter sequence is considered smaller in case of all values being equal.**
+
+```python
+from functools import total_ordering
+
+@total_ordering
+class MySortable:
+ def __init__(self, a):
+ self.a = a
+ def __eq__(self, other):
+ if isinstance(other, type(self)):
+ return self.a == other.a
+ return NotImplemented
+ def __lt__(self, other):
+ if isinstance(other, type(self)):
+ return self.a < other.a
+ return NotImplemented
+```
+
+### Iterator
+* **Any object that has methods next() and iter() is an iterator.**
+* **Next() should return next item or raise StopIteration.**
+* **Iter() should return 'self'.**
+```python
+class Counter:
+ def __init__(self):
+ self.i = 0
+ def __next__(self):
+ self.i += 1
+ return self.i
+ def __iter__(self):
+ return self
+```
+
+```python
+>>> counter = Counter()
+>>> next(counter), next(counter), next(counter)
+(1, 2, 3)
+```
+
+#### Python has many different iterator objects:
+* **Sequence iterators returned by the [iter()](#iterator) function, such as list\_iterator and set\_iterator.**
+* **Objects returned by the [itertools](#itertools) module, such as count, repeat and cycle.**
+* **Generators returned by the [generator functions](#generator) and [generator expressions](#comprehensions).**
+* **File objects returned by the [open()](#open) function, etc.**
+
+### Callable
+* **All functions and classes have a call() method, hence are callable.**
+* **When this cheatsheet uses `'<function>'` as an argument, it actually means `'<callable>'`.**
+```python
+class Counter:
+ def __init__(self):
+ self.i = 0
+ def __call__(self):
+ self.i += 1
+ return self.i
+```
+
+```python
+>>> counter = Counter()
+>>> counter(), counter(), counter()
+(1, 2, 3)
+```
+
+### Context Manager
+* **Enter() should lock the resources and optionally return an object.**
+* **Exit() should release the resources.**
+* **Any exception that happens inside the with block is passed to the exit() method.**
+* **If it wishes to suppress the exception it must return a true value.**
+```python
+class MyOpen:
+ def __init__(self, filename):
+ self.filename = filename
+ def __enter__(self):
+ self.file = open(self.filename)
+ return self.file
+ def __exit__(self, exc_type, exception, traceback):
+ self.file.close()
+```
+
+```python
+>>> with open('test.txt', 'w') as file:
+... file.write('Hello World!')
+>>> with MyOpen('test.txt') as file:
+... print(file.read())
+Hello World!
+```
+
+
+Iterable Duck Types
+-------------------
+### Iterable
+* **Only required method is iter(). It should return an iterator of object's items.**
+* **Contains() automatically works on any object that has iter() defined.**
+```python
+class MyIterable:
+ def __init__(self, a):
+ self.a = a
+ def __iter__(self):
+ return iter(self.a)
+ def __contains__(self, el):
+ return el in self.a
+```
+
+```python
+>>> obj = MyIterable([1, 2, 3])
+>>> [el for el in obj]
+[1, 2, 3]
+>>> 1 in obj
+True
+```
+
+### Collection
+* **Only required methods are iter() and len(). Len() should return the number of items.**
+* **This cheatsheet actually means `'<iterable>'` when it uses `'<collection>'`.**
+* **I chose not to use the name 'iterable' because it sounds scarier and more vague than 'collection'. The only drawback of this decision is that a reader could think a certain function doesn't accept iterators when it does, since iterators are the only built-in objects that are iterable but are not collections.**
+```python
+class MyCollection:
+ def __init__(self, a):
+ self.a = a
+ def __iter__(self):
+ return iter(self.a)
+ def __contains__(self, el):
+ return el in self.a
+ def __len__(self):
+ return len(self.a)
+```
+
+### Sequence
+* **Only required methods are len() and getitem().**
+* **Getitem() should return an item at the passed index or raise IndexError.**
+* **Iter() and contains() automatically work on any object that has getitem() defined.**
+* **Reversed() automatically works on any object that has len() and getitem() defined.**
+```python
+class MySequence:
+ def __init__(self, a):
+ self.a = a
+ def __iter__(self):
+ return iter(self.a)
+ def __contains__(self, el):
+ return el in self.a
+ def __len__(self):
+ return len(self.a)
+ def __getitem__(self, i):
+ return self.a[i]
+ def __reversed__(self):
+ return reversed(self.a)
+```
+
+#### Discrepancies between glossary definitions and abstract base classes:
+* **Glossary defines iterable as any object with iter() or getitem() and sequence as any object with getitem() and len(). It does not define collection.**
+* **Passing ABC Iterable to isinstance() or issubclass() checks whether object/class has method iter(), while ABC Collection checks for iter(), contains() and len().**
+
+### ABC Sequence
+* **It's a richer interface than the basic sequence.**
+* **Extending it generates iter(), contains(), reversed(), index() and count().**
+* **Unlike `'abc.Iterable'` and `'abc.Collection'`, it is not a duck type. That is why `'issubclass(MySequence, abc.Sequence)'` would return False even if MySequence had all the methods defined. It however recognizes list, tuple, range, str, bytes, bytearray, memoryview and deque, because they are registered as Sequence's virtual subclasses.**
+```python
+from collections import abc
+
+class MyAbcSequence(abc.Sequence):
+ def __init__(self, a):
+ self.a = a
+ def __len__(self):
+ return len(self.a)
+ def __getitem__(self, i):
+ return self.a[i]
+```
+
+#### Table of required and automatically available special methods:
+```text
++------------+------------+------------+------------+--------------+
+| | Iterable | Collection | Sequence | abc.Sequence |
++------------+------------+------------+------------+--------------+
+| iter() | REQ | REQ | Yes | Yes |
+| contains() | Yes | Yes | Yes | Yes |
+| len() | | REQ | REQ | REQ |
+| getitem() | | | REQ | REQ |
+| reversed() | | | Yes | Yes |
+| index() | | | | Yes |
+| count() | | | | Yes |
++------------+------------+------------+------------+--------------+
+```
+* **Other ABCs that generate missing methods are: MutableSequence, Set, MutableSet, Mapping and MutableMapping.**
+* **Names of their required methods are stored in `'<abc>.__abstractmethods__'`.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Enum
+----
+```python
+from enum import Enum, auto
+```
+
+```python
+class <enum_name>(Enum):
+ <member_name_1> = <value_1>
+ <member_name_2> = <value_2_a>, <value_2_b>
+ <member_name_3> = auto()
+```
+* **If there are no numeric values before auto(), it returns 1.**
+* **Otherwise it returns an increment of the last numeric value.**
+
+```python
+<member> = <enum>.<member_name> # Returns a member.
+<member> = <enum>['<member_name>'] # Returns a member or raises KeyError.
+<member> = <enum>(<value>) # Returns a member or raises ValueError.
+<str> = <member>.name # Returns member's name.
+<obj> = <member>.value # Returns member's value.
+```
+
+```python
+list_of_members = list(<enum>)
+member_names = [a.name for a in <enum>]
+member_values = [a.value for a in <enum>]
+random_member = random.choice(list(<enum>))
+```
+
+```python
+def get_next_member(member):
+ members = list(member.__class__)
+ index = (members.index(member) + 1) % len(members)
+ return members[index]
+```
+
+### Inline
+```python
+Cutlery = Enum('Cutlery', 'fork knife spoon')
+Cutlery = Enum('Cutlery', ['fork', 'knife', 'spoon'])
+Cutlery = Enum('Cutlery', {'fork': 1, 'knife': 2, 'spoon': 3})
+```
+
+#### User-defined functions cannot be values, so they must be wrapped:
+```python
+from functools import partial
+LogicOp = Enum('LogicOp', {'AND': partial(lambda l, r: l and r),
+ 'OR': partial(lambda l, r: l or r)})
+```
+* **Member names are in all caps because trying to access a member that is named after a reserved keyword raises SyntaxError.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Exceptions
+----------
+```python
+try:
+ <code>
+except <exception>:
+ <code>
+```
+
+### Complex Example
+```python
+try:
+ <code_1>
+except <exception_a>:
+ <code_2_a>
+except <exception_b>:
+ <code_2_b>
+else:
+ <code_2_c>
+finally:
+ <code_3>
+```
+* **Code inside the `'else'` block will only be executed if `'try'` block had no exceptions.**
+* **Code inside the `'finally'` block will always be executed (unless a signal is received).**
+
+### Catching Exceptions
+```python
+except <exception>: ...
+except <exception> as <name>: ...
+except (<exception>, [...]): ...
+except (<exception>, [...]) as <name>: ...
+```
+* **Also catches subclasses of the exception.**
+* **Use `'traceback.print_exc()'` to print the error message to stderr.**
+* **Use `'print(<name>)'` to print just the cause of the exception (its arguments).**
+* **Use `'logging.exception(<message>)'` to log the exception.**
+
+### Raising Exceptions
+```python
+raise <exception>
+raise <exception>()
+raise <exception>(<el> [, ...])
+```
+
+#### Re-raising caught exception:
+```python
+except <exception> as <name>:
+ ...
+ raise
+```
+
+### Exception Object
+```python
+arguments = <name>.args
+exc_type = <name>.__class__
+filename = <name>.__traceback__.tb_frame.f_code.co_filename
+func_name = <name>.__traceback__.tb_frame.f_code.co_name
+line = linecache.getline(filename, <name>.__traceback__.tb_lineno)
+traceback = ''.join(traceback.format_tb(<name>.__traceback__))
+error_msg = ''.join(traceback.format_exception(exc_type, <name>, <name>.__traceback__))
+```
+
+### Built-in Exceptions
+```text
+BaseException
+ +-- SystemExit # Raised by the sys.exit() function.
+ +-- KeyboardInterrupt # Raised when the user hits the interrupt key (ctrl-c).
+ +-- Exception # User-defined exceptions should be derived from this class.
+ +-- ArithmeticError # Base class for arithmetic errors.
+ | +-- ZeroDivisionError # Raised when dividing by zero.
+ +-- AssertionError # Raised by `assert <exp>` if expression returns false value.
+ +-- AttributeError # Raised when an attribute is missing.
+ +-- EOFError # Raised by input() when it hits end-of-file condition.
+ +-- LookupError # Raised when a look-up on a collection fails.
+ | +-- IndexError # Raised when a sequence index is out of range.
+ | +-- KeyError # Raised when a dictionary key or set element is missing.
+ +-- MemoryError # Out of memory. Could be too late to start deleting vars.
+ +-- NameError # Raised when an object is missing.
+ +-- OSError # Errors such as “file not found” or “disk full” (see Open).
+ | +-- FileNotFoundError # When a file or directory is requested but doesn't exist.
+ +-- RuntimeError # Raised by errors that don't fall into other categories.
+ | +-- RecursionError # Raised when the maximum recursion depth is exceeded.
+ +-- StopIteration # Raised by next() when run on an empty iterator.
+ +-- TypeError # Raised when an argument is of wrong type.
+ +-- ValueError # When an argument is of right type but inappropriate value.
+ +-- UnicodeError # Raised when encoding/decoding strings to/from bytes fails.
+```
+
+#### Collections and their exceptions:
+```text
++-----------+------------+------------+------------+
+| | List | Set | Dict |
++-----------+------------+------------+------------+
+| getitem() | IndexError | | KeyError |
+| pop() | IndexError | KeyError | KeyError |
+| remove() | ValueError | KeyError | |
+| index() | ValueError | | |
++-----------+------------+------------+------------+
+```
+
+#### Useful built-in exceptions:
+```python
+raise TypeError('Argument is of wrong type!')
+raise ValueError('Argument is of right type but inappropriate value!')
+raise RuntimeError('None of above!')
+```
+
+### User-defined Exceptions
+```python
+class MyError(Exception): pass
+class MyInputError(MyError): pass
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Exit
+----
+**Exits the interpreter by raising SystemExit exception.**
+```python
+import sys
+sys.exit() # Exits with exit code 0 (success).
+sys.exit(<el>) # Prints to stderr and exits with 1.
+sys.exit(<int>) # Exits with passed exit code.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Print
+-----
+```python
+print(<el_1>, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
+```
+* **Use `'file=sys.stderr'` for messages about errors.**
+* **Use `'flush=True'` to forcibly flush the stream.**
+
+### Pretty Print
+```python
+from pprint import pprint
+pprint(<collection>, width=80, depth=None, compact=False, sort_dicts=True)
+```
+* **Levels deeper than 'depth' get replaced by '...'.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Input
+-----
+**Reads a line from user input or pipe if present.**
+
+```python
+<str> = input(prompt=None)
+```
+* **Trailing newline gets stripped.**
+* **Prompt string is printed to the standard output before reading input.**
+* **Raises EOFError when user hits EOF (ctrl-d/ctrl-z⏎) or input stream gets exhausted.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Command Line Arguments
+----------------------
+```python
+import sys
+scripts_path = sys.argv[0]
+arguments = sys.argv[1:]
+```
+
+### Argument Parser
+```python
+from argparse import ArgumentParser, FileType
+p = ArgumentParser(description=<str>)
+p.add_argument('-<short_name>', '--<name>', action='store_true') # Flag.
+p.add_argument('-<short_name>', '--<name>', type=<type>) # Option.
+p.add_argument('<name>', type=<type>, nargs=1) # First argument.
+p.add_argument('<name>', type=<type>, nargs='+') # Remaining arguments.
+p.add_argument('<name>', type=<type>, nargs='*') # Optional arguments.
+args = p.parse_args() # Exits on error.
+value = args.<name>
+```
+
+* **Use `'help=<str>'` to set argument description that will be displayed in help message.**
+* **Use `'default=<el>'` to set the default value.**
+* **Use `'type=FileType(<mode>)'` for files. Accepts 'encoding', but 'newline' is None.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Open
+----
+**Opens the file and returns a corresponding file object.**
+
+```python
+<file> = open(<path>, mode='r', encoding=None, newline=None)
+```
+* **`'encoding=None'` means that the default encoding is used, which is platform dependent. Best practice is to use `'encoding="utf-8"'` whenever possible.**
+* **`'newline=None'` means all different end of line combinations are converted to '\n' on read, while on write all '\n' characters are converted to system's default line separator.**
+* **`'newline=""'` means no conversions take place, but input is still broken into chunks by readline() and readlines() on every '\n', '\r' and '\r\n'.**
+
+### Modes
+* **`'r'` - Read (default).**
+* **`'w'` - Write (truncate).**
+* **`'x'` - Write or fail if the file already exists.**
+* **`'a'` - Append.**
+* **`'w+'` - Read and write (truncate).**
+* **`'r+'` - Read and write from the start.**
+* **`'a+'` - Read and write from the end.**
+* **`'t'` - Text mode (default).**
+* **`'b'` - Binary mode (`'br'`, `'bw'`, `'bx'`, …).**
+
+### Exceptions
+* **`'FileNotFoundError'` can be raised when reading with `'r'` or `'r+'`.**
+* **`'FileExistsError'` can be raised when writing with `'x'`.**
+* **`'IsADirectoryError'` and `'PermissionError'` can be raised by any.**
+* **`'OSError'` is the parent class of all listed exceptions.**
+
+### File Object
+```python
+<file>.seek(0) # Moves to the start of the file.
+<file>.seek(offset) # Moves 'offset' chars/bytes from the start.
+<file>.seek(0, 2) # Moves to the end of the file.
+<bin_file>.seek(±offset, <anchor>) # Anchor: 0 start, 1 current position, 2 end.
+```
+
+```python
+<str/bytes> = <file>.read(size=-1) # Reads 'size' chars/bytes or until EOF.
+<str/bytes> = <file>.readline() # Returns a line or empty string/bytes on EOF.
+<list> = <file>.readlines() # Returns a list of remaining lines.
+<str/bytes> = next(<file>) # Returns a line using buffer. Do not mix.
+```
+
+```python
+<file>.write(<str/bytes>) # Writes a string or bytes object.
+<file>.writelines(<collection>) # Writes a coll. of strings or bytes objects.
+<file>.flush() # Flushes write buffer. Runs every 4096/8192 B.
+```
+* **Methods do not add or strip trailing newlines, even writelines().**
+
+### Read Text from File
+```python
+def read_file(filename):
+ with open(filename, encoding='utf-8') as file:
+ return file.readlines()
+```
+
+### Write Text to File
+```python
+def write_to_file(filename, text):
+ with open(filename, 'w', encoding='utf-8') as file:
+ file.write(text)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Paths
+-----
+```python
+from os import getcwd, path, listdir, scandir
+from glob import glob
+```
+
+```python
+<str> = getcwd() # Returns the current working directory.
+<str> = path.join(<path>, ...) # Joins two or more pathname components.
+<str> = path.abspath(<path>) # Returns absolute path.
+```
+
+```python
+<str> = path.basename(<path>) # Returns final component of the path.
+<str> = path.dirname(<path>) # Returns path without the final component.
+<tup.> = path.splitext(<path>) # Splits on last period of the final component.
+```
+
+```python
+<list> = listdir(path='.') # Returns filenames located at path.
+<list> = glob('<pattern>') # Returns paths matching the wildcard pattern.
+```
+
+```python
+<bool> = path.exists(<path>) # Or: <Path>.exists()
+<bool> = path.isfile(<path>) # Or: <DirEntry/Path>.is_file()
+<bool> = path.isdir(<path>) # Or: <DirEntry/Path>.is_dir()
+```
+
+```python
+<stat> = os.stat(<path>) # Or: <DirEntry/Path>.stat()
+<real> = <stat>.st_mtime/st_size/… # Modification time, size in bytes, …
+```
+
+### DirEntry
+**Unlike listdir(), scandir() returns DirEntry objects that cache isfile, isdir and on Windows also stat information, thus significantly increasing the performance of code that requires it.**
+
+```python
+<iter> = scandir(path='.') # Returns DirEntry objects located at path.
+<str> = <DirEntry>.path # Returns whole path as a string.
+<str> = <DirEntry>.name # Returns final component as a string.
+<file> = open(<DirEntry>) # Opens the file and returns a file object.
+```
+
+### Path Object
+```python
+from pathlib import Path
+```
+
+```python
+<Path> = Path(<path> [, ...]) # Accepts strings, Paths and DirEntry objects.
+<Path> = <path> / <path> [/ ...] # First or second path must be a Path object.
+```
+
+```python
+<Path> = Path() # Returns relative cwd. Also Path('.').
+<Path> = Path.cwd() # Returns absolute cwd. Also Path().resolve().
+<Path> = Path.home() # Returns user's home directory (absolute).
+<Path> = Path(__file__).resolve() # Returns script's path if cwd wasn't changed.
+```
+
+```python
+<Path> = <Path>.parent # Returns Path without the final component.
+<str> = <Path>.name # Returns final component as a string.
+<str> = <Path>.stem # Returns final component without extension.
+<str> = <Path>.suffix # Returns final component's extension.
+<tup.> = <Path>.parts # Returns all components as strings.
+```
+
+```python
+<iter> = <Path>.iterdir() # Returns directory contents as Path objects.
+<iter> = <Path>.glob('<pattern>') # Returns Paths matching the wildcard pattern.
+```
+
+```python
+<str> = str(<Path>) # Returns path as a string.
+<file> = open(<Path>) # Also <Path>.read/write_text/bytes().
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+OS Commands
+-----------
+```python
+import os, shutil, subprocess
+```
+
+```python
+os.chdir(<path>) # Changes the current working directory.
+os.mkdir(<path>, mode=0o777) # Creates a directory. Permissions are in octal.
+os.makedirs(<path>, mode=0o777) # Creates all path's dirs. Also: `exist_ok=False`.
+```
+
+```python
+shutil.copy(from, to) # Copies the file. 'to' can exist or be a dir.
+shutil.copytree(from, to) # Copies the directory. 'to' must not exist.
+```
+
+
+```python
+os.rename(from, to) # Renames/moves the file or directory.
+os.replace(from, to) # Same, but overwrites 'to' if it exists.
+```
+
+```python
+os.remove(<path>) # Deletes the file.
+os.rmdir(<path>) # Deletes the empty directory.
+shutil.rmtree(<path>) # Deletes the directory.
+```
+* **Paths can be either strings, Paths or DirEntry objects.**
+* **Functions report OS related errors by raising either OSError or one of its [subclasses](#exceptions-1).**
+
+### Shell Commands
+```python
+<pipe> = os.popen('<command>') # Executes command in sh/cmd. Returns its stdout pipe.
+<str> = <pipe>.read(size=-1) # Reads 'size' chars or until EOF. Also readline/s().
+<int> = <pipe>.close() # Closes the pipe. Returns None on success.
+```
+
+#### Sends '1 + 1' to the basic calculator and captures its output:
+```python
+>>> subprocess.run('bc', input='1 + 1\n', capture_output=True, text=True)
+CompletedProcess(args='bc', returncode=0, stdout='2\n', stderr='')
+```
+
+#### Sends test.in to the basic calculator running in standard mode and saves its output to test.out:
+```python
+>>> from shlex import split
+>>> os.popen('echo 1 + 1 > test.in')
+>>> subprocess.run(split('bc -s'), stdin=open('test.in'), stdout=open('test.out', 'w'))
+CompletedProcess(args=['bc', '-s'], returncode=0)
+>>> open('test.out').read()
+'2\n'
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+JSON
+----
+**Text file format for storing collections of strings and numbers.**
+
+```python
+import json
+<str> = json.dumps(<object>) # Converts object to JSON string.
+<object> = json.loads(<str>) # Converts JSON string to object.
+```
+
+### Read Object from JSON File
+```python
+def read_json_file(filename):
+ with open(filename, encoding='utf-8') as file:
+ return json.load(file)
+```
+
+### Write Object to JSON File
+```python
+def write_to_json_file(filename, an_object):
+ with open(filename, 'w', encoding='utf-8') as file:
+ json.dump(an_object, file, ensure_ascii=False, indent=2)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Pickle
+------
+**Binary file format for storing Python objects.**
+
+```python
+import pickle
+<bytes> = pickle.dumps(<object>) # Converts object to bytes object.
+<object> = pickle.loads(<bytes>) # Converts bytes object to object.
+```
+
+### Read Object from File
+```python
+def read_pickle_file(filename):
+ with open(filename, 'rb') as file:
+ return pickle.load(file)
+```
+
+### Write Object to File
+```python
+def write_to_pickle_file(filename, an_object):
+ with open(filename, 'wb') as file:
+ pickle.dump(an_object, file)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+CSV
+---
+**Text file format for storing spreadsheets.**
+
+```python
+import csv
+```
+
+### Read
+```python
+<reader> = csv.reader(<file>) # Also: `dialect='excel', delimiter=','`.
+<list> = next(<reader>) # Returns next row as a list of strings.
+<list> = list(<reader>) # Returns a list of remaining rows.
+```
+* **File must be opened with a `'newline=""'` argument, or newlines embedded inside quoted fields will not be interpreted correctly!**
+* **To print the spreadsheet to the console use [Tabulate](#table) library.**
+* **For XML and binary Excel files (xlsx, xlsm and xlsb) use [Pandas](#dataframe-plot-encode-decode) library.**
+
+### Write
+```python
+<writer> = csv.writer(<file>) # Also: `dialect='excel', delimiter=','`.
+<writer>.writerow(<collection>) # Encodes objects using `str(<el>)`.
+<writer>.writerows(<coll_of_coll>) # Appends multiple rows.
+```
+* **File must be opened with a `'newline=""'` argument, or '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings!**
+
+### Parameters
+* **`'dialect'` - Master parameter that sets the default values. String or a Dialect object.**
+* **`'delimiter'` - A one-character string used to separate fields.**
+* **`'quotechar'` - Character for quoting fields that contain special characters.**
+* **`'doublequote'` - Whether quotechars inside fields are/get doubled or escaped.**
+* **`'skipinitialspace'` - Is space character at the start of the field stripped by the reader.**
+* **`'lineterminator'` - How writer terminates rows. Reader is hardcoded to '\n', '\r', '\r\n'.**
+* **`'quoting'` - 0: As necessary, 1: All, 2: All but numbers which are read as floats, 3: None.**
+* **`'escapechar'` - Character for escaping quotechars if doublequote is False.**
+
+### Dialects
+```text
++------------------+--------------+--------------+--------------+
+| | excel | excel-tab | unix |
++------------------+--------------+--------------+--------------+
+| delimiter | ',' | '\t' | ',' |
+| quotechar | '"' | '"' | '"' |
+| doublequote | True | True | True |
+| skipinitialspace | False | False | False |
+| lineterminator | '\r\n' | '\r\n' | '\n' |
+| quoting | 0 | 0 | 1 |
+| escapechar | None | None | None |
++------------------+--------------+--------------+--------------+
+```
+
+### Read Rows from CSV File
+```python
+def read_csv_file(filename, dialect='excel'):
+ with open(filename, encoding='utf-8', newline='') as file:
+ return list(csv.reader(file, dialect))
+```
+
+### Write Rows to CSV File
+```python
+def write_to_csv_file(filename, rows, dialect='excel'):
+ with open(filename, 'w', encoding='utf-8', newline='') as file:
+ writer = csv.writer(file, dialect)
+ writer.writerows(rows)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+SQLite
+------
+**A server-less database engine that stores each database into a separate file.**
+
+```python
+import sqlite3
+<conn> = sqlite3.connect(<path>) # Opens existing or new file. Also ':memory:'.
+<conn>.close() # Closes the connection.
+```
+
+### Read
+```python
+<cursor> = <conn>.execute('<query>') # Can raise a subclass of sqlite3.Error.
+<tuple> = <cursor>.fetchone() # Returns next row. Also next(<cursor>).
+<list> = <cursor>.fetchall() # Returns remaining rows. Also list(<cursor>).
+```
+
+### Write
+```python
+<conn>.execute('<query>') # Can raise a subclass of sqlite3.Error.
+<conn>.commit() # Saves all changes since the last commit.
+<conn>.rollback() # Discards all changes since the last commit.
+```
+
+#### Or:
+```python
+with <conn>: # Exits the block with commit() or rollback(),
+ <conn>.execute('<query>') # depending on whether any exception occurred.
+```
+
+### Placeholders
+```python
+<conn>.execute('<query>', <list/tuple>) # Replaces '?'s in query with values.
+<conn>.execute('<query>', <dict/namedtuple>) # Replaces ':<key>'s with values.
+<conn>.executemany('<query>', <coll_of_above>) # Runs execute() multiple times.
+```
+* **Passed values can be of type str, int, float, bytes, None, bool, datetime.date or datetime.datetime.**
+* **Bools will be stored and returned as ints and dates as [ISO formatted strings](#encode).**
+
+### Example
+**Values are not actually saved in this example because `'conn.commit()'` is omitted!**
+```python
+>>> conn = sqlite3.connect('test.db')
+>>> conn.execute('CREATE TABLE person (person_id INTEGER PRIMARY KEY, name, height)')
+>>> conn.execute('INSERT INTO person VALUES (NULL, ?, ?)', ('Jean-Luc', 187)).lastrowid
+1
+>>> conn.execute('SELECT * FROM person').fetchall()
+[(1, 'Jean-Luc', 187)]
+```
+
+### SqlAlchemy
+```python
+# $ pip3 install sqlalchemy
+from sqlalchemy import create_engine, text
+<engine> = create_engine('<url>').connect() # Url: 'dialect://user:password@host/dbname'.
+<conn> = <engine>.connect() # Creates a connection. Also <conn>.close().
+<cursor> = <conn>.execute(text('<query>'), …) # Replaces ':<key>'s with keyword arguments.
+with <conn>.begin(): ... # Exits the block with commit or rollback.
+```
+
+```text
++------------+--------------+-----------+-----------------------------------+
+| Dialects | pip3 install | import | Dependencies |
++------------+--------------+-----------+-----------------------------------+
+| mysql | mysqlclient | MySQLdb | www.pypi.org/project/mysqlclient |
+| postgresql | psycopg2 | psycopg2 | www.psycopg.org/docs/install.html |
+| mssql | pyodbc | pyodbc | apt install g++ unixodbc-dev |
+| oracle | cx_oracle | cx_Oracle | Oracle Instant Client |
++------------+--------------+-----------+-----------------------------------+
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Bytes
+-----
+**Bytes object is an immutable sequence of single bytes. Mutable version is called bytearray.**
+
+```python
+<bytes> = b'<str>' # Only accepts ASCII characters and \x00-\xff.
+<int> = <bytes>[<index>] # Returns an int in range from 0 to 255.
+<bytes> = <bytes>[<slice>] # Returns bytes even if it has only one element.
+<bytes> = <bytes>.join(<coll_of_bytes>) # Joins elements using bytes as a separator.
+```
+
+### Encode
+```python
+<bytes> = bytes(<coll_of_ints>) # Ints must be in range from 0 to 255.
+<bytes> = bytes(<str>, 'utf-8') # Or: <str>.encode('utf-8')
+<bytes> = <int>.to_bytes(n_bytes, …) # `byteorder='little/big', signed=False`.
+<bytes> = bytes.fromhex('<hex>') # Hex pairs can be separated by whitespaces.
+```
+
+### Decode
+```python
+<list> = list(<bytes>) # Returns ints in range from 0 to 255.
+<str> = str(<bytes>, 'utf-8') # Or: <bytes>.decode('utf-8')
+<int> = int.from_bytes(<bytes>, …) # `byteorder='little/big', signed=False`.
+'<hex>' = <bytes>.hex() # Returns hex pairs. Accepts `sep=<str>`.
+```
+
+### Read Bytes from File
+```python
+def read_bytes(filename):
+ with open(filename, 'rb') as file:
+ return file.read()
+```
+
+### Write Bytes to File
+```python
+def write_bytes(filename, bytes_obj):
+ with open(filename, 'wb') as file:
+ file.write(bytes_obj)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Struct
+------
+* **Module that performs conversions between a sequence of numbers and a bytes object.**
+* **System’s type sizes, byte order, and alignment rules are used by default.**
+
+```python
+from struct import pack, unpack
+<bytes> = pack('<format>', <el_1> [, ...]) # Packages arguments into bytes object.
+<tuple> = unpack('<format>', <bytes>) # Use iter_unpack() for iterator of tuples.
+```
+
+```python
+>>> pack('>hhl', 1, 2, 3)
+b'\x00\x01\x00\x02\x00\x00\x00\x03'
+>>> unpack('>hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
+(1, 2, 3)
+```
+
+### Format
+#### For standard type sizes and manual alignment (padding) start format string with:
+* **`'='` - System's byte order (usually little-endian).**
+* **`'<'` - Little-endian.**
+* **`'>'` - Big-endian (also `'!'`).**
+
+#### Besides numbers, pack() and unpack() also support bytes objects as part of the sequence:
+* **`'c'` - A bytes object with a single element. For pad byte use `'x'`.**
+* **`'<n>s'` - A bytes object with n elements.**
+
+#### Integer types. Use a capital letter for unsigned type. Minimum and standard sizes are in brackets:
+* **`'b'` - char (1/1)**
+* **`'h'` - short (2/2)**
+* **`'i'` - int (2/4)**
+* **`'l'` - long (4/4)**
+* **`'q'` - long long (8/8)**
+
+#### Floating point types:
+* **`'f'` - float (4/4)**
+* **`'d'` - double (8/8)**
+
+**[🔼Back to Top](#content-outlines)**
+
+Array
+-----
+**List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Sizes and byte order are always determined by the system.**
+
+```python
+from array import array
+<array> = array('<typecode>', <collection>) # Array from collection of numbers.
+<array> = array('<typecode>', <bytes>) # Array from bytes object.
+<array> = array('<typecode>', <array>) # Treats array as a sequence of numbers.
+<bytes> = bytes(<array>) # Or: <array>.tobytes()
+<file>.write(<array>) # Writes array to the binary file.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Memory View
+-----------
+* **A sequence object that points to the memory of another object.**
+* **Each element can reference a single or multiple consecutive bytes, depending on format.**
+* **Order and number of elements can be changed with slicing.**
+* **Casting only works between char and other types and uses system's sizes.**
+* **Byte order is always determined by the system.**
+
+```python
+<mview> = memoryview(<bytes/bytearray/array>) # Immutable if bytes, else mutable.
+<real> = <mview>[<index>] # Returns an int or a float.
+<mview> = <mview>[<slice>] # Mview with rearranged elements.
+<mview> = <mview>.cast('<typecode>') # Casts memoryview to the new format.
+<mview>.release() # Releases the object's memory buffer.
+```
+
+### Decode
+```python
+<bytes> = bytes(<mview>) # Returns a new bytes object.
+<bytes> = <bytes>.join(<coll_of_mviews>) # Joins mviews using bytes object as sep.
+<array> = array('<typecode>', <mview>) # Treats mview as a sequence of numbers.
+<file>.write(<mview>) # Writes mview to the binary file.
+```
+
+```python
+<list> = list(<mview>) # Returns a list of ints or floats.
+<str> = str(<mview>, 'utf-8') # Treats mview as a bytes object.
+<int> = int.from_bytes(<mview>, …) # `byteorder='little/big', signed=False`.
+'<hex>' = <mview>.hex() # Treats mview as a bytes object.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Deque
+-----
+**A thread-safe list with efficient appends and pops from either side. Pronounced "deck".**
+
+```python
+from collections import deque
+<deque> = deque(<collection>, maxlen=None)
+```
+
+```python
+<deque>.appendleft(<el>) # Opposite element is dropped if full.
+<deque>.extendleft(<collection>) # Collection gets reversed.
+<el> = <deque>.popleft() # Raises IndexError if empty.
+<deque>.rotate(n=1) # Rotates elements to the right.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Threading
+---------
+* **CPython interpreter can only run a single thread at a time.**
+* **That is why using multiple threads won't result in a faster execution, unless at least one of the threads contains an I/O operation.**
+```python
+from threading import Thread, RLock, Semaphore, Event, Barrier
+from concurrent.futures import ThreadPoolExecutor
+```
+
+### Thread
+```python
+<Thread> = Thread(target=<function>) # Use `args=<collection>` to set the arguments.
+<Thread>.start() # Starts the thread.
+<bool> = <Thread>.is_alive() # Checks if the thread has finished executing.
+<Thread>.join() # Waits for the thread to finish.
+```
+* **Use `'kwargs=<dict>'` to pass keyword arguments to the function.**
+* **Use `'daemon=True'`, or the program will not be able to exit while the thread is alive.**
+
+### Lock
+```python
+<lock> = RLock() # Lock that can only be released by acquirer.
+<lock>.acquire() # Waits for the lock to be available.
+<lock>.release() # Makes the lock available again.
+```
+
+#### Or:
+```python
+with <lock>: # Enters the block by calling acquire(),
+ ... # and exits it with release().
+```
+
+### Semaphore, Event, Barrier
+```python
+<Semaphore> = Semaphore(value=1) # Lock that can be acquired by 'value' threads.
+<Event> = Event() # Method wait() blocks until set() is called.
+<Barrier> = Barrier(n_times) # Wait() blocks until it's called n_times.
+```
+
+### Thread Pool Executor
+* **Object that manages thread execution.**
+* **An object with the same interface called ProcessPoolExecutor provides true parallelism by running a separate interpreter in each process. All arguments must be [pickable](#pickle).**
+
+```python
+<Exec> = ThreadPoolExecutor(max_workers=None) # Or: `with ThreadPoolExecutor() as <name>: …`
+<Exec>.shutdown(wait=True) # Blocks until all threads finish executing.
+```
+
+```python
+<iter> = <Exec>.map(<func>, <args_1>, ...) # A multithreaded and non-lazy map().
+<Futr> = <Exec>.submit(<func>, <arg_1>, ...) # Starts a thread and returns its Future object.
+<bool> = <Futr>.done() # Checks if the thread has finished executing.
+<obj> = <Futr>.result() # Waits for thread to finish and returns result.
+```
+
+### Queue
+**A thread-safe FIFO queue. For LIFO queue use LifoQueue.**
+```python
+from queue import Queue
+<Queue> = Queue(maxsize=0)
+```
+
+```python
+<Queue>.put(<el>) # Blocks until queue stops being full.
+<Queue>.put_nowait(<el>) # Raises queue.Full exception if full.
+<el> = <Queue>.get() # Blocks until queue stops being empty.
+<el> = <Queue>.get_nowait() # Raises queue.Empty exception if empty.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Operator
+--------
+**Module of functions that provide the functionality of operators.**
+```python
+import operator as op
+<el> = op.add/sub/mul/truediv/floordiv/mod(<el>, <el>) # +, -, *, /, //, %
+<int/set> = op.and_/or_/xor(<int/set>, <int/set>) # &, |, ^
+<bool> = op.eq/ne/lt/le/gt/ge(<sortable>, <sortable>) # ==, !=, <, <=, >, >=
+<func> = op.itemgetter/attrgetter/methodcaller(<obj>) # [index/key], .name, .name()
+```
+
+```python
+elementwise_sum = map(op.add, list_a, list_b)
+sorted_by_second = sorted(<collection>, key=op.itemgetter(1))
+sorted_by_both = sorted(<collection>, key=op.itemgetter(1, 0))
+product_of_elems = functools.reduce(op.mul, <collection>)
+union_of_sets = functools.reduce(op.or_, <coll_of_sets>)
+first_element = op.methodcaller('pop', 0)(<list>)
+```
+* **Binary operators require objects to have and(), or(), xor() and invert() special methods, unlike logical operators that work on all types of objects.**
+* **Also: `'<bool> = <bool> &|^ <bool>'` and `'<int> = <bool> &|^ <int>'`.**
+
+**[🔼Back to Top](#content-outlines)**
+
+Introspection
+-------------
+**Inspecting code at runtime.**
+
+### Variables
+```python
+<list> = dir() # Names of local variables (incl. functions).
+<dict> = vars() # Dict of local variables. Also locals().
+<dict> = globals() # Dict of global variables.
+```
+
+### Attributes
+```python
+<list> = dir(<object>) # Names of object's attributes (incl. methods).
+<dict> = vars(<object>) # Dict of writable attributes. Also <obj>.__dict__.
+<bool> = hasattr(<object>, '<attr_name>') # Checks if getattr() raises an AttributeError.
+value = getattr(<object>, '<attr_name>') # Raises AttributeError if attribute is missing.
+setattr(<object>, '<attr_name>', value) # Only works on objects with '__dict__' attribute.
+delattr(<object>, '<attr_name>') # Same. Also `del <object>.<attr_name>`.
+```
+
+### Parameters
+```python
+<Sig> = inspect.signature(<function>) # Function's Signature object.
+<dict> = <Sig>.parameters # Dict of Parameter objects.
+<memb> = <Param>.kind # Member of ParameterKind enum.
+<obj> = <Param>.default # Default value or <Param>.empty.
+<type> = <Param>.annotation # Type or <Param>.empty.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Metaprogramming
+---------------
+**Code that generates code.**
+
+### Type
+**Type is the root class. If only passed an object it returns its type (class). Otherwise it creates a new class.**
+
+```python
+<class> = type('<class_name>', <tuple_of_parents>, <dict_of_class_attributes>)
+```
+
+```python
+>>> Z = type('Z', (), {'a': 'abcde', 'b': 12345})
+>>> z = Z()
+```
+
+### Meta Class
+**A class that creates classes.**
+
+```python
+def my_meta_class(name, parents, attrs):
+ attrs['a'] = 'abcde'
+ return type(name, parents, attrs)
+```
+
+#### Or:
+```python
+class MyMetaClass(type):
+ def __new__(cls, name, parents, attrs):
+ attrs['a'] = 'abcde'
+ return type.__new__(cls, name, parents, attrs)
+```
+* **New() is a class method that gets called before init(). If it returns an instance of its class, then that instance gets passed to init() as a 'self' argument.**
+* **It receives the same arguments as init(), except for the first one that specifies the desired type of the returned instance (MyMetaClass in our case).**
+* **Like in our case, new() can also be called directly, usually from a new() method of a child class (**`def __new__(cls): return super().__new__(cls)`**).**
+* **The only difference between the examples above is that my\_meta\_class() returns a class of type type, while MyMetaClass() returns a class of type MyMetaClass.**
+
+### Metaclass Attribute
+**Right before a class is created it checks if it has the 'metaclass' attribute defined. If not, it recursively checks if any of his parents has it defined and eventually comes to type().**
+
+```python
+class MyClass(metaclass=MyMetaClass):
+ b = 12345
+```
+
+```python
+>>> MyClass.a, MyClass.b
+('abcde', 12345)
+```
+
+### Type Diagram
+```python
+type(MyClass) == MyMetaClass # MyClass is an instance of MyMetaClass.
+type(MyMetaClass) == type # MyMetaClass is an instance of type.
+```
+
+```text
++-------------+-------------+
+| Classes | Metaclasses |
++-------------+-------------|
+| MyClass --> MyMetaClass |
+| | v |
+| object -----> type <+ |
+| | ^ +--+ |
+| str ----------+ |
++-------------+-------------+
+```
+
+### Inheritance Diagram
+```python
+MyClass.__base__ == object # MyClass is a subclass of object.
+MyMetaClass.__base__ == type # MyMetaClass is a subclass of type.
+```
+
+```text
++-------------+-------------+
+| Classes | Metaclasses |
++-------------+-------------|
+| MyClass | MyMetaClass |
+| v | v |
+| object <----- type |
+| ^ | |
+| str | |
++-------------+-------------+
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Eval
+----
+```python
+>>> from ast import literal_eval
+>>> literal_eval('[1, 2, 3]')
+[1, 2, 3]
+>>> literal_eval('1 + 2')
+ValueError: malformed node or string
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Coroutines
+----------
+* **Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t use as much memory.**
+* **Coroutine definition starts with `'async'` and its call with `'await'`.**
+* **`'asyncio.run(<coroutine>)'` is the main entry point for asynchronous programs.**
+* **Functions wait(), gather() and as_completed() start multiple coroutines at the same time.**
+* **Asyncio module also provides its own [Queue](#queue), [Event](#semaphore-event-barrier), [Lock](#lock) and [Semaphore](#semaphore-event-barrier) classes.**
+
+#### Runs a terminal game where you control an asterisk that must avoid numbers:
+
+```python
+import asyncio, collections, curses, curses.textpad, enum, random
+
+P = collections.namedtuple('P', 'x y') # Position
+D = enum.Enum('D', 'n e s w') # Direction
+W, H = 15, 7 # Width, Height
+
+def main(screen):
+ curses.curs_set(0) # Makes cursor invisible.
+ screen.nodelay(True) # Makes getch() non-blocking.
+ asyncio.run(main_coroutine(screen)) # Starts running asyncio code.
+
+async def main_coroutine(screen):
+ moves = asyncio.Queue()
+ state = {'*': P(0, 0), **{id_: P(W//2, H//2) for id_ in range(10)}}
+ ai = [random_controller(id_, moves) for id_ in range(10)]
+ mvc = [human_controller(screen, moves), model(moves, state), view(state, screen)]
+ tasks = [asyncio.create_task(cor) for cor in ai + mvc]
+ await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
+
+async def random_controller(id_, moves):
+ while True:
+ d = random.choice(list(D))
+ moves.put_nowait((id_, d))
+ await asyncio.sleep(random.triangular(0.01, 0.65))
+
+async def human_controller(screen, moves):
+ while True:
+ ch = screen.getch()
+ key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e}
+ if ch in key_mappings:
+ moves.put_nowait(('*', key_mappings[ch]))
+ await asyncio.sleep(0.005)
+
+async def model(moves, state):
+ while state['*'] not in (state[id_] for id_ in range(10)):
+ id_, d = await moves.get()
+ x, y = state[id_]
+ deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
+ state[id_] = P((x + deltas[d].x) % W, (y + deltas[d].y) % H)
+
+async def view(state, screen):
+ offset = P(curses.COLS//2 - W//2, curses.LINES//2 - H//2)
+ while True:
+ screen.erase()
+ curses.textpad.rectangle(screen, offset.y-1, offset.x-1, offset.y+H, offset.x+W)
+ for id_, p in state.items():
+ screen.addstr(offset.y + (p.y - state['*'].y + H//2) % H,
+ offset.x + (p.x - state['*'].x + W//2) % W, str(id_))
+ await asyncio.sleep(0.005)
+
+if __name__ == '__main__':
+ curses.wrapper(main)
+```
+<br>
+
+**[🔼Back to Top](#content-outlines)**
+
+Libraries
+=========
+
+Progress Bar
+------------
+```python
+# $ pip3 install tqdm
+>>> from tqdm import tqdm
+>>> from time import sleep
+>>> for el in tqdm([1, 2, 3], desc='Processing'):
+... sleep(1)
+Processing: 100%|████████████████████| 3/3 [00:03<00:00, 1.00s/it]
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Plot
+----
+```python
+# $ pip3 install matplotlib
+import matplotlib.pyplot as plt
+plt.plot(<x_data>, <y_data> [, label=<str>]) # Or: plt.plot(<y_data>)
+plt.legend() # Adds a legend.
+plt.savefig(<path>) # Saves the figure.
+plt.show() # Displays the figure.
+plt.clf() # Clears the figure.
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Table
+-----
+#### Prints a CSV file as an ASCII table:
+```python
+# $ pip3 install tabulate
+import csv, tabulate
+with open('test.csv', encoding='utf-8', newline='') as file:
+ rows = csv.reader(file)
+ header = next(rows)
+ table = tabulate.tabulate(rows, header)
+print(table)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Curses
+------
+#### Runs a basic file explorer in the terminal:
+```python
+import curses, curses.ascii, os
+from curses import A_REVERSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_ENTER
+
+def main(screen):
+ ch, first, selected, paths = 0, 0, 0, os.listdir()
+ while ch != curses.ascii.ESC:
+ height, _ = screen.getmaxyx()
+ screen.erase()
+ for y, filename in enumerate(paths[first : first+height]):
+ screen.addstr(y, 0, filename, A_REVERSE * (selected == first + y))
+ ch = screen.getch()
+ selected += (ch == KEY_DOWN) - (ch == KEY_UP)
+ selected = max(0, min(len(paths)-1, selected))
+ first += (first <= selected - height) - (first > selected)
+ if ch in [KEY_LEFT, KEY_RIGHT, KEY_ENTER, 10, 13]:
+ new_dir = '..' if ch == KEY_LEFT else paths[selected]
+ if os.path.isdir(new_dir):
+ os.chdir(new_dir)
+ first, selected, paths = 0, 0, os.listdir()
+
+if __name__ == '__main__':
+ curses.wrapper(main)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Logging
+-------
+```python
+# $ pip3 install loguru
+from loguru import logger
+```
+
+```python
+logger.add('debug_{time}.log', colorize=True) # Connects a log file.
+logger.add('error_{time}.log', level='ERROR') # Another file for errors or higher.
+logger.<level>('A logging message.') # Logs to file/s and prints to stderr.
+```
+* **Levels: `'debug'`, `'info'`, `'success'`, `'warning'`, `'error'`, `'critical'`.**
+
+### Exceptions
+**Exception description, stack trace and values of variables are appended automatically.**
+
+```python
+try:
+ ...
+except <exception>:
+ logger.exception('An error happened.')
+```
+
+### Rotation
+**Argument that sets a condition when a new log file is created.**
+```python
+rotation=<int>|<datetime.timedelta>|<datetime.time>|<str>
+```
+* **`'<int>'` - Max file size in bytes.**
+* **`'<timedelta>'` - Max age of a file.**
+* **`'<time>'` - Time of day.**
+* **`'<str>'` - Any of above as a string: `'100 MB'`, `'1 month'`, `'monday at 12:00'`, ...**
+
+### Retention
+**Sets a condition which old log files get deleted.**
+```python
+retention=<int>|<datetime.timedelta>|<str>
+```
+* **`'<int>'` - Max number of files.**
+* **`'<timedelta>'` - Max age of a file.**
+* **`'<str>'` - Max age as a string: `'1 week, 3 days'`, `'2 months'`, ...**
+
+**[🔼Back to Top](#content-outlines)**
+
+Scraping
+--------
+#### Scrapes Python's URL, version number and logo from its Wikipedia page:
+```python
+# $ pip3 install requests beautifulsoup4
+import requests, bs4, os, sys
+
+WIKI_URL = 'https://en.wikipedia.org/wiki/Python_(programming_language)'
+try:
+ html = requests.get(WIKI_URL).text
+ document = bs4.BeautifulSoup(html, 'html.parser')
+ table = document.find('table', class_='infobox vevent')
+ python_url = table.find('th', text='Website').next_sibling.a['href']
+ version = table.find('th', text='Stable release').next_sibling.strings.__next__()
+ logo_url = table.find('img')['src']
+ logo = requests.get(f'https:{logo_url}').content
+ filename = os.path.basename(logo_url)
+ with open(filename, 'wb') as file:
+ file.write(logo)
+ print(f'{python_url}, {version}, file://{os.path.abspath(filename)}')
+except requests.exceptions.ConnectionError:
+ print("You've got problems with connection.", file=sys.stderr)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Web
+---
+```python
+# $ pip3 install bottle
+from bottle import run, route, static_file, template, post, request, response
+import json
+```
+
+### Run
+```python
+run(host='localhost', port=8080) # Runs locally.
+run(host='0.0.0.0', port=80) # Runs globally.
+```
+
+### Static Request
+```python
+@route('/img/<filename>')
+def send_file(filename):
+ return static_file(filename, root='img_dir/')
+```
+
+### Dynamic Request
+```python
+@route('/<sport>')
+def send_html(sport):
+ return template('<h1>{{title}}</h1>', title=sport)
+```
+
+### REST Request
+```python
+@post('/<sport>/odds')
+def send_json(sport):
+ team = request.forms.get('team')
+ response.headers['Content-Type'] = 'application/json'
+ response.headers['Cache-Control'] = 'no-cache'
+ return json.dumps({'team': team, 'odds': [2.09, 3.74, 3.68]})
+```
+
+#### Test:
+```python
+# $ pip3 install requests
+>>> import threading, requests
+>>> threading.Thread(target=run, daemon=True).start()
+>>> url = 'http://localhost:8080/football/odds'
+>>> request_data = {'team': 'arsenal f.c.'}
+>>> response = requests.post(url, data=request_data)
+>>> response.json()
+{'team': 'arsenal f.c.', 'odds': [2.09, 3.74, 3.68]}
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Profiling
+---------
+### Stopwatch
+```python
+from time import perf_counter
+start_time = perf_counter()
+...
+duration_in_seconds = perf_counter() - start_time
+```
+
+### Timing a Snippet
+```python
+>>> from timeit import timeit
+>>> timeit("''.join(str(i) for i in range(100))",
+... number=10000, globals=globals(), setup='pass')
+0.34986
+```
+
+### Profiling by Line
+```python
+# $ pip3 install line_profiler memory_profiler
+@profile
+def main():
+ a = [*range(10000)]
+ b = {*range(10000)}
+main()
+```
+
+```text
+$ kernprof -lv test.py
+Line # Hits Time Per Hit % Time Line Contents
+=======================================================
+ 1 @profile
+ 2 def main():
+ 3 1 955.0 955.0 43.7 a = [*range(10000)]
+ 4 1 1231.0 1231.0 56.3 b = {*range(10000)}
+```
+
+```text
+$ python3 -m memory_profiler test.py
+Line # Mem usage Increment Line Contents
+=======================================================
+ 1 37.668 MiB 37.668 MiB @profile
+ 2 def main():
+ 3 38.012 MiB 0.344 MiB a = [*range(10000)]
+ 4 38.477 MiB 0.465 MiB b = {*range(10000)}
+```
+
+### Call Graph
+#### Generates a PNG image of the call graph with highlighted bottlenecks:
+```python
+# $ pip3 install pycallgraph2; apt/brew install graphviz
+import pycallgraph2 as cg, datetime
+
+filename = f'profile-{datetime.datetime.now():%Y%m%d_%H%M%S}.png'
+drawer = cg.output.GraphvizOutput(output_file=filename)
+with cg.PyCallGraph(drawer):
+ <code_to_be_profiled>
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+NumPy
+-----
+**Array manipulation mini-language. It can run up to one hundred times faster than the equivalent Python code. An even faster alternative that runs on a GPU is called CuPy.**
+
+```python
+# $ pip3 install numpy
+import numpy as np
+```
+
+```python
+<array> = np.array(<list/list_of_lists>) # Returns 1d/2d NumPy array.
+<array> = np.zeros/ones(<shape>) # Also np.full(<shape>, <el>).
+<array> = np.arange(from_inc, to_exc, ±step) # Also np.linspace(start, stop, num).
+<array> = np.random.randint(from_inc, to_exc, <shape>) # Also np.random.random(<shape>).
+```
+
+```python
+<view> = <array>.reshape(<shape>) # Also `<array>.shape = <shape>`.
+<array> = <array>.flatten() # Collapses array into one dimension.
+<view> = <array>.squeeze() # Removes dimensions of length one.
+```
+
+```python
+<array> = <array>.sum/min/mean/var/std(axis) # Passed dimension gets aggregated.
+<array> = <array>.argmin(axis) # Returns indexes of smallest elements.
+<array> = np.apply_along_axis(<func>, axis, <array>) # Func can return a scalar or array.
+```
+
+* **Shape is a tuple of dimension sizes. A 100x50 RGB image has shape (50, 100, 3).**
+* **Axis is an index of the dimension that gets aggregated. Leftmost dimension has index 0. Summing the RGB image along axis 2 will return a greyscale image with shape (50, 100).**
+* **Passing a tuple of axes will chain the operations like this: `'<array>.<method>(axis_1, keepdims=True).<method>(axis_2).squeeze()'`.**
+
+### Indexing
+```bash
+<el> = <2d_array>[row_index, column_index] # <3d_a>[table_i, row_i, column_i]
+<1d_view> = <2d_array>[row_index] # <3d_a>[table_i, row_i]
+<1d_view> = <2d_array>[:, column_index] # <3d_a>[table_i, :, column_i]
+```
+
+```bash
+<1d_array> = <2d_array>[row_indexes, column_indexes] # <3d_a>[table_is, row_is, column_is]
+<2d_array> = <2d_array>[row_indexes] # <3d_a>[table_is, row_is]
+<2d_array> = <2d_array>[:, column_indexes] # <3d_a>[table_is, :, column_is]
+```
+
+```bash
+<2d_bools> = <2d_array> ><== <el> # <3d_array> ><== <1d_array>
+<1d_array> = <2d_array>[<2d_bools>] # <3d_array>[<2d_bools>]
+```
+* **All examples also allow assignments.**
+
+### Broadcasting
+**Broadcasting is a set of rules by which NumPy functions operate on arrays of different sizes and/or dimensions.**
+
+```python
+left = [[0.1], [0.6], [0.8]] # Shape: (3, 1)
+right = [ 0.1 , 0.6 , 0.8 ] # Shape: (3,)
+```
+
+#### 1. If array shapes differ in length, left-pad the shorter shape with ones:
+```python
+left = [[0.1], [0.6], [0.8]] # Shape: (3, 1)
+right = [[0.1 , 0.6 , 0.8]] # Shape: (1, 3) <- !
+```
+
+#### 2. If any dimensions differ in size, expand the ones that have size 1 by duplicating their elements:
+```python
+left = [[0.1, 0.1, 0.1], # Shape: (3, 3) <- !
+ [0.6, 0.6, 0.6],
+ [0.8, 0.8, 0.8]]
+
+right = [[0.1, 0.6, 0.8], # Shape: (3, 3) <- !
+ [0.1, 0.6, 0.8],
+ [0.1, 0.6, 0.8]]
+```
+
+#### 3. If neither non-matching dimension has size 1, raise an error.
+
+
+### Example
+#### For each point returns index of its nearest point (`[0.1, 0.6, 0.8] => [1, 2, 1]`):
+
+```python
+>>> points = np.array([0.1, 0.6, 0.8])
+ [ 0.1, 0.6, 0.8]
+>>> wrapped_points = points.reshape(3, 1)
+[[ 0.1],
+ [ 0.6],
+ [ 0.8]]
+>>> distances = wrapped_points - points
+[[ 0. , -0.5, -0.7],
+ [ 0.5, 0. , -0.2],
+ [ 0.7, 0.2, 0. ]]
+>>> distances = np.abs(distances)
+[[ 0. , 0.5, 0.7],
+ [ 0.5, 0. , 0.2],
+ [ 0.7, 0.2, 0. ]]
+>>> i = np.arange(3)
+[0, 1, 2]
+>>> distances[i, i] = np.inf
+[[ inf, 0.5, 0.7],
+ [ 0.5, inf, 0.2],
+ [ 0.7, 0.2, inf]]
+>>> distances.argmin(1)
+[1, 2, 1]
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Image
+-----
+```python
+# $ pip3 install pillow
+from PIL import Image
+```
+
+```python
+<Image> = Image.new('<mode>', (width, height)) # Also: `color=<int/tuple/str>`.
+<Image> = Image.open(<path>) # Identifies format based on file contents.
+<Image> = <Image>.convert('<mode>') # Converts image to the new mode.
+<Image>.save(<path>) # Selects format based on the path extension.
+<Image>.show() # Opens image in default preview app.
+```
+
+```python
+<int/tuple> = <Image>.getpixel((x, y)) # Returns a pixel.
+<Image>.putpixel((x, y), <int/tuple>) # Writes a pixel to the image.
+<ImagingCore> = <Image>.getdata() # Returns a flattened sequence of pixels.
+<Image>.putdata(<list/ImagingCore>) # Writes a flattened sequence of pixels.
+<Image>.paste(<Image>, (x, y)) # Writes passed image to the image.
+```
+
+```bash
+<2d_array> = np.array(<Image_L>) # Creates NumPy array from greyscale image.
+<3d_array> = np.array(<Image_RGB/A>) # Creates NumPy array from color image.
+<Image> = Image.fromarray(np.uint8(<array>)) # Use <array>.clip(0, 255) to clip the values.
+```
+
+### Modes
+* **`'1'` - 1-bit pixels, black and white, stored with one pixel per byte.**
+* **`'L'` - 8-bit pixels, greyscale.**
+* **`'RGB'` - 3x8-bit pixels, true color.**
+* **`'RGBA'` - 4x8-bit pixels, true color with transparency mask.**
+* **`'HSV'` - 3x8-bit pixels, Hue, Saturation, Value color space.**
+
+### Examples
+#### Creates a PNG image of a rainbow gradient:
+```python
+WIDTH, HEIGHT = 100, 100
+n_pixels = WIDTH * HEIGHT
+hues = (255 * i/n_pixels for i in range(n_pixels))
+img = Image.new('HSV', (WIDTH, HEIGHT))
+img.putdata([(int(h), 255, 255) for h in hues])
+img.convert('RGB').save('test.png')
+```
+
+#### Adds noise to a PNG image:
+```python
+from random import randint
+add_noise = lambda value: max(0, min(255, value + randint(-20, 20)))
+img = Image.open('test.png').convert('HSV')
+img.putdata([(add_noise(h), s, v) for h, s, v in img.getdata()])
+img.convert('RGB').save('test.png')
+```
+
+### Image Draw
+```python
+from PIL import ImageDraw
+<ImageDraw> = ImageDraw.Draw(<Image>)
+```
+
+```python
+<ImageDraw>.point((x, y)) # Truncates floats into ints.
+<ImageDraw>.line((x1, y1, x2, y2 [, ...])) # To get anti-aliasing use Image's resize().
+<ImageDraw>.arc((x1, y1, x2, y2), deg1, deg2) # Always draws in clockwise direction.
+<ImageDraw>.rectangle((x1, y1, x2, y2)) # To rotate use Image's rotate() and paste().
+<ImageDraw>.polygon((x1, y1, x2, y2, ...)) # Last point gets connected to the first.
+<ImageDraw>.ellipse((x1, y1, x2, y2)) # To rotate use Image's rotate() and paste().
+```
+* **Use `'fill=<color>'` to set the primary color.**
+* **Use `'width=<int>'` to set the width of lines or contours.**
+* **Use `'outline=<color>'` to set the color of the contours.**
+* **Color can be an int, tuple, `'#rrggbb[aa]'` string or a color name.**
+
+
+Animation
+---------
+#### Creates a GIF of a bouncing ball:
+```python
+# $ pip3 install imageio
+from PIL import Image, ImageDraw
+import imageio
+
+WIDTH, HEIGHT, R = 126, 126, 10
+frames = []
+for velocity in range(1, 16):
+ y = sum(range(velocity))
+ frame = Image.new('L', (WIDTH, HEIGHT))
+ draw = ImageDraw.Draw(frame)
+ draw.ellipse((WIDTH/2-R, y, WIDTH/2+R, y+R*2), fill='white')
+ frames.append(frame)
+frames += reversed(frames[1:-1])
+imageio.mimsave('test.gif', frames, duration=0.03)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Audio
+-----
+```python
+import wave
+```
+
+```python
+<Wave_read> = wave.open('<path>', 'rb') # Opens the WAV file.
+framerate = <Wave_read>.getframerate() # Number of frames per second.
+nchannels = <Wave_read>.getnchannels() # Number of samples per frame.
+sampwidth = <Wave_read>.getsampwidth() # Sample size in bytes.
+nframes = <Wave_read>.getnframes() # Number of frames.
+<params> = <Wave_read>.getparams() # Immutable collection of above.
+<bytes> = <Wave_read>.readframes(nframes) # Returns next 'nframes' frames.
+```
+
+```python
+<Wave_write> = wave.open('<path>', 'wb') # Truncates existing file.
+<Wave_write>.setframerate(<int>) # 44100 for CD, 48000 for video.
+<Wave_write>.setnchannels(<int>) # 1 for mono, 2 for stereo.
+<Wave_write>.setsampwidth(<int>) # 2 for CD quality sound.
+<Wave_write>.setparams(<params>) # Sets all parameters.
+<Wave_write>.writeframes(<bytes>) # Appends frames to the file.
+```
+* **Bytes object contains a sequence of frames, each consisting of one or more samples.**
+* **In a stereo signal, the first sample of a frame belongs to the left channel.**
+* **Each sample consists of one or more bytes that, when converted to an integer, indicate the displacement of a speaker membrane at a given moment.**
+* **If sample width is one byte, then the integer should be encoded unsigned.**
+* **For all other sizes, the integer should be encoded signed with little-endian byte order.**
+
+### Sample Values
+```text
++-----------+-----------+------+-----------+
+| sampwidth | min | zero | max |
++-----------+-----------+------+-----------+
+| 1 | 0 | 128 | 255 |
+| 2 | -32768 | 0 | 32767 |
+| 3 | -8388608 | 0 | 8388607 |
++-----------+-----------+------+-----------+
+```
+
+### Read Float Samples from WAV File
+```python
+def read_wav_file(filename):
+ def get_int(bytes_obj):
+ an_int = int.from_bytes(bytes_obj, 'little', signed=(sampwidth != 1))
+ return an_int - 128 * (sampwidth == 1)
+ with wave.open(filename, 'rb') as file:
+ sampwidth = file.getsampwidth()
+ frames = file.readframes(-1)
+ bytes_samples = (frames[i : i+sampwidth] for i in range(0, len(frames), sampwidth))
+ return [get_int(b) / pow(2, sampwidth * 8 - 1) for b in bytes_samples]
+```
+
+### Write Float Samples to WAV File
+```python
+def write_to_wav_file(filename, float_samples, nchannels=1, sampwidth=2, framerate=44100):
+ def get_bytes(a_float):
+ a_float = max(-1, min(1 - 2e-16, a_float))
+ a_float += sampwidth == 1
+ a_float *= pow(2, sampwidth * 8 - 1)
+ return int(a_float).to_bytes(sampwidth, 'little', signed=(sampwidth != 1))
+ with wave.open(filename, 'wb') as file:
+ file.setnchannels(nchannels)
+ file.setsampwidth(sampwidth)
+ file.setframerate(framerate)
+ file.writeframes(b''.join(get_bytes(f) for f in float_samples))
+```
+
+### Examples
+#### Saves a 440 Hz sine wave to a mono WAV file:
+```python
+from math import pi, sin
+samples_f = (sin(i * 2 * pi * 440 / 44100) for i in range(100000))
+write_to_wav_file('test.wav', samples_f)
+```
+
+#### Adds noise to a mono WAV file:
+```python
+from random import random
+add_noise = lambda value: value + (random() - 0.5) * 0.03
+samples_f = (add_noise(f) for f in read_wav_file('test.wav'))
+write_to_wav_file('test.wav', samples_f)
+```
+
+#### Plays a WAV file:
+```python
+# $ pip3 install simpleaudio
+from simpleaudio import play_buffer
+with wave.open('test.wav', 'rb') as file:
+ p = file.getparams()
+ frames = file.readframes(-1)
+ play_buffer(frames, p.nchannels, p.sampwidth, p.framerate)
+```
+
+### Text to Speech
+```python
+# $ pip3 install pyttsx3
+import pyttsx3
+engine = pyttsx3.init()
+engine.say('Sally sells seashells by the seashore.')
+engine.runAndWait()
+```
+
+
+Synthesizer
+-----------
+#### Plays Popcorn by Gershon Kingsley:
+```python
+# $ pip3 install simpleaudio
+import itertools as it, math, struct, simpleaudio
+
+F = 44100
+P1 = '71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,'
+P2 = '71♩,73♪,,74♩,73♪,,74♪,,71♪,,73♩,71♪,,73♪,,69♪,,71♩,69♪,,71♪,,67♪,,71♩,,'
+get_pause = lambda seconds: it.repeat(0, int(seconds * F))
+sin_f = lambda i, hz: math.sin(i * 2 * math.pi * hz / F)
+get_wave = lambda hz, seconds: (sin_f(i, hz) for i in range(int(seconds * F)))
+get_hz = lambda key: 8.176 * 2 ** (int(key) / 12)
+parse_note = lambda note: (get_hz(note[:2]), 1/4 if '♩' in note else 1/8)
+get_samples = lambda note: get_wave(*parse_note(note)) if note else get_pause(1/8)
+samples_f = it.chain.from_iterable(get_samples(n) for n in f'{P1},{P1},{P2}'.split(','))
+samples_b = b''.join(struct.pack('<h', int(f * 30000)) for f in samples_f)
+simpleaudio.play_buffer(samples_b, 1, 2, F)
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Pygame
+------
+```python
+# $ pip3 install pygame
+import pygame as pg
+
+pg.init()
+screen = pg.display.set_mode((500, 500))
+rect = pg.Rect(240, 240, 20, 20)
+while all(event.type != pg.QUIT for event in pg.event.get()):
+ deltas = {pg.K_UP: (0, -1), pg.K_RIGHT: (1, 0), pg.K_DOWN: (0, 1), pg.K_LEFT: (-1, 0)}
+ for ch, is_pressed in enumerate(pg.key.get_pressed()):
+ rect = rect.move(deltas[ch]) if ch in deltas and is_pressed else rect
+ screen.fill((0, 0, 0))
+ pg.draw.rect(screen, (255, 255, 255), rect)
+ pg.display.flip()
+```
+
+### Rectangle
+**Object for storing rectangular coordinates.**
+```python
+<Rect> = pg.Rect(x, y, width, height) # Floats get truncated into ints.
+<int> = <Rect>.x/y/centerx/centery/… # Top, right, bottom, left. Allows assignments.
+<tup.> = <Rect>.topleft/center/… # Topright, bottomright, bottomleft. Same.
+<Rect> = <Rect>.move((x, y)) # Use move_ip() to move in-place.
+```
+
+```python
+<bool> = <Rect>.collidepoint((x, y)) # Checks if rectangle contains a point.
+<bool> = <Rect>.colliderect(<Rect>) # Checks if two rectangles overlap.
+<int> = <Rect>.collidelist(<list_of_Rect>) # Returns index of first colliding Rect or -1.
+<list> = <Rect>.collidelistall(<list_of_Rect>) # Returns indexes of all colliding rectangles.
+```
+
+### Surface
+**Object for representing images.**
+```python
+<Surf> = pg.display.set_mode((width, height)) # Returns a display surface.
+<Surf> = pg.Surface((width, height)) # New RGB surface. RGBA if `flags=pg.SRCALPHA`.
+<Surf> = pg.image.load('<path>') # Loads the image. Format depends on source.
+<Surf> = <Surf>.subsurface(<Rect>) # Returns a subsurface.
+```
+
+```python
+<Surf>.fill(color) # Tuple, Color('#rrggbb[aa]') or Color(<name>).
+<Surf>.set_at((x, y), color) # Updates pixel.
+<Surf>.blit(<Surf>, (x, y)) # Draws passed surface to the surface.
+```
+
+```python
+from pygame.transform import scale, ...
+<Surf> = scale(<Surf>, (width, height)) # Returns scaled surface.
+<Surf> = rotate(<Surf>, anticlock_degrees) # Returns rotated and scaled surface.
+<Surf> = flip(<Surf>, x_bool, y_bool) # Returns flipped surface.
+```
+
+```python
+from pygame.draw import line, ...
+line(<Surf>, color, (x1, y1), (x2, y2), width) # Draws a line to the surface.
+arc(<Surf>, color, <Rect>, from_rad, to_rad) # Also: ellipse(<Surf>, color, <Rect>, width=0)
+rect(<Surf>, color, <Rect>, width=0) # Also: polygon(<Surf>, color, points, width=0)
+```
+
+### Font
+```python
+<Font> = pg.font.SysFont('<name>', size) # Loads the system font or default if missing.
+<Font> = pg.font.Font('<path>', size) # Loads the TTF file. Pass None for default.
+<Surf> = <Font>.render(text, antialias, color) # Background color can be specified at the end.
+```
+
+### Sound
+```python
+<Sound> = pg.mixer.Sound('<path>') # Loads the WAV file.
+<Sound>.play() # Starts playing the sound.
+```
+
+### Basic Mario Brothers Example
+```python
+import collections, dataclasses, enum, io, itertools as it, pygame as pg, urllib.request
+from random import randint
+
+P = collections.namedtuple('P', 'x y') # Position
+D = enum.Enum('D', 'n e s w') # Direction
+W, H, MAX_S = 50, 50, P(5, 10) # Width, Height, Max speed
+
+def main():
+ def get_screen():
+ pg.init()
+ return pg.display.set_mode((W*16, H*16))
+ def get_images():
+ url = 'https://gto76.github.io/python-cheatsheet/web/mario_bros.png'
+ img = pg.image.load(io.BytesIO(urllib.request.urlopen(url).read()))
+ return [img.subsurface(get_rect(x, 0)) for x in range(img.get_width() // 16)]
+ def get_mario():
+ Mario = dataclasses.make_dataclass('Mario', 'rect spd facing_left frame_cycle'.split())
+ return Mario(get_rect(1, 1), P(0, 0), False, it.cycle(range(3)))
+ def get_tiles():
+ border = [(x, y) for x in range(W) for y in range(H) if x in [0, W-1] or y in [0, H-1]]
+ platforms = [(randint(1, W-2), randint(2, H-2)) for _ in range(W*H // 10)]
+ return [get_rect(x, y) for x, y in border + platforms]
+ def get_rect(x, y):
+ return pg.Rect(x*16, y*16, 16, 16)
+ run(get_screen(), get_images(), get_mario(), get_tiles())
+
+def run(screen, images, mario, tiles):
+ clock = pg.time.Clock()
+ while all(event.type != pg.QUIT for event in pg.event.get()):
+ keys = {pg.K_UP: D.n, pg.K_RIGHT: D.e, pg.K_DOWN: D.s, pg.K_LEFT: D.w}
+ pressed = {keys.get(ch) for ch, is_prsd in enumerate(pg.key.get_pressed()) if is_prsd}
+ update_speed(mario, tiles, pressed)
+ update_position(mario, tiles)
+ draw(screen, images, mario, tiles, pressed)
+ clock.tick(28)
+
+def update_speed(mario, tiles, pressed):
+ x, y = mario.spd
+ x += 2 * ((D.e in pressed) - (D.w in pressed))
+ x -= (x > 0) - (x < 0)
+ y += 1 if D.s not in get_boundaries(mario.rect, tiles) else (D.n in pressed) * -10
+ mario.spd = P(x=max(-MAX_S.x, min(MAX_S.x, x)), y=max(-MAX_S.y, min(MAX_S.y, y)))
+
+def update_position(mario, tiles):
+ x, y = mario.rect.topleft
+ n_steps = max(abs(s) for s in mario.spd)
+ for _ in range(n_steps):
+ mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect, tiles))
+ x, y = x + mario.spd.x / n_steps, y + mario.spd.y / n_steps
+ mario.rect.topleft = x, y
+
+def get_boundaries(rect, tiles):
+ deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
+ return {d for d, delta in deltas.items() if rect.move(delta).collidelist(tiles) != -1}
+
+def stop_on_collision(spd, bounds):
+ return P(x=0 if (D.w in bounds and spd.x < 0) or (D.e in bounds and spd.x > 0) else spd.x,
+ y=0 if (D.n in bounds and spd.y < 0) or (D.s in bounds and spd.y > 0) else spd.y)
+
+def draw(screen, images, mario, tiles, pressed):
+ def get_marios_image_index():
+ if D.s not in get_boundaries(mario.rect, tiles):
+ return 4
+ return next(mario.frame_cycle) if {D.w, D.e} & pressed else 6
+ screen.fill((85, 168, 255))
+ mario.facing_left = (D.w in pressed) if {D.w, D.e} & pressed else mario.facing_left
+ screen.blit(images[get_marios_image_index() + mario.facing_left * 9], mario.rect)
+ for t in tiles:
+ screen.blit(images[18 if t.x in [0, (W-1)*16] or t.y in [0, (H-1)*16] else 19], t)
+ pg.display.flip()
+
+if __name__ == '__main__':
+ main()
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+Pandas
+------
+```python
+# $ pip3 install pandas matplotlib
+import pandas as pd
+from pandas import Series, DataFrame
+import matplotlib.pyplot as plt
+```
+
+### Series
+**Ordered dictionary with a name.**
+
+```python
+>>> Series([1, 2], index=['x', 'y'], name='a')
+x 1
+y 2
+Name: a, dtype: int64
+```
+
+```python
+<Sr> = Series(<list>) # Assigns RangeIndex starting at 0.
+<Sr> = Series(<dict>) # Takes dictionary's keys for index.
+<Sr> = Series(<dict/Series>, index=<list>) # Only keeps items with keys specified in index.
+```
+
+```python
+<el> = <Sr>.loc[key] # Or: <Sr>.iloc[index]
+<Sr> = <Sr>.loc[keys] # Or: <Sr>.iloc[indexes]
+<Sr> = <Sr>.loc[from_key : to_key_inclusive] # Or: <Sr>.iloc[from_i : to_i_exclusive]
+```
+
+```python
+<el> = <Sr>[key/index] # Or: <Sr>.key
+<Sr> = <Sr>[keys/indexes] # Or: <Sr>[<key_range/range>]
+<Sr> = <Sr>[bools] # Or: <Sr>.i/loc[bools]
+```
+
+```python
+<Sr> = <Sr> ><== <el/Sr> # Returns a Series of bools.
+<Sr> = <Sr> +-*/ <el/Sr> # Items with non-matching keys get value NaN.
+```
+
+```python
+<Sr> = <Sr>.append(<Sr>) # Or: pd.concat(<coll_of_Sr>)
+<Sr> = <Sr>.combine_first(<Sr>) # Adds items that are not yet present.
+<Sr>.update(<Sr>) # Updates items that are already present.
+```
+
+```python
+<Sr>.plot.line/area/bar/pie/hist() # Generates a Matplotlib plot.
+plt.show() # Displays the plot. Also plt.savefig(<path>).
+```
+
+#### Series — Aggregate, Transform, Map:
+```python
+<el> = <Sr>.sum/max/mean/idxmax/all() # Or: <Sr>.agg(lambda <Sr>: <el>)
+<Sr> = <Sr>.rank/diff/cumsum/ffill/interpl() # Or: <Sr>.agg/transform(lambda <Sr>: <Sr>)
+<Sr> = <Sr>.fillna(<el>) # Or: <Sr>.agg/transform/map(lambda <el>: <el>)
+```
+
+```python
+>>> sr = Series([1, 2], index=['x', 'y'])
+x 1
+y 2
+```
+
+```text
++-----------------+-------------+-------------+---------------+
+| | 'sum' | ['sum'] | {'s': 'sum'} |
++-----------------+-------------+-------------+---------------+
+| sr.apply(…) | 3 | sum 3 | s 3 |
+| sr.agg(…) | | | |
++-----------------+-------------+-------------+---------------+
+```
+
+```text
++-----------------+-------------+-------------+---------------+
+| | 'rank' | ['rank'] | {'r': 'rank'} |
++-----------------+-------------+-------------+---------------+
+| sr.apply(…) | | rank | |
+| sr.agg(…) | x 1 | x 1 | r x 1 |
+| sr.transform(…) | y 2 | y 2 | y 2 |
++-----------------+-------------+-------------+---------------+
+```
+* **Last result has a hierarchical index. Use `'<Sr>[key_1, key_2]'` to get its values.**
+
+**[🔼Back to Top](#content-outlines)**
+
+## OpenCV
+
+```python
+# Importing an Image & Viewing it
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+cv2.imshow("Image", image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+
+Croping an image
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+crop_img = image[y:y+h, x:x+w]
+cv2.imshow("cropped", crop_img)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+Resizing an image
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+resized_image = cv2.resize(image, (100, 100))
+cv2.imshow("Resized image", resized_image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+Saving an image
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+cv2.imwrite("./path/to/save/image.jpg", image)
+```
+Converting an image to grayscale
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
+cv2.imshow("Grayscale image", gray_image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+Converting an image to HSV
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
+cv2.imshow("HSV image", hsv_image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+Converting an image to LAB
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+lab_image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
+cv2.imshow("LAB image", lab_image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+Converting an image to RGB
+```python
+import cv2
+image = cv2.imread("./path/to/image.jpg")
+rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
+cv2.imshow("RGB image", rgb_image)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+```
+
+**[🔼Back to Top](#content-outlines)**
+
+## DataFrame
+**Table with labeled rows and columns.**
+
+```python
+>>> DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
+ x y
+a 1 2
+b 3 4
+```
+
+```python
+<DF> = DataFrame(<list_of_rows>) # Rows can be either lists, dicts or series.
+<DF> = DataFrame(<dict_of_columns>) # Columns can be either lists, dicts or series.
+```
+
+```python
+<el> = <DF>.loc[row_key, column_key] # Or: <DF>.iloc[row_index, column_index]
+<Sr/DF> = <DF>.loc[row_key/s] # Or: <DF>.iloc[row_index/es]
+<Sr/DF> = <DF>.loc[:, column_key/s] # Or: <DF>.iloc[:, column_index/es]
+<DF> = <DF>.loc[row_bools, column_bools] # Or: <DF>.iloc[row_bools, column_bools]
+```
+
+```python
+<Sr/DF> = <DF>[column_key/s] # Or: <DF>.column_key
+<DF> = <DF>[row_bools] # Keeps rows as specified by bools.
+<DF> = <DF>[<DF_of_bools>] # Assigns NaN to False values.
+```
+
+```python
+<DF> = <DF> ><== <el/Sr/DF> # Returns DF of bools. Sr is treated as a row.
+<DF> = <DF> +-*/ <el/Sr/DF> # Items with non-matching keys get value NaN.
+```
+
+```python
+<DF> = <DF>.set_index(column_key) # Replaces row keys with values from a column.
+<DF> = <DF>.reset_index() # Moves row keys to a column named index.
+<DF> = <DF>.sort_index(ascending=True) # Sorts rows by row keys.
+<DF> = <DF>.sort_values(column_key/s) # Sorts rows by the passed column/s.
+```
+
+#### DataFrame — Merge, Join, Concat:
+```python
+>>> l = DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
+ x y
+a 1 2
+b 3 4
+>>> r = DataFrame([[4, 5], [6, 7]], index=['b', 'c'], columns=['y', 'z'])
+ y z
+b 4 5
+c 6 7
+```
+
+```text
++------------------------+---------------+------------+------------+--------------------------+
+| | 'outer' | 'inner' | 'left' | Description |
++------------------------+---------------+------------+------------+--------------------------+
+| l.merge(r, on='y', | x y z | x y z | x y z | Joins/merges on column. |
+| how=…) | 0 1 2 . | 3 4 5 | 1 2 . | Also accepts left_on and |
+| | 1 3 4 5 | | 3 4 5 | right_on parameters. |
+| | 2 . 6 7 | | | Uses 'inner' by default. |
++------------------------+---------------+------------+------------+--------------------------+
+| l.join(r, lsuffix='l', | x yl yr z | | x yl yr z | Joins/merges on row keys.|
+| rsuffix='r', | a 1 2 . . | x yl yr z | 1 2 . . | Uses 'left' by default. |
+| how=…) | b 3 4 4 5 | 3 4 4 5 | 3 4 4 5 | If r is a Series, it is |
+| | c . . 6 7 | | | treated as a column. |
++------------------------+---------------+------------+------------+--------------------------+
+| pd.concat([l, r], | x y z | y | | Adds rows at the bottom. |
+| axis=0, | a 1 2 . | 2 | | Uses 'outer' by default. |
+| join=…) | b 3 4 . | 4 | | A Series is treated as a |
+| | b . 4 5 | 4 | | column. Use l.append(sr) |
+| | c . 6 7 | 6 | | to add a row instead. |
++------------------------+---------------+------------+------------+--------------------------+
+| pd.concat([l, r], | x y y z | | | Adds columns at the |
+| axis=1, | a 1 2 . . | x y y z | | right end. Uses 'outer' |
+| join=…) | b 3 4 4 5 | 3 4 4 5 | | by default. A Series is |
+| | c . . 6 7 | | | treated as a column. |
++------------------------+---------------+------------+------------+--------------------------+
+| l.combine_first(r) | x y z | | | Adds missing rows and |
+| | a 1 2 . | | | columns. Also updates |
+| | b 3 4 5 | | | items that contain NaN. |
+| | c . 6 7 | | | R must be a DataFrame. |
++------------------------+---------------+------------+------------+--------------------------+
+```
+
+#### DataFrame — Aggregate, Transform, Map:
+```python
+<Sr> = <DF>.sum/max/mean/idxmax/all() # Or: <DF>.apply/agg(lambda <Sr>: <el>)
+<DF> = <DF>.rank/diff/cumsum/ffill/interpl() # Or: <DF>.apply/agg/transfrm(lambda <Sr>: <Sr>)
+<DF> = <DF>.fillna(<el>) # Or: <DF>.applymap(lambda <el>: <el>)
+```
+* **All operations operate on columns by default. Pass `'axis=1'` to process the rows instead.**
+
+```python
+>>> df = DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
+ x y
+a 1 2
+b 3 4
+```
+
+```text
++-----------------+-------------+-------------+---------------+
+| | 'sum' | ['sum'] | {'x': 'sum'} |
++-----------------+-------------+-------------+---------------+
+| df.apply(…) | | x y | |
+| df.agg(…) | x 4 | sum 4 6 | x 4 |
+| | y 6 | | |
++-----------------+-------------+-------------+---------------+
+```
+
+```text
++-----------------+-------------+-------------+---------------+
+| | 'rank' | ['rank'] | {'x': 'rank'} |
++-----------------+-------------+-------------+---------------+
+| df.apply(…) | x y | x y | x |
+| df.agg(…) | a 1 1 | rank rank | a 1 |
+| df.transform(…) | b 2 2 | a 1 1 | b 2 |
+| | | b 2 2 | |
++-----------------+-------------+-------------+---------------+
+```
+* **Use `'<DF>[col_key_1, col_key_2][row_key]'` to get the fifth result's values.**
+
+#### DataFrame — Plot, Encode, Decode:
+```python
+<DF>.plot.line/bar/hist/scatter/box() # Also: `x=column_key, y=column_key/s`.
+plt.show() # Displays the plot. Also plt.savefig(<path>).
+```
+
+```python
+<DF> = pd.read_json/html('<str/path/url>') # Run `$ pip3 install beautifulsoup4 lxml`.
+<DF> = pd.read_csv/pickle/excel('<path/url>') # Use `sheet_name=None` to get all Excel sheets.
+<DF> = pd.read_sql('<table/query>', <conn.>) # Accepts SQLite3 or SQLAlchemy connection.
+<DF> = pd.read_clipboard() # Reads a copied table from the clipboard.
+```
+
+```python
+<dict> = <DF>.to_dict(['d/l/s/…']) # Returns columns as dicts, lists or series.
+<str> = <DF>.to_json/html/csv([<path>]) # Also to_markdown/latex([<path>]).
+<DF>.to_pickle/excel(<path>) # Run `$ pip3 install openpyxl` for xlsx files.
+<DF>.to_sql('<table_name>', <connection>) # Accepts SQLite3 or SQLAlchemy connection.
+```
+
+### GroupBy
+**Object that groups together rows of a dataframe based on the value of the passed column.**
+
+```python
+>>> df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 6]], index=list('abc'), columns=list('xyz'))
+>>> df.groupby('z').get_group(6)
+ x y
+b 4 5
+c 7 8
+```
+
+```python
+<GB> = <DF>.groupby(column_key/s) # Splits DF into groups based on passed column.
+<DF> = <GB>.apply(<func>) # Maps each group. Func can return DF, Sr or el.
+<GB> = <GB>[column_key] # Single column GB. All operations return a Sr.
+```
+
+#### GroupBy — Aggregate, Transform, Map:
+```python
+<DF> = <GB>.sum/max/mean/idxmax/all() # Or: <GB>.agg(lambda <Sr>: <el>)
+<DF> = <GB>.rank/diff/cumsum/ffill() # Or: <GB>.transform(lambda <Sr>: <Sr>)
+<DF> = <GB>.fillna(<el>) # Or: <GB>.transform(lambda <Sr>: <Sr>)
+```
+
+```python
+>>> gb = df.groupby('z')
+ x y z
+3: a 1 2 3
+6: b 4 5 6
+ c 7 8 6
+```
+
+```text
++-----------------+-------------+-------------+-------------+---------------+
+| | 'sum' | 'rank' | ['rank'] | {'x': 'rank'} |
++-----------------+-------------+-------------+-------------+---------------+
+| gb.agg(…) | x y | x y | x y | x |
+| | z | a 1 1 | rank rank | a 1 |
+| | 3 1 2 | b 1 1 | a 1 1 | b 1 |
+| | 6 11 13 | c 2 2 | b 1 1 | c 2 |
+| | | | c 2 2 | |
++-----------------+-------------+-------------+-------------+---------------+
+| gb.transform(…) | x y | x y | | |
+| | a 1 2 | a 1 1 | | |
+| | b 11 13 | b 1 1 | | |
+| | c 11 13 | c 2 2 | | |
++-----------------+-------------+-------------+-------------+---------------+
+```
+
+### Rolling
+**Object for rolling window calculations.**
+
+```python
+<RSr/RDF/RGB> = <Sr/DF/GB>.rolling(win_size) # Also: `min_periods=None, center=False`.
+<RSr/RDF/RGB> = <RDF/RGB>[column_key/s] # Or: <RDF/RGB>.column_key
+<Sr/DF> = <R>.mean/sum/max() # Or: <R>.apply/agg(<agg_func/str>)
+```
+
+
+Plotly
+------
+```python
+# $ pip3 install plotly kaleido
+from plotly.express import line
+<Figure> = line(<DF>, x=<col_name>, y=<col_name>) # Or: line(x=<list>, y=<list>)
+<Figure>.update_layout(margin=dict(t=0, r=0, b=0, l=0)) # Or: paper_bgcolor='rgba(0, 0, 0, 0)'
+<Figure>.write_html/json/image('<path>') # Also: <Figure>.show()
+```
+
+#### Covid deaths by continent:
+
+![Covid Deaths](web/covid_deaths.png)
+<div id="2a950764-39fc-416d-97fe-0a6226a3095f" class="plotly-graph-div" style="height:340px; width:100%;"></div>
+
+```python
+covid = pd.read_csv('https://covid.ourworldindata.org/data/owid-covid-data.csv',
+ usecols=['iso_code', 'date', 'total_deaths', 'population'])
+continents = pd.read_csv('https://gist.githubusercontent.com/stevewithington/20a69c0b6d2ff'
+ '846ea5d35e5fc47f26c/raw/country-and-continent-codes-list-csv.csv',
+ usecols=['Three_Letter_Country_Code', 'Continent_Name'])
+df = pd.merge(covid, continents, left_on='iso_code', right_on='Three_Letter_Country_Code')
+df = df.groupby(['Continent_Name', 'date']).sum().reset_index()
+df['Total Deaths per Million'] = df.total_deaths * 1e6 / df.population
+df = df[df.date > '2020-03-14']
+df = df.rename({'date': 'Date', 'Continent_Name': 'Continent'}, axis='columns')
+line(df, x='Date', y='Total Deaths per Million', color='Continent').show()
+```
+
+#### Confirmed covid cases, Dow Jones, Gold, and Bitcoin price:
+
+![Covid Cases](web/covid_cases.png)
+<div id="e23ccacc-a456-478b-b467-7282a2165921" class="plotly-graph-div" style="height:315px; width:100%;"></div>
+
+```python
+import pandas as pd
+import plotly.graph_objects as go
+
+def main():
+ display_data(wrangle_data(*scrape_data()))
+
+def scrape_data():
+ def scrape_covid():
+ url = 'https://covid.ourworldindata.org/data/owid-covid-data.csv'
+ df = pd.read_csv(url, usecols=['location', 'date', 'total_cases'])
+ return df[df.location == 'World'].set_index('date').total_cases
+ def scrape_yahoo(slug):
+ url = f'https://query1.finance.yahoo.com/v7/finance/download/{slug}' + \
+ '?period1=1579651200&period2=9999999999&interval=1d&events=history'
+ df = pd.read_csv(url, usecols=['Date', 'Close'])
+ return df.set_index('Date').Close
+ out = scrape_covid(), scrape_yahoo('BTC-USD'), scrape_yahoo('GC=F'), scrape_yahoo('^DJI')
+ return map(pd.Series.rename, out, ['Total Cases', 'Bitcoin', 'Gold', 'Dow Jones'])
+
+def wrangle_data(covid, bitcoin, gold, dow):
+ df = pd.concat([bitcoin, gold, dow], axis=1) # Joins columns on dates.
+ df = df.sort_index().interpolate() # Sorts by date and interpolates NaN-s.
+ df = df.loc['2020-02-23':] # Discards rows before '2020-02-23'.
+ df = (df / df.iloc[0]) * 100 # Calculates percentages relative to day 1.
+ df = df.join(covid) # Adds column with covid cases.
+ return df.sort_values(df.index[-1], axis=1) # Sorts columns by last day's value.
+
+def display_data(df):
+ figure = go.Figure()
+ for col_name in reversed(df.columns):
+ yaxis = 'y1' if col_name == 'Total Cases' else 'y2'
+ trace = go.Scatter(x=df.index, y=df[col_name], name=col_name, yaxis=yaxis)
+ figure.add_trace(trace)
+ figure.update_layout(
+ yaxis1=dict(title='Total Cases', rangemode='tozero'),
+ yaxis2=dict(title='%', rangemode='tozero', overlaying='y', side='right'),
+ legend=dict(x=1.1),
+ height=450
+ ).show()
+
+if __name__ == '__main__':
+ main()
+```
+
+
+PySimpleGUI
+-----------
+```python
+# $ pip3 install PySimpleGUI
+import PySimpleGUI as sg
+layout = [[sg.Text("What's your name?")], [sg.Input()], [sg.Button('Ok')]]
+window = sg.Window('Window Title', layout)
+event, values = window.read()
+print(f'Hello {values[0]}!' if event == 'Ok' else '')
+```
+
+
+Appendix
+--------
+### Cython
+**Library that compiles Python code into C.**
+
+```python
+# $ pip3 install cython
+import pyximport; pyximport.install()
+import <cython_script>
+<cython_script>.main()
+```
+
+#### Definitions:
+* **All `'cdef'` definitions are optional, but they contribute to the speed-up.**
+* **Script needs to be saved with a `'pyx'` extension.**
+
+```python
+cdef <ctype> <var_name> = <el>
+cdef <ctype>[n_elements] <var_name> = [<el_1>, <el_2>, ...]
+cdef <ctype/void> <func_name>(<ctype> <arg_name>): ...
+```
+
+```python
+cdef class <class_name>:
+ cdef public <ctype> <attr_name>
+ def __init__(self, <ctype> <arg_name>):
+ self.<attr_name> = <arg_name>
+```
+
+```python
+cdef enum <enum_name>: <member_name_1>, <member_name_2>, ...
+```
+
+### PyInstaller
+```bash
+$ pip3 install pyinstaller
+$ pyinstaller script.py # Compiles into './dist/script' directory.
+$ pyinstaller script.py --onefile # Compiles into './dist/script' console app.
+$ pyinstaller script.py --windowed # Compiles into './dist/script' windowed app.
+$ pyinstaller script.py --add-data '<path>:.' # Adds file to the root of the executable.
+```
+* **File paths need to be updated to `'os.path.join(sys._MEIPASS, <path>)'`.**
+
+### Basic Script Template
+```python
+#!/usr/bin/env python3
+#
+# Usage: .py
+#
+
+from sys import argv, exit
+from collections import defaultdict, namedtuple
+from dataclasses import make_dataclass
+from enum import Enum
+import functools as ft, itertools as it, operator as op, re
+
+
+def main():
+ pass
+
+
+###
+## UTIL
+#
+
+def read_file(filename):
+ with open(filename, encoding='utf-8') as file:
+ return file.readlines()
+
+
+if __name__ == '__main__':
+ main()
+```
+
+
+Index
+-----
+* **Only available in the [PDF](https://transactions.sendowl.com/products/78175486/4422834F/view).**
+* **Ctrl+F / ⌘F is usually sufficient.**
+* **Searching `'#<title>'` on the [webpage](https://gto76.github.io/python-cheatsheet/) will limit the search to the titles.**
+
+**[🔼Back to Top](#content-outlines)**
diff --git a/PYTHON/ROUND, CEIL, FLOOR.md b/PYTHON/ROUND, CEIL, FLOOR.md
new file mode 100644
index 0000000..6a9bf93
--- /dev/null
+++ b/PYTHON/ROUND, CEIL, FLOOR.md
@@ -0,0 +1,20 @@
+👩‍💻 **Как округлять числа в Python?**
+
+Python предоставляет гибкие способы округления чисел:
+
+➡️ **Округление до ближайшего целого:**
+
+print(round(3.14159))  # 3
+
+➡️ **Округление с точностью до 2 знаков:**
+
+print(round(3.14159, 2))  # 3.14
+
+**➡️ Округление вверх или вниз:**
+
+import math
+
+print(math.ceil(3.1))  # 4
+print(math.floor(3.9))  # 3
+
+🗣 Используйте round(), когда важна точность, и math.ceil()/math.floor() для контроля направления округления. \ No newline at end of file
diff --git a/PYTHON/SET, DICT, LIST, TUPLE, RANGE.md b/PYTHON/SET, DICT, LIST, TUPLE, RANGE.md
new file mode 100644
index 0000000..4d58f5e
--- /dev/null
+++ b/PYTHON/SET, DICT, LIST, TUPLE, RANGE.md
@@ -0,0 +1,219 @@
+### SET - набор неупорядоченная последовательность элементов
+Может содержать ТОЛЬКО уникальные элементы !
+По сути тот же список, но только с уникальными элементами !
+
+```python
+S = {'Moscow', 'Riga', ''Dolgoprudny', 'Piter'}
+
+S.add('Rostov')
+
+if 'Riga' in S :
+ print('.......')
+
+for el in S :
+ print( el )
+```
+
+### DICT - словарь (набор элементов)
+ключ : значение (нет уникальных индексов)
+
+```python
+D = {
+ 'Moscow': 12547,
+ 'Dolgoprudny': 3562,
+ 'Piter': 15478
+ 'Rostov': 333
+}
+print(D['Moscow'])
+# 12547
+
+D['Rostov'] = 756
+print(D['Rostov'])
+# 756
+
+
+# с python 3.9 новый оператор слияния словарей
+# создается новый словарь:
+x = {1: 'qwe', 2: 'ggfd'}
+y = {3: 'qwjhgv', 4: 'gfffgfd'}
+z = {5: 'q234we', 6: 'gg45fd'}
+
+d = x | y | z
+print(d)
+```
+**Удаление дубликатов в словаре**
+
+Стратегия заключается в преобразовании списка словарей в список кортежей, где кортежи содержат элементы словаря. 
+Поскольку кортежи могут быть хешированы, вы можете удалить дубликаты с помощью set(используя здесь понимание множества , более старой альтернативой Python будет set(tuple(d.items()) for d in l)) и после этого заново создать словари из кортежей с помощью dict.
+![[telegram-cloud-photo-size-2-5280728042542581828-y.jpg]]
+
+** - можно распаковать словарь
+### LIST - список (изменяемый)
+Упорядоченная последовательность любых элементов.
+Может содержать ЛЮБЫЕ (даже повторяющиеся - НЕ уникальные) элементы.
+Можно изменять, удалять, добавлять.
+```python
+A = [1,2,3,4,5]
+A = [(1,10),(2,20),(3,30), {'den': 345}]
+
+# копирование списка (создание нового, еще одного):
+B = A[:]
+B = A.copy()
+B = list(A)
+```
+
+### TUPLE - кортеж (не изменяемый)
+последовательность элементов - ВАЖНА !
+```python
+T = (1, 2, 3, 4, 5)
+a, b, *rest = T
+*rest, a, b = T
+
+print(T)
+print(*T)
+print(*T, sep=":", end=" !\n")
+
+
+def hello_n(name: str, n: int):
+ for i in range(n):
+ print('Привет ', name)
+
+hello_n('Алеша', 5)
+hello_n('Петя', 4)
+vasya = 'Вася', 3
+hello_n(*vasya)
+
+########################################################
+
+my_object = {
+ 'x': 10,
+ 'y': True
+}
+
+for item in my_object.items():
+ key, value = item
+ print(key, value)
+
+for key, value in my_object.items():
+ print(key, value)
+```
+
+### RANGE - упорядоченная НЕизменяемая последовательность элементов
+Обычно используются в циклах.
+```python
+my_range = range(20) # до
+my_range = range(10, 20) # от : до
+my_range = range(10, 20, 3) # от : до : шаг
+
+print(my_range)
+# range(10, 20, 3)
+
+print(list(my_range))
+# [10, 13, 16, 19]
+
+for n in my_range:
+ print(n)
+# 10
+# 13
+# 16
+# 19
+```
+
+![[Снимок экрана 2024-03-10 в 20.55.05.png]]
+
+### ZIP - встроенная функция
+
+```python
+fruits = ['apple', 'banana', 'lime']
+quantities = [100, 70, 50]
+availabiliti = [True, False, False, True]
+
+fruit_qtys_zip = zip(fruits, quantities, availabiliti)
+
+print(fruit_qtys_zip)
+
+print(type(fruit_qtys_zip))
+
+fruit_qtys_list = list(fruit_qtys_zip)
+print(fruit_qtys_list) # создается список кортежей
+
+# fruit_qtys_list = tuple(fruit_qtys_zip)
+# print(fruit_qtys_list) # создается список кортежей
+
+# fruit_qtys_list = dict(fruit_qtys_zip)
+# print(fruit_qtys_list) # создается список словарей
+```
+
+```python
+from copy import deepcopy # глубокая копия
+
+
+info = {
+ 'name': 'vlapa',
+ 'is_instructor': True,
+ 'reviews': []
+}
+
+
+info_deepcopy = info.copy() # deepcopy(info)
+info_deepcopy['reviews'].append('Great_course')
+info['reviews'].append('Super')
+
+print('\n-1-')
+print(info)
+
+print('\n-2-')
+print(info_deepcopy)
+```
+
+```python
+# Фильтрация элементов по типу:
+def filter_list(list_to_filter, value_type):
+ filtered_list = []
+ for element in list_to_filter:
+ if type(element) == value_type:
+ filtered_list.append(element)
+
+ # Not recomended! Because bool is subclass of int !
+ # if isinstance(element, value_type):
+ # filtered_list.append(element)
+
+ return filtered_list
+
+
+print(filter_list([35, True, 'abc', 10], int))
+print(filter_list([35, True, 'abc', 10], str))
+print(filter_list([35, True, 'abc', 10], bool))
+
+################################################
+# Фильтрация элементов с помощью <filter>:
+
+def filter_list(list_to_filter, value_type):
+ def check_element_type(elem):
+ return isinstance(elem, value_type)
+ return list(filter(check_element_type, list_to_filter))
+
+res = filter_list([1, 10, 'abc', 5.5], int)
+print(res)
+
+################################################
+# Фильтрация элементов с помощью <filter> и <lambda>:
+
+def filter_list(list_to_filter, value_type):
+ return list(filter(lambda elem: type(elem) is value_type, list_to_filter))
+
+res = filter_list([1, 10, 'abc', 5.5], int)
+print(res)
+```
+
+**Соединение списка и множества в словарь**
+
+![[telegram-cloud-photo-size-2-5249336126576118394-y.jpg]]
+
+Для соединения списка и множества в словарь можно использовать встроенную функцию `zip()`. Эта функция объединяет элементы нескольких последовательностей (`списков`, множеств, кортежей и т.д.) в кортежи.
+
+Для создания словаря из списков и множеств нужно сначала объединить элементы в кортежи при помощи функции zip(), а затем создать словарь из этих кортежей при помощи функции dict().
+
+`fruit_prices
+будет содержать словарь
+`{'apple': 1.20, 'banana': 0.80, 'orange': 1.50}. \ No newline at end of file
diff --git a/PYTHON/SPEEDTEST python.md b/PYTHON/SPEEDTEST python.md
new file mode 100644
index 0000000..1a0f86e
--- /dev/null
+++ b/PYTHON/SPEEDTEST python.md
@@ -0,0 +1,51 @@
+⏲ Проверка скорости интернета с Python
+
+`import speedtest
+
+`st = speedtest.Speedtest()
+`print("Загрузка:", round(st.download() / 1_000_000, 2), "Мбит/с")
+`print("Выгрузка:", round(st.upload() / 1_000_000, 2), "Мбит/с")
+`print("Пинг:", st.results.ping, "мс")
+
+📝 Пример результата
+
+Загрузка: 55.23 Мбит/с
+Выгрузка: 33.66 Мбит/с
+Пинг: 42.241 мс
+
+✔️ Установка библиотеки
+
+`pip install speedtest-cli
+
+Можно использовать для быстрой проверки интернет-соединение или автоматизации мониторинга сети.
+
+
+![[telegram-cloud-photo-size-2-5316919099131030050-y.jpg]]
+
+=======================================
+
+⚙️ **Проверка скорости интернета в Linux**
+
+Если интернет работает медленно, важно быстро протестировать его скорость. В Linux это можно сделать прямо из терминала.
+# Установить Speedtest CLI (если не установлен)
+
+```sh
+sudo apt install speedtest-cli  # Для Debian/Ubuntu  
+sudo pacman -S speedtest-cli  # Для Arch  
+sudo dnf install speedtest-cli  # Для Fedora  
+
+# Проверить скорость интернета
+speedtest
+
+# Проверить только скорость загрузки и отдачи
+speedtest --simple
+
+# Получить результат в виде JSON (можно использовать для автоматизации)
+speedtest --json
+```
+🟢 Показывает реальную скорость загрузки и отдачи.
+🟢 Подходит для диагностики интернет-соединения.
+🟢 Можно встроить в мониторинг серверов
+
+**🗣 Эти команды помогут быстро определить, есть ли проблемы со скоростью сети.**
+
diff --git a/PYTHON/SPLIT, JOIN.md b/PYTHON/SPLIT, JOIN.md
new file mode 100644
index 0000000..1ee06b8
--- /dev/null
+++ b/PYTHON/SPLIT, JOIN.md
@@ -0,0 +1,2 @@
+![[telegram-cloud-photo-size-2-5253592580900375225-x.jpg]]
+
diff --git a/PYTHON/STRING.md b/PYTHON/STRING.md
new file mode 100644
index 0000000..6e7d4f9
--- /dev/null
+++ b/PYTHON/STRING.md
@@ -0,0 +1,121 @@
+```python
+my_str = 'Vlapa'
+print(len(my_str))
+# 5
+
+print(my_str[0])
+# V
+
+print(my_str[2:5])
+# apa
+
+
+my_str.upper()
+my_str.replace('__old', '__new') # само значение не изменяется !
+my_str.count('__x') # считает кол-во вхождений
+
+
+```
+![[Снимок экрана 2025-02-11 в 01.15.10.png]]
+
+Создать 1000 папок со случайными именами:
+![[Снимок экрана 2025-02-11 в 01.17.25.png]]
+
+
+**Работа с регистрами**
+
+![[telegram-cloud-photo-size-4-5863854399405801203-w.jpg]]
+
+_Строки в Python имеют свойство регистра — прописные и строчные буквы считаются разными символами._
+
+Для преобразования регистра используются методы строк:
+str.upper() — преобразует строку к верхнему регистру.
+str.lower() — преобразует строку к нижнему регистру.
+str.swapcase() — меняет регистр каждого символа на противоположный.
+str.capitalize() — делает первую букву заглавной, остальные строчными.
+
+Регистр конкретного символа можно проверить в строке методами:
+str.isupper() — проверка на заглавный регистр.
+str.islower() — проверка на строчный регистр.
+
+Регистр важно учитывать при сравнении строк на равенство.
+
+
+### Полезные строковые методы
+
+1️⃣ .upper() **– переводит строку в ВЕРХНИЙ РЕГИСТР**
+text = "hello world"
+print(text.upper())  # "HELLO WORLD"
+
+2️⃣ .lower() **– переводит строку в нижний регистр**
+text = "HELLO WORLD"
+print(text.lower())  # "hello world"
+
+3️⃣ .strip() **– удаляет пробелы и символы переноса с начала и конца строки**
+text = "  hello world  "
+print(text.strip())  # "hello world"
+
+Можно указать символы, которые нужно удалить:
+text = "##hello##"
+print(text.strip("#"))  # "hello"
+
+4️⃣ .replace(old, new) **– заменяет подстроку в строке**
+text = "Hello, world!"
+print(text.replace("world", "Python"))  # "Hello, Python!"
+
+5️⃣ .split(separator) **– разделяет строку по разделителю и возвращает список**
+text = "apple,banana,orange"
+print(text.split(","))  # ['apple', 'banana', 'orange']
+
+Если разделитель не указан, удаляются пробелы:
+text = "Hello   world"
+print(text.split())  # ['Hello', 'world']
+
+Эти методы часто используются вместе, например:
+text = "  hello, world!  "
+clean_text = text.strip().replace(",", "").upper()
+print(clean_text)  # "HELLO WORLD"
+
+
+### f-строки с выражениями в форматировании
+
+Да, ты можешь использовать **выражения прямо внутри f-строк**!
+```python
+value = 3.14159
+print(f"Округлено: {value:.2f}")      # Округлит до 2 знаков
+print(f"В квадрате: {value**2:.1f}")  # Сразу вычисляет и форматирует
+```
+
+🧠 **А можно и условие:**
+```python
+name = None
+print(f"Привет, {name or 'гость'}!")
+```
+
+🗣 **При генерации отчётов, логов, CLI-вывода, шаблонов — везде, где важна читаемость и компактность.**
+
+
+
+PEP 750 вводят t-строки — это как f"..." но:
+- интерполяция ограничена и контролируема;
+- поддерживается строгое соответствие шаблону;
+- можно передавать переменные явно, предотвращая **SQL-инъекции** и XSS.
+
+Для чего? Для того, чтобы писать shell-команды, SQL, HTML и т.д. без риска инъекций и без ручного экранирования
+
+**Пример**:
+
+```PYTHON
+from string.templatelib import Template
+
+name = "World"
+template: Template = t"Hello, {name}!"
+
+safe_output = html(template)
+```
+
+Здесь html() — это функция, которая принимает объект Template и возвращает безопасную строку, экранируя потенциально опасные вставки
+
+PEP 787 предлагает добавить это прямо в subprocess и shlex (но пока в статусе "отложен")
+
+Следим за развитием 😊 \ No newline at end of file
diff --git a/PYTHON/SnakeViz.md b/PYTHON/SnakeViz.md
new file mode 100644
index 0000000..eb624ff
--- /dev/null
+++ b/PYTHON/SnakeViz.md
@@ -0,0 +1,15 @@
+⚙️ Библиотека Python: SnakeViz
+
+SnakeViz — незаменимый инструмент, если ваш Python-скрипт работает медленно. Он позволяет наглядно увидеть, где "утекает" производительность, и понять, какие функции требуют оптимизации.
+
+`pip install snakeviz
+
+• Профилируйте код с помощью cProfile:
+
+`python -m cProfile -o output.prof script.py
+
+• Визуализируйте результаты:
+
+`snakeviz output.prof
+
+Откроется удобный интерфейс в браузере, где будет ясно видно, какие функции "съедают" больше всего времени. \ No newline at end of file
diff --git a/PYTHON/TELEGRAMM BOT.md b/PYTHON/TELEGRAMM BOT.md
new file mode 100644
index 0000000..50a2ae1
--- /dev/null
+++ b/PYTHON/TELEGRAMM BOT.md
@@ -0,0 +1,59 @@
+Создаем бота в BotFather:
+
+==/newbot = Work_Server==
+t.me/vlapa_bot
+7286073488:AAGzCTkYPI8rleSXpZxRm5isckgN09YVhiM
+'id': 949969015
+
+==/newbot = Home_server==
+t.me/vlapaHome_bot
+7423942020:AAGw0w75nFIkMGLDRSk2Spx1wq_-dlJOPeM
+'id': 949969015
+
+==/newbot = mqtt==
+t.me/vlapa_mqtt_log_Bot
+7238364847:AAGbx7RwzPaTpirzttnLQT9FPWL7DmvyWDs
+'id': 949969015
+
+
+Запускаем боты:
+- /start
+
+```python
+# python -m pip install requests
+
+import requests
+TOKEN = "Сюда вставьте свой токен"
+url = f"https://api.telegram.org/bot{TOKEN}/getUpdates"
+print(requests.get(url).json())
+```
+
+{'ok': True, 'result': [{'update_id': 552144411, 'message': {'message_id': 1, 'from': {'id': 949969015, 'is_bot': False, 'first_name': 'Vlapa', 'language_code': 'ru'}, 'chat': {'id': 949969015, 'first_name': 'Vlapa', 'type': 'private'}, 'date': 1725095952, 'text': '/start', 'entities': [{'offset': 0, 'length': 6, 'type': 'bot_command'}]}}]}
+
+```python
+import requests
+TOKEN = "Сюда вставьте свой токен"
+chat_id = "Сюда вставьте ID чата"
+message = "Здесь напишите свое сообщение"
+url = f"https://api.telegram.org/bot{TOKEN}/sendMessage?chat_id={chat_id}&text={message}"
+print(requests.get(url).json()) # Эта строка отсылает сообщение
+```
+
+
+```python
+disk_list = '/dev/sdd5 /dev/md127p1 /dev/md126p1 /dev/sdc'
+disk_name = '300Gb 1Tb-a 1Tb-b 1Tb-c'
+
+
+
+for disk in disk_list
+ command = f"df {disk} | grep / | awk 'print $5' | sed 's/%//g' >> size.txt"
+ current = os.system(command)
+
+ threshold = 5
+ if current > threshold
+ f = open('size.txt', w+)
+
+ f.close()
+
+``` \ No newline at end of file
diff --git a/PYTHON/THREADING.md b/PYTHON/THREADING.md
new file mode 100644
index 0000000..8ebc631
--- /dev/null
+++ b/PYTHON/THREADING.md
@@ -0,0 +1,128 @@
+**Как работать с многопоточностью в Python**
+
+Python поддерживает многопоточность, но из-за **GIL (Global Interpreter Lock)** потоки работают не параллельно на CPU.
+
+✅ Использование потоков:
+```python
+import threading
+
+def worker():
+    print("Работает поток")
+
+t = threading.Thread(target=worker)
+t.start()
+t.join()  # Ждем завершения потока
+```
+
+**⚙️ Когда использовать?**
+🟢 Для задач ввода-вывода (запросы в сеть, работа с файлами)
+🟠 Для вычислений лучше использовать multiprocessing, а не threading
+🗣 **Потоки полезны, но знай их ограничения!**
+
+
+
+# Python: как сделать многопоточную программу
+
+Когда-то давно мы делали [простой таймер с напоминанием на Python](https://thecode.media/python-time/). Он работал так:
+
+1. Мы спрашивали пользователя, о чём ему напомнить и через сколько минут.
+2. Программа на это время засыпала и ничего не делала.
+3. Как только время сна заканчивалось, программа просыпалась и выводила напоминание.
+
+У такой схемы есть минус: мы не можем пользоваться программой и выделенными на неё ресурсами до тех пор, пока она не проснётся. Процессор по кругу гоняет пустые команды и ждёт, когда можно будет продолжить полезную работу. Чтобы процессор и программа могли во время работы таймера делать что-то ещё, используют потоки.
+
+## Что такое поток
+
+В упрощённом виде потоки — это параллельно выполняемые задачи. По умолчанию используется один поток — это значит, что программа делает всё по очереди, линейно, без возможности делать несколько дел одновременно.
+
+Но если мы сделаем в программе два потока задач, то они будут работать параллельно и независимо друг от друга. Одному потоку не нужно будет становиться на паузу, когда в другом что-то происходит.
+
+👉 Важно понимать, что поток — это высокоуровневое понятие из области программирования. На уровне вашего «железа» эти потоки всё ещё могут обсчитываться последовательно. Но благодаря тому, что они будут обсчитываться быстро, вам может показаться, что они работают параллельно.
+
+## Многопоточность
+
+Представим такую ситуацию:
+
+- У вас на руке смарт-часы, которые собирают данные о вашем пульсе, УФ-излучении и движениях. На смарт-часах работает программа, которая обрабатывает эти данные.
+- Программа состоит из четырёх функций. Первая собирает данные с датчиков. Три другие обрабатывают эти данные и делают выводы.
+- Пока первая функция не собрала нужные данные, ничего другого не происходит.
+- Как только данные введены, запускаются три оставшиеся функции. Они не зависят друг от друга и каждая считает своё.
+- Как только все три функции закончат работу, программа выдаёт нужный результат.
+
+А теперь давайте посмотрим, как это выглядит в однопоточной и многопоточной системе. Видно, что если процессор позволяет делать несколько дел одновременно, в многопоточном режиме программа будет работать быстрее:
+
+![Python: как сделать многопоточную программу](https://thecode.media/wp-content/uploads/2021/09/Mnogopotochnaya-zadacha.png "Многопоточность")
+## Многопоточность на Python
+
+За потоки в Python отвечает модуль threading, а сам поток можно создать с помощью класса Thread из этого модуля. Подключается он так:
+
+```markup
+from threading import Thread
+```
+
+После этого с помощью функции Thread () мы сможем создать столько потоков, сколько нам нужно. Логика работы такая:
+
+1. Подключаем нужный модуль и класс Thread.
+2. Пишем функции, которые нам нужно выполнять в потоках.
+3. Создаём новую переменную — поток, и передаём в неё название функции и её аргументы. Один поток = одна функция на входе.
+4. Делаем так столько потоков, сколько требует логика программы.
+5. Потоки сами следят за тем, закончилась в них функция или нет. Пока работает функция — работает и поток.
+6. Всё это работает параллельно и (в теории) не мешает друг другу.
+
+Для иллюстрации запустим такой код:
+
+```python
+import time
+from threading import Thread
+
+def sleepMe(i):
+ print("Поток %i засыпает на 5 секунд.\n" % i)
+ time.sleep(5)
+ print("Поток %i сейчас проснулся.\n" % i)
+for i in range(10):
+ th = Thread(target=sleepMe, args=(i, ))
+ th.start()
+```
+
+А вот как выглядит результат. Обратите внимание — потоки просыпаются не в той последовательности, как мы их запустили, а в той, в какой их выполнил процессор. Иногда это может помешать работе программы, но про это мы поговорим отдельно в другой статье.
+
+![Многопоточность в Python: обратите внимание — потоки просыпаются не в той последовательности, как мы их запустили, а в той, в какой их выполнил процессор](https://thecode.media/wp-content/uploads/2020/11/image3.png "Обратите внимание — потоки просыпаются не в той последовательности, как мы их запустили, а в той, в какой их выполнил процессор")
+## Добавляем потоки в таймер
+
+Первое, что нам нужно сделать, — вынести код таймера-напоминания в отдельную функцию, чтобы создать с ней поток. Для этого используем команду `def`:
+
+```python
+# Делаем отдельную функцию с напоминанием
+def remind():
+ # Спрашиваем текст напоминания, который нужно потом показать пользователю
+ print("О чём вам напомнить?")
+ # Ждём ответа пользователя и результат помещаем в строковую переменную text
+ text = str(input())
+ # Спрашиваем про время
+ print("Через сколько минут?")
+ # Тут будем хранить время, через которое нужно показать напоминание
+ local_time = float(input())
+ # Переводим минуты в секунды
+ local_time = local_time * 60
+ # Ждём нужное количество секунд, программа в это время ничего не делает
+ time.sleep(local_time)
+ # Показываем текст напоминания
+ print(text)
+```
+
+Теперь сделаем новый поток, в который отправим выполняться нашу новую функцию. Так как аргументов у нас нет, то и аргументы передавать не будем, а напишем args=().
+
+```markup
+# Создаём новый поток
+th = Thread(target=remind, args=())
+# И запускаем его
+th.start()
+```
+
+Нам осталось убедиться в том, что пока поток работает, мы можем выполнять в программе что-то ещё и наше засыпание в потоке на это не повлияет. Для этого мы через 20 секунд после запуска выведем сообщение на экран. За 20 секунд пользователь успеет ввести напоминание и время, после чего таймер уйдёт в новый поток и там уснёт на нужное количество минут. Но это не помешает работе основной программы — она тоже будет выполнять свои команды параллельно с потоком.
+
+```markup
+# Пока работает поток, выведем что-то на экран через 20 секунд после запуска
+time.sleep(20)
+print("Пока поток работает, мы можем сделать что-нибудь ещё.\n")
+```
diff --git a/PYTHON/TKINTER.md b/PYTHON/TKINTER.md
new file mode 100644
index 0000000..23b9658
--- /dev/null
+++ b/PYTHON/TKINTER.md
@@ -0,0 +1,52 @@
+```python
+from tkinter import *
+
+root = Tk()
+root.title("Hello!")
+root.iconbitmap(default="favicon.ico") # определим в одной папке с файлом приложения какой-нибудь файл с иконкой, допустип, он называется "favicon.ico". через параметр `default` в метод iconbitmap передается путь к иконки. В данном случае файл иконки располагается с файлом приложения в одной папке, поэтому в качестве пути указывается просто имя файла.
+
+root.geometry("300x250+400+200")
+# Теперь строка в методе geometry имеет следующий формат: "Ширина x Высота + координатаX + координатаY". То есть при запуске окно шириной в 300 единиц и высотой 250 единиц будет находиться на 400 пикселей вправо и на 200 пикселей вниз от верхнего левого угла экрана.
+
+root.update_idletasks()
+print(root.geometry()) # Для получения данных о размере и позиции также можно использовать метод geometry(), который возвращает данные значения в виде строки в формате "widthxheight+x+y". Чтобы приложение еще до метода `mainloop()` принименило для окна переданные ему значения по ширине, высоте и позиции, вызывается метод `root.update_idletasks()`. В итоге вызов `root.geometry()` возвратить строку "300x250+400+200"
+
+root.resizable(False,False) # Можно-ли растягивать: ширина - высота
+
+root.mainloop()
+
+```
+### Виджеты. Основные:
+
+- Button: кнопка
+- Label: текстовая метка
+- Entry: однострочное текстовое поле
+- Text: многострочное текстовое поле
+- Checkbutton: флажок
+- Radiobutton: переключатель или радиокнопка
+- Frame: фрейм, который организует виджеты в группы
+- Listbox: список
+- Combobox: выпадающий список
+- Menu: элемент меню
+- Scrollbar: полоса прокрутки
+- Treeview: позволяет создавать древовидные и табличные элементы
+- Scale: текстовая метка
+- Spinbox: список значений со стрелками для перемещения по элементам
+- Progressbar: текстовая метка
+- Canvas: текстовая метка
+- Notebook: панель вкладок
+
+### Получение информации о виджете
+
+Для получения информации о виджете можно использовать ряд его атрибутов. Рассмотрим некоторые из них:
+
+- winfo_class: возвращает класс виджета, например, для кнопки это класс `TButton`
+- winfo_children: возвращает для текущего виджета список вложенных виджетов
+- winfo_parent: возвращает родительский виджет
+- winfo_toplevel: возвращает окно, которое содержит данный виджет
+- winfo_width и winfo_height: текущая ширина и высота виджета
+- winfo_reqwidth и winfo_reqheight: запрошенная виджетом ширина и высота
+- winfo_x и winfo_y: x и y координаты верхнего левого угла виджета относительно родительского элемента
+- winfo_rootx и winfo_rooty: x и y координаты верхнего левого угла виджета относительно экрана
+- winfo_viewable: указывает, отображается ли виджет или скрыт
+
diff --git a/PYTHON/TRY ... EXCEPT.md b/PYTHON/TRY ... EXCEPT.md
new file mode 100644
index 0000000..087abe3
--- /dev/null
+++ b/PYTHON/TRY ... EXCEPT.md
@@ -0,0 +1,269 @@
+## исключения и какие они бывают
+
+В Python исключение — это событие, которое возникает при выполнении программы и меняет его нормальный поток. Также это объект, который обозначает различные типы состояний отказа программы и содержит информацию об ошибке:
+
+- тип ошибки;
+- состояние программы на момент, когда ошибка возникла;
+- описание ошибки.
+
+При этом в Python много встроенных исключений, которые охватывают стандартные типы ошибок:
+
+- `AssertionError` — возникает, когда оператор assert терпит неудачу.
+- `AttributeError` — попытка доступа к атрибуту объекта, которого не существует.
+- `EOFError` — возникает, когда функция input () достигает конца файла.
+- `FileNotFoundError` — программа пытается открыть несуществующий файл.
+- `FloatingPointError` — сбой операции с плавающей запятой.
+- `ImportError` — неудачная попытка импорта модуля.
+- `IndexError` — попытка доступа к элементу списка по недопустимому индексу, например когда индекс выходит за пределы допустимого диапазона.
+- `IndentationError` — неправильные отступы в коде.
+- `KeyError` — ключ не найден в словаре.
+- `KeyboardInterrupt` — пользователь нажал клавишу прерывания.
+- `MemoryError` — операции не хватает памяти.
+- `NameError` — попытка использовать переменную или функцию, которая не определена.
+- `OSError` — ошибка, связанная с операционной системой, например при проблеме с файловой системой.
+- `RuntimeError` — общая ошибка, которая не подпадает под другие категории.
+- `SyntaxError` — ошибка в синтаксисе кода.
+- `TabError` — ошибка смешивания табуляции и пробелов для отступов.
+- `TypeError` — ошибка, которая возникает, когда операция или функция применяются к объекту неподходящего типа.
+- `ValueError` — возникает, когда функция получает аргумент правильного типа, но некорректного знания.
+- `ZeroDivisionError` — ошибка при делении на ноль.
+
+## Обработка исключений по умолчанию
+
+По умолчанию исключения в Python обрабатываются с помощью блоков `try`, `except`, `else` и `finally`. Стандартная структура этой конструкции такая:
+
+```python
+try:
+ # код, который может вызвать исключение
+except SomeException:
+ # код для обработки исключения, которое может возникнуть в блоке try
+else:
+ # код, который выполняется, если исключения не было
+finally:
+ # код, который выполняется в любом случае
+```
+
+Сначала Python выполняет код в блоке `try`. Если в блоке `try` возникает исключение, Python останавливает выполнение кода в этом блоке и начинает искать блок `except`, который соответствует типу возникшего исключения. Если соответствующий блок найден, выполняет код в этом блоке, если нет — исключение передаётся выше по цепочке вызовов (это может привести к завершению программы, если исключение не будет нигде поймано). Если в блоке `try` не возникло исключение, код выполняется в блоке `else` (если он есть). Затем, наконец, выполняется код в блоке `finally`, независимо от того, было исключение или нет.
+
+Блок `else` бывает полезен, когда нам нужно выполнить какой-то код только в том случае, если блок try завершился успешно. Например, когда нам нужно разделить логику обработки ошибок и основную логику, чтобы код был более читаемым и структурированным. Также это может быть полезно, если мы хотим выполнить какую-то операцию только после успешного выполнения блока try, например когда она требует много ресурсов.
+
+Рассмотрим простой пример с операцией деления на 0:
+
+```python
+# выполняем попытку деления
+try:
+ result = 10 / 0
+# исключение при делении на 0
+except ZeroDivisionError:
+ print("Деление на ноль!")
+# этот блок не будет выполнен, поскольку возникло исключение
+else:
+ print("Ошибок нет, результат:", result)
+# этот блок выполнится в любом случае
+finally:
+ print("Блок finally выполнен.")
+```
+
+Блок `finally` гарантирует, что критические операции, такие как освобождение ресурсов или закрытие файлов, будут выполнены независимо от того, произошла ошибка или нет.
+
+## Обработка нескольких типов исключений
+
+Для обработки различных типов исключений, которые могут возникнуть в блоке `try`, можно использовать несколько блоков `except`. Это позволит обрабатывать каждое исключение по-своему, в зависимости от типа исключения.
+
+Например, у нас есть операция деления на какое-то число, которое может ввести пользователь, в том числе из заданного списка. Мы можем предусмотреть исключения для ввода, который не может быть преобразован в целое число, для деления на 0 и для случаев, когда пользователь указывает индекс за пределами списка:
+
+```python
+try:
+ # код, который может вызвать различные исключения
+ x = int(input("Введите число: "))
+ result = 10 / x
+ my_list = [0, 1, 2, 3]
+ print(10 / my_list[x])
+# это исключение будет вызвано, если пользователь введёт некорректное значение
+except ValueError:
+ print("Это не число! Пожалуйста, введите корректное число.")
+# это исключение будет вызвано, если пользователь введёт 0
+except ZeroDivisionError:
+ print("Деление на 0 невозможно! Пожалуйста, введите число, отличное от 0.")
+# это исключение будет вызвано, если пользователь введёт число за пределами индексов списка my_list
+except IndexError:
+ print("Индекс выходит за пределы списка! Пожалуйста, введите число от 0 до 3.")
+```
+
+Если мы хотим обработать все три типа исключений при помощи одного блока, это можно сделать при помощи переменной, которая будет содержать объект исключения и может быть использована, чтобы получить дополнительную информацию об ошибке.
+
+```python
+try:
+ # код, который может вызвать различные исключения
+ x = int(input("Введите число: "))
+ result = 10 / x
+ my_list = [0, 1, 2, 3]
+ print(10 / my_list[x])
+# это исключение будет вызвано, если пользователь введёт некорректное значение, 0 или число за пределами индексов списка my list
+except (ValueError, ZeroDivisionError, IndexError) as e:
+ print(f"Произошла ошибка: {e}")
+```
+## Обработка всех типов исключений
+
+Если мы хотим обработать все возможные исключения, можно использовать блок except, не указывая конкретный тип исключения:
+
+```python
+try:
+ # код, который может вызвать различные исключения
+ x = int(input("Введите число: "))
+ result = 10 / x
+ my_list = [0, 1, 2, 3]
+ print(10 / my_list[x])
+except Exception as e:
+ print(f"Произошла ошибка: {e}")
+```
+
+Общее исключение без конкретизации типа нужно применять с осторожностью, поскольку это может затруднить отладку и скрыть реальные ошибки, которые лучше обрабатывать отдельно.
+
+## Использование оператора raise
+
+Оператор raise используется для генерации существующих встроенных исключений, для создания пользовательских исключений или для повторного вызова исключения, которое было перехвачено. С его помощью можно узнавать о проблемах и ошибках, которые возникли в коде, и контролировать то, как эти ошибки обрабатываются.
+
+Генерация встроенных исключений будет выглядеть так:
+
+```python
+def divide(a, b):
+ if b == 0:
+ # генерируем встроенное исключение
+ raise ZeroDivisionError("Деление на ноль невозможно!")
+ return a / b
+
+# исключение перехватывается в этом блоке
+try:
+ result = divide(10, 0)
+except ZeroDivisionError as e:
+ print(f"Ошибка: {e}")
+```
+
+Собственные исключения бывают полезны для точного описания определённых сценариев ошибок или нештатных ситуаций, которые могут возникнуть в коде. С помощью собственных исключений можно повысить читаемость кода, сделать его надёжнее и обеспечить более точную обработку ошибок. При этом можно определить специализированные обработчики для различных типов ошибок, так что это упрощает отладку и управление ошибками. Создать пользовательское исключение можно так:
+
+```python
+# объявляем класс пользовательского исключения
+class NegativeNumberError(Exception):
+ """Класс для пользовательского исключения при отрицательном числе."""
+ pass
+
+def check_positive(number):
+ if number < 0:
+ # генерируем пользовательское исключение
+ raise NegativeNumberError("Число не должно быть отрицательным!")
+ return number
+
+try:
+ check_positive(-5)
+except NegativeNumberError as e:
+ print(f"Ошибка: {e}")
+```
+
+Можно повторно вызвать исключение, которое было перехвачено, чтобы оно могло быть обработано на более высоком уровне:
+
+```python
+def process_value(value):
+ try:
+ if value < 0:
+ # генерируем исключение
+ raise ValueError("Значение не должно быть отрицательным!")
+ except ValueError as e:
+ print(f"Перехвачено исключение: {e}")
+ # повторно генерируем исключение
+ raise
+
+try:
+ process_value(-10)
+except ValueError as e:
+ print(f"Повторно перехвачено исключение: {e}")
+```
+## Наследование от классов исключений
+
+Пользовательские исключения можно создавать путём наследования от встроенных классов исключений или от любого другого пользовательского исключения. Таким образом можно описывать различные сценарии ошибок, что делает код более управляем. Пользовательские исключения могут содержать дополнительные атрибуты и методы для предоставления информации об ошибке, за счёт чего можно проще отлаживать код и быстрее исправлять проблемы.
+
+```python
+class MyCustomError(Exception):
+ """Пользовательское исключение для представления специфического типа ошибки."""
+
+ def __init__(self, message):
+ self.message = message
+ super().__init__(self.message)
+
+try:
+ # генерируем пользовательское исключение
+ raise MyCustomError("Это моё пользовательское исключение!")
+except MyCustomError as e:
+ print(f"Поймано пользовательское исключение: {e}")
+```
+## Логирование исключений
+
+С помощью модуля `logging` бывает полезно вести журнал исключений для обнаружения проблем и отладки приложений. При этом исключения могут быть записаны в файл, выведены в консоль, отправлены на электронную почту и так далее.
+
+```python
+import logging
+
+# настраиваем конфигурацию логирования
+logging.basicConfig(filename='app.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
+
+try:
+ # код, который может вызывать исключения
+ result = 10 / 0
+except Exception as e:
+ # записываем сообщение об ошибке в журнал
+ logging.error("Произошло исключение: %s", e)
+```
+
+#==============================================
+```python
+try:
+ print('10' / 0)
+except Exception as e:
+ print(e)
+except ZeroDivisionError as e:
+ print(type(e))
+ print(e)
+except TypeError as e:
+ print(type(e))
+ print(e)
+else: # выполняется если ошибок нет
+ print("There was no ERROR !")
+finally: # выполняется в любом случае
+ print('Continue...')
+
+########################################################
+
+def divide_nums(a, b):
+ if b == 0:
+ raise ValueError("Second argument can't be 0")
+ return a / b
+
+
+try:
+ divide_nums(10, 0)
+except Exception as e:
+ print(isinstance(e, Exception))
+ print(e)
+
+
+print('Continue...')
+
+########################################################
+
+def image_info(img):
+ if ('image_id' not in img) or ('image_title' not in img):
+ raise TypeError("Keys image_id and image_title must be present")
+ return f"Image '{img['image_title']}' has id {img['image_id']}"
+
+
+print(image_info({'image_title': 'My cat', 'image_id': 123}))
+
+
+try:
+ print(image_info({'image_id': 123}))
+except TypeError as e:
+ print(e)
+```
+
+
+
diff --git a/PYTHON/Translators.md b/PYTHON/Translators.md
new file mode 100644
index 0000000..c0d001e
--- /dev/null
+++ b/PYTHON/Translators.md
@@ -0,0 +1,20 @@
+
+**Translators** — это лёгкая и мощная библиотека для Python, которая позволяет выполнять переводы текста через популярные онлайн-сервисы, такие как Google Translate, Microsoft Translator, DeepL и многие другие.
+
+— Поддержка множества переводчиков: Google Translate, DeepL, Microsoft Translator, и т.д.
+— Перевод текста на более чем 100 языков.
+— Простота в использовании: подключение к API не требуется!
+— Возможность указания источника и целевого языка.
+
+```python
+import translators as ts
+
+# Перевод через DeepL
+result = ts.deepl("Как дела?", from_language="ru", to_language="en")
+print(result)
+
+# Вывод: How are you?
+```
+✔️ **Установка**
+
+`pip install translators \ No newline at end of file
diff --git a/PYTHON/WITH.md b/PYTHON/WITH.md
new file mode 100644
index 0000000..437e03d
--- /dev/null
+++ b/PYTHON/WITH.md
@@ -0,0 +1,28 @@
+Ещё одна мощная и идиоматичная конструкция в Python — это менеджеры контекста и оператор ==with==. Они кардинально упрощают работу с ресурсами, которые требуют обязательной "очистки" после использования, например, файлы, сетевые соединения или блокировки потоков.
+
+Представьте, что вы открываете файл для записи. Крайне важно его потом закрыть, иначе данные могут не сохраниться, а файловый дескриптор останется занят. Классический, но многословный способ гарантировать закрытие — использовать `try...finally:
+```sh
+f = open('my_file.txt', 'w')
+try:
+    f.write('Важные данные!')
+    # ... тут может произойти ошибка
+finally:
+    f.close() # Закроется в любом случае
+```
+
+Работает, но выглядит громоздко. Python предлагает элегантную альтернативу с with:
+```sh
+with open('my_file.txt', 'w') as f:
+    f.write('Важные данные!')
+    # ... тут тоже может произойти ошибка
+# Как только блок `with` завершается (нормально или из-за ошибки),
+# Python АВТОМАТИЧЕСКИ вызывает f.close()!
+```
+
+**В чём магия with?**
+
+Объекты, используемые с with (как тот, что возвращает open()), реализуют так называемый "протокол менеджера контекста". У них есть специальные методы (__enter__ и __exit__). Оператор with вызывает __enter__ при входе в блок и гарантированно вызывает __exit__ при выходе из блока, передавая туда информацию об исключениях, если они были. Именно в __exit__ и происходит освобождение ресурса (закрытие файла).
+
+
+
+Оператор with — это стандарт де-факто для управления ресурсами в Python. Он делает код не только короче, но и значительно безопаснее и понятнее. \ No newline at end of file
diff --git a/PYTHON/any, all.md b/PYTHON/any, all.md
new file mode 100644
index 0000000..56a60e6
--- /dev/null
+++ b/PYTHON/any, all.md
@@ -0,0 +1,22 @@
+Когда нужно проверить список условий, any и all делают код более читаемым и удобным.
+
+```python
+numbers = [2, 4, 6, 8]
+
+# Проверяем, есть ли хотя бы одно нечётное число
+print(any(n % 2 != 0 for n in numbers))  # False
+
+# Проверяем, все ли числа чётные
+print(all(n % 2 == 0 for n in numbers))  # True
+```
+
+**📌 Почему это удобно?**
+
+🟢 any() возвращает True, если хотя бы одно условие выполняется
+
+🟢 all() возвращает True, если выполняются все условия
+
+🟢 Упрощает код вместо громоздких циклов
+
+
+🗣️ **Используйте** any() **и** all()**, когда нужно быстро проверить списки условий без явных циклов**. \ No newline at end of file
diff --git a/PYTHON/argparse.md b/PYTHON/argparse.md
new file mode 100644
index 0000000..b5019b5
--- /dev/null
+++ b/PYTHON/argparse.md
@@ -0,0 +1,5 @@
+Библиотека argparse является стандартным инструментом для обработки аргументов командной строки в Python. **Она позволяет легко определить и обработать аргументы, передаваемые программе при запуске из командной строки.**
+
+В данном примере мы просто выводим значения аргументов на экран, но в реальном приложении вы можете использовать их для выполнения конкретных действий, например, **чтения входного файла, обработки данных и записи результата в выходной файл.**
+
+![[telegram-cloud-photo-size-4-6016931255788092228-y.jpg]]
diff --git a/PYTHON/dir().md b/PYTHON/dir().md
new file mode 100644
index 0000000..1fb2745
--- /dev/null
+++ b/PYTHON/dir().md
@@ -0,0 +1,17 @@
+**Функция dir()**
+
+![[telegram-cloud-photo-size-4-5850234924966199387-w.jpg]]
+
+_Функция dir() используется для получения списка атрибутов и методов объекта._
+
+Она принимает объект в качестве аргумента и возвращает список его атрибутов и методов в виде строк.
+
+
+
+Для модулей dir() возвращает список определенных в нем имен. Для классов возвращаются его атрибуты, методы и внутренние функции.
+
+
+
+С помощью dir() можно получить список ключевых слов языка, передав builtin или builtins в качестве аргумента.
+
+Без аргументов dir() возвращает список имен, определенных в текущей области видимости. \ No newline at end of file
diff --git a/PYTHON/exec.md b/PYTHON/exec.md
new file mode 100644
index 0000000..90c622f
--- /dev/null
+++ b/PYTHON/exec.md
@@ -0,0 +1,6 @@
+**Как обратиться к коду из другого файла**
+
+Если вам нужно выполнить код, содержащийся в другом файле, вы можете использовать встроенную функцию exec.
+
+![[telegram-cloud-photo-size-2-5269638479933982570-y.jpg]]
+
diff --git a/PYTHON/hash() - хэширование.md b/PYTHON/hash() - хэширование.md
new file mode 100644
index 0000000..6f9e143
--- /dev/null
+++ b/PYTHON/hash() - хэширование.md
@@ -0,0 +1,19 @@
+Хэш — это целое число фиксированного размера, которое идентифицирует определенное значение. Каждое уникальное значение должно иметь свой собственный хэш.
+
+Для хэширования значений есть встроенная (её не надо устанавливать или импортировать) функция hash(). Используется в основном для сравнения разных объектов — сравнивать хэши легче и выгоднее.
+
+Однако изменяемые объекты по типу списков и словарей нельзя хэшировать — интерпретатор выдаст ошибку.
+
+Кстати, есть две пасхалки. Хэш бесконечности равен первым цифрам числа Пи, а хэш Not a Number равен нулю.
+
+А ещё бывают коллизии: например, хэши чисел -1 и -2 одинаковы.
+
+```python
+hash(42) # 42
+hash(1) # 1
+hash((1, 2, 3, 4)) # 590899387183067792
+hash('hi') # 2211653411235407625
+hash(float('inf')) # 314159
+hash(float 'nan' )) # 0
+hash(-1) == hash(-2) # True
+```
diff --git a/PYTHON/itertools.md b/PYTHON/itertools.md
new file mode 100644
index 0000000..b4b4141
--- /dev/null
+++ b/PYTHON/itertools.md
@@ -0,0 +1,8 @@
+**Перебираем все возможные 8 значные пароли состоящие из букв и цифр с помощью Python**
+
+В этом примере используется модуль itertools, который позволяет генерировать все возможные комбинации символов из списка characters длиной password_length. Затем перебираются все комбинации и выводятся на экран.
+
+Обратите внимание, что перебор всех возможных 8-значных паролей может занять большое количество времени, особенно если количество символов в characters большое. Будьте осторожны и убедитесь, что ваше намерение соответствует законам и этическим правилам.
+
+![[telegram-cloud-photo-size-2-5255812146984502723-y.jpg]]
+
diff --git a/PYTHON/keys() + &.md b/PYTHON/keys() + &.md
new file mode 100644
index 0000000..b9de8ab
--- /dev/null
+++ b/PYTHON/keys() + &.md
@@ -0,0 +1,8 @@
+**Находим общие ключи в двух словарях**
+
+В этом примере мы используем метод keys() для получения наборов ключей из словарей dict1 и dict2, а затем применяем оператор & для нахождения общих ключей.
+
+Обратите внимание, что как множества, так и словари не сохраняют порядок элементов/ключей, поэтому порядок результатов может быть произвольным.
+
+![[telegram-cloud-photo-size-2-5264765212471446942-y.jpg]]
+
diff --git a/PYTHON/nmcli - Работа с сетевыми интерфейсами.md b/PYTHON/nmcli - Работа с сетевыми интерфейсами.md
new file mode 100644
index 0000000..4a8c1b5
--- /dev/null
+++ b/PYTHON/nmcli - Работа с сетевыми интерфейсами.md
@@ -0,0 +1,56 @@
+
+`nmcli` — это удобный инструмент командной строки для управления сетевыми интерфейсами в Linux. С помощью Python его можно использовать для автоматизации таких задач, как: подключение к Wi-Fi, работа с Ethernet или настройки VPN.
+
+Для начала, убедимся, что nmcli установлен. Проверим его версию:
+
+`nmcli --version
+
+Затем установим библиотеку subprocess для работы с командами оболочки:
+
+`pip install subprocess
+
+➡️ **1. Получение информации о сетях:**
+
+```python
+import subprocess
+
+def list_wifi():
+
+    result = subprocess.run(['nmcli', '-t', '-f', 'SSID,SIGNAL', 'dev', 'wifi'], 
+
+                             stdout=subprocess.PIPE, text=True)
+
+    print("Доступные сети:")
+
+    print(result.stdout)
+
+
+
+list_wifi()
+```
+Данный пример выведет список SSID и уровень сигнала доступных Wi-Fi сетей.
+
+➡️ **2. Подключение к WiFi с паролем:**
+
+```python
+def connect_wifi(ssid, password):
+
+    command = ['nmcli', 'dev', 'wifi', 'connect', ssid, 'password', password]
+
+    result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
+
+    if result.returncode == 0:
+
+        print(f"Успешно подключено к {ssid}")
+
+    else:
+
+        print(f"Ошибка подключения: {result.stdout}")
+
+
+
+connect_wifi("PyLinux_WIFI", "CoolPassword")
+```
+В этом же примере, автоматически подключаемся к указанной сети.
+
+Это лишь пару примеров, которые призваны показать принцип работы. А дальше, вы можете использовать это для автоматизации подключения к сетям в скриптах, разработки своей утилиты для управления сетями или мониторинга состояния сети. \ No newline at end of file
diff --git a/PYTHON/pandas.md b/PYTHON/pandas.md
new file mode 100644
index 0000000..748f66b
--- /dev/null
+++ b/PYTHON/pandas.md
@@ -0,0 +1,2 @@
+![[telegram-cloud-photo-size-2-5350524228924991162-y.jpg]]
+
diff --git a/PYTHON/partition().md b/PYTHON/partition().md
new file mode 100644
index 0000000..a7aa52a
--- /dev/null
+++ b/PYTHON/partition().md
@@ -0,0 +1,8 @@
+Разбивает стороку по заданной подстроке.
+После этого результат возвращается в виде кортежа.
+При этом подстрока по которой осуществлялась разбика, тоже входит в кортеж.
+```python
+str1 = "I want to become a Python developer"
+print(str1.partition(' want '))
+# ('I', ' want ', 'to become a Python developer')
+```
diff --git a/PYTHON/poyo.md b/PYTHON/poyo.md
new file mode 100644
index 0000000..b8028cc
--- /dev/null
+++ b/PYTHON/poyo.md
@@ -0,0 +1,35 @@
+⚙️ **Библиотека Python: Poyo**
+
+**Poyo** — это лёгкая библиотека для разбора (парсинга) YAML-файлов в виде словарей Python. Она особенно полезна для простых конфигурационных файлов и легковесных проектов.
+
+Подходит для разработчиков, которым нужно быстро и без сложных зависимостей читать YAML-файлы в Python.
+
+⚙️ **Пример использования**
+```python
+import poyo
+
+yaml_content = """
+name: PyLinux
+version: 1.0
+features:
+  - Linux
+  - Python
+  - DevOps
+"""
+
+# Разбор YAML в словарь
+parsed_data = poyo.parse_string(yaml_content)
+
+print(parsed_data)
+
+# Вывод: {'name': 'PyLinux', 'version': 1.0, 'features': ['Linux', 'Python', 'DevOps']}
+```
+**➕ Преимущества:**
+
+🟢 Минималистичный парсер YAML
+🟢 Без сложных зависимостей (в отличие от PyYAML)
+🟢 Удобно для небольших конфигурационных файлов
+
+✔️ **Установка**
+
+`pip install poyo
diff --git a/PYTHON/pprint().md b/PYTHON/pprint().md
new file mode 100644
index 0000000..9fb735a
--- /dev/null
+++ b/PYTHON/pprint().md
@@ -0,0 +1,17 @@
+_Функция pprint() предназначена для красивого форматированного вывода данных._
+
+![[telegram-cloud-photo-size-4-5850234924966199390-w.jpg]]
+
+pprint() форматирует произвольные структуры данных, такие как вложенные списки, словари, кортежи для удобочитаемого вывода.
+
+По умолчанию выводит данные с отступами и переносами, чтобы структура была наглядной.
+
+
+
+Может принимать ширину вывода в количестве символов для лучшего форматирования.
+
+Имеет параметры depth и compact для настройки уровня вложенности и режима компактного вывода.
+
+
+
+Функционал pprint() доступен через модуль pprint в стандартной библиотеке. \ No newline at end of file
diff --git a/PYTHON/sleep().md b/PYTHON/sleep().md
new file mode 100644
index 0000000..3c91c5e
--- /dev/null
+++ b/PYTHON/sleep().md
@@ -0,0 +1,8 @@
+**Усыпляем программу**
+
+Иногда возникает необходимость остановить выполнение программы, чтобы можно было выполнить несколько других операций, или просто из-за требуемой утилиты. В такой ситуации может пригодиться функция sleep(), которая обеспечивает точный и гибкий способ остановки потока кода на любой период времени.
+
+В нашем примере время начала и время окончания будут напечатаны с задержкой в ​​6 секунд.
+
+![[telegram-cloud-photo-size-2-5363884033936834909-y.jpg]]
+
diff --git a/PYTHON/tabulate.md b/PYTHON/tabulate.md
new file mode 100644
index 0000000..ad838f7
--- /dev/null
+++ b/PYTHON/tabulate.md
@@ -0,0 +1,12 @@
+**Библиотека Tabulate предназначена для красивого вывода табличных данных.** Она поддерживает различные форматы таблиц и позволяет легко выводить данные в виде таблицы с заголовками и разделителями.
+
+**Основные возможности библиотеки Tabulate:**
+
+- Вывод табличных данных в различных форматах (plain, simple, grid, pipe, orgtbl, tsv, html, latex, ts, и др.).
+- Автоматическое определение заголовков таблицы.
+- Поддержка разных типов данных (числа, строки, списки, кортежи, словари и др.).
+
+В результате работы кода из примера выводится таблица с данными о фруктах, их цене и количестве, оформленная в формате _"grid"_.
+
+![[telegram-cloud-photo-size-4-5776315316576566340-y.jpg]]
+
diff --git a/PYTHON/Библиотеки для ленивых.md b/PYTHON/Библиотеки для ленивых.md
new file mode 100644
index 0000000..66f7096
--- /dev/null
+++ b/PYTHON/Библиотеки для ленивых.md
@@ -0,0 +1,83 @@
+👩‍💻 когда хочется больше делать с меньшими усилиями
+
+Каждый программист знает: кодить можно быстрее, если использовать проверенные решения. В Python куча библиотек, которые делают работу за тебя — не нужно изобретать велосипед.
+
+ 1. **`pathlib` — путь к лёгкости работы с путями**
+
+Ты всё ещё юзаешь os.path? Пора остановиться. pathlib — это объектно-ориентированная альтернатива, которая позволяет работать с путями проще и чище.
+
+```python
+from pathlib import Path
+
+p = Path("/home/user/docs")
+print(p / "file.txt")  # /home/user/docs/file.txt
+```
+
+👉 Это значительно читаемее и проще, чем старые подходы с os.path.
+
+ 2. **`requests` — HTTP без головной боли**
+
+HTTP-запросы — одна из самых часто используемых операций, но с ними можно запутаться. requests делает этот процесс понятным и удобным.
+
+```python
+import requests
+
+response = requests.get('https://api.example.com/data')
+print(response.json())
+```
+
+👉 Всё! Нет лишнего кода, никаких ненужных сложностей.
+
+ 3. **`pydantic` — валидация данных с минимальными усилиями**
+
+Если тебе нужно проверять данные, а писать вручную кучу кода — не хочется, используй pydantic. Эта библиотека позволяет быстро проверять и валидировать данные, обрабатывая JSON, параметры и модели.
+
+```python
+from pydantic import BaseModel
+
+class User(BaseModel):
+    name: str
+    age: int
+
+user = User(name="Alice", age=30)
+print(user)
+```
+
+👉 Валидация без лишних усилий и работы с типами. Всё автоматически.
+
+ 4. **`attrs` — создание классов без ненужного кода**
+ 5.
+Если ты создаёшь классы, но не хочешь писать лишние методы вроде __init__, __repr__, __eq__, то тебе в помощь attrs. Эта библиотека позволяет легко создавать классы с автоматическим заполнением всех необходимых методов.
+
+```python
+import attr
+
+@attr.s
+class Point:
+    x = attr.ib()
+    y = attr.ib()
+
+p = Point(3, 4)
+print(p)  # Point(x=3, y=4)
+```
+
+👉 Быстро, удобно, без лишних усилий.
+
+ 5. **`click` — консольные приложения без мук**
+
+Создание CLI-интерфейсов может быть утомительным, но с click это делается быстро и без боли. Ты можешь создавать простые и мощные консольные команды с минимальными усилиями.
+
+```python
+import click
+
+@click.command()
+@click.argument('name')
+def greet(name):
+    click.echo(f"Hello, {name}!")
+
+greet()
+```
+
+👉 Лёгкая настройка, удобная обработка аргументов и флагов.
+
+🗣️ **Запомни**: используй библиотеки, чтобы минимизировать шаблонный код, повысить читаемость и улучшить производительность разработки. \ No newline at end of file
diff --git a/PYTHON/Возврат нескольких значений.md b/PYTHON/Возврат нескольких значений.md
new file mode 100644
index 0000000..b1e54e0
--- /dev/null
+++ b/PYTHON/Возврат нескольких значений.md
@@ -0,0 +1,5 @@
+из функции с помощью именованных кортежей:
+
+![[telegram-cloud-photo-size-2-5406976059292904279-x.jpg]]
+
+По умолчанию функция возвращает одно значение и останавливает выполнение функции. Есть несколько способов вернуть несколько значений. Например, вы можете использовать именованный кортеж из модуля collections. Это поможет вам определить структуру возвращаемых значений. В коде на картинке мы создаем именованный кортеж и назначаем поля: имя, возраст и автомобиль, а затем присваиваем эти значения переменным вне функции! \ No newline at end of file
diff --git a/PYTHON/Генерируем QR-код.md b/PYTHON/Генерируем QR-код.md
new file mode 100644
index 0000000..d5dde9c
--- /dev/null
+++ b/PYTHON/Генерируем QR-код.md
@@ -0,0 +1,10 @@
+Необходимый пакет ставится командой
+`pip install qrcode
+. Также должна быть установлена библиотека pillow.
+
+Программа с картинки создаёт QR-код, который при сканировании ведёт пользователя на сайт google, и сохраняет его в новую картинку.
+
+Теперь вы можете генерировать QR-коды в своих приложениях.
+
+![[Pasted image 20250501031129.png]]
+
diff --git a/PYTHON/Контекстные менеджеры.md b/PYTHON/Контекстные менеджеры.md
new file mode 100644
index 0000000..f685d3b
--- /dev/null
+++ b/PYTHON/Контекстные менеджеры.md
@@ -0,0 +1,8 @@
+Контекстные менеджеры позволяют задать поведение при работе с конструкцией with: при входе и выходе из блока. Это упрощает работу с ресурсами в части их захвата и освобождения; транзакциями, когда нужно либо полностью закончить транзакцию, либо откатить ее целиком.
+
+Простейшей функцией, использующей данный принцип, является функция open. Каждый раз, когда мы открываем файл, нам необходимо его закрыть, чтобы вытолкнуть выходные данные на диск (на самом деле Python вызывает метод close автоматически, но явное его использование является хорошим тоном).
+
+Чтобы каждый раз не вызывать метод close, мы можем воспользоваться контекстным менеджером функции open, который автоматически закроет файл после выхода из блока.
+
+![[Pasted image 20250501031423.png]]
+
diff --git a/PYTHON/Копируем файл в другой каталог.md b/PYTHON/Копируем файл в другой каталог.md
new file mode 100644
index 0000000..75026af
--- /dev/null
+++ b/PYTHON/Копируем файл в другой каталог.md
@@ -0,0 +1,4 @@
+Метод ==Shutil.copy()== в Python используется для копирования содержимого исходного файла в целевой файл или каталог. Он также сохраняет режим разрешений файла, но другие метаданные файла, такие как время создания и изменения файла, не сохраняются.
+
+![[Pasted image 20250501030617.png]]
+
diff --git a/PYTHON/Множественный ввод.md b/PYTHON/Множественный ввод.md
new file mode 100644
index 0000000..3098ec8
--- /dev/null
+++ b/PYTHON/Множественный ввод.md
@@ -0,0 +1,2 @@
+![[2025-02-20 04.18.59.jpg]]
+
diff --git a/PYTHON/Моржовый оператор.md b/PYTHON/Моржовый оператор.md
new file mode 100644
index 0000000..23697c9
--- /dev/null
+++ b/PYTHON/Моржовый оператор.md
@@ -0,0 +1,17 @@
+```python
+# Добавлен в Python 3.8, позволяет присваивать значение прямо внутри выражения.
+
+# Без := :
+
+data = input("Введите число: ")
+if data.isdigit():
+    print(int(data) * 2)
+
+# С := становится короче:
+
+if (data := input("Введите число: ")).isdigit():
+    print(int(data) * 2)
+
+# Полезно для сокращения кода в if и while!
+```
+
diff --git a/PYTHON/ОБНОВЛЕНИЕ до последней версии.md b/PYTHON/ОБНОВЛЕНИЕ до последней версии.md
new file mode 100644
index 0000000..7297876
--- /dev/null
+++ b/PYTHON/ОБНОВЛЕНИЕ до последней версии.md
@@ -0,0 +1,98 @@
+```shell
+python3 --version
+sudo apt update && sudo apt upgrade -y
+sudo add-apt-repository ppa:deadsnakes/ppa
+sudo apt update
+apt list | grep python3.12
+
+sudo apt install python3.12
+python3.12 --version
+# правильно создать вирт среду в приложении:
+python3.12 -m venv env
+source env/bin/activate
+# можно создать псевдоним:
+echo "alias py=/usr/bin/python3" >> ~/.bashrc # ~/.zshrc
+echo "alias python=/usr/bin/python3" >> ~/.bashrc # ~/.zshrc
+```
+
+```shell
+# Установка зависимостей Python:
+sudo apt-get install build-essential checkinstall
+sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev \ libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev
+
+# Скачивание Python:
+cd /opt
+sudo wget https://www.python.org/ftp/python/3.9.2/Python-3.9.2.tgz
+sudo tar xzf Python-3.9.2.tgz
+
+# Установка из исходных кодов:
+cd Python-3.9.2$ sudo ./configure —enable-optimizations
+sudo make altinstall
+
+# После того, как команды будут выполнены вы можете увидеть python3.9 в вашей директории /usr/local/bin/
+# Проверка версии Python
+cd /usr/local/bin/
+ls
+
+# Включение альтернативной версии Python
+update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1
+update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.9 2
+
+# Список установленных версий Python
+update-alternatives --list python3
+/usr/bin/python3.6
+/usr/local/bin/python3.8
+
+# Переключение между версиями
+update-alternatives --config python3
+
+
+# CLI для переключения активной версии Python
+Selection Path Priority Status
+------------------------------------------------------------
+ 0 /usr/bin/python3.8 3 auto mode
+* 1 /usr/bin/python3.8 3 manual mode
+ 2 /usr/local/bin/python3.10 2 manual mode
+
+Press <enter> to keep the current choice[*], or type selection number:
+
+
+# Установка pip3
+apt install python3-pip
+python3.9 -m pip install --upgrade pip
+
+# Скажите Ubuntu что python это python3
+apt-get install python-is-python3
+```
+
+```shell
+# Запускайте скрипты на Python с помощью Shebang:
+
+#!/usr/bin/env python
+print("Привет, мир!")
+
+chmod +x hello.py
+
+./hello.py
+
+# Запускать скрипты с аргументами скрипта:
+
+#!/usr/bin/env python
+import argparse
+
+def main():
+ parser = argparse.ArgumentParser(description='Скрипт, который приветствует пользователя.')
+ parser.add_argument('name', help='Имя человека, которого нужно поприветствовать')
+ args = parser.parse_args()
+ greet(args.name)
+
+def greet(name):
+ print(f'Hello, {name}!')
+
+if __name__ == '__main__':
+ main()
+```
+
+```shell
+python hello.py John
+```
diff --git a/PYTHON/ОТЛАДКА КОДА.md b/PYTHON/ОТЛАДКА КОДА.md
new file mode 100644
index 0000000..df19336
--- /dev/null
+++ b/PYTHON/ОТЛАДКА КОДА.md
@@ -0,0 +1,32 @@
+🖥 **Как отлаживать Python-код, не выходя из редактора — с помощью встроенного модуля** pdb**.**
+
+Часто, когда код не работает как надо, мы начинаем закидывать print()-ами. Но это неудобно, медленно и мусорит код. Вместо этого вставь в нужное место строчку:
+
+```python
+import pdb; pdb.set_trace()
+```
+Когда выполнение дойдет до этой строки, ты попадешь в интерактивную консоль отладчика прямо в терминале. Дальше можно:
+
+- n (next) — перейти к следующей строке;
+- s (step) — зайти внутрь функции;
+- c (continue) — продолжить выполнение;
+- l (list) — показать текущий контекст;
+- p var — вывести значение переменной var.
+
+```python
+def calc(a, b):
+    import pdb; pdb.set_trace()
+    result = a + b
+    return result
+
+calc(2, 3)
+```
+На строке с ==`pdb.set_trace()`== ты остановишься и сможешь изучить, что происходит внутри.
+
+Зачем это нужно?
+
+- Понять, **почему что-то идет не так**.
+- Посмотреть, **какие значения у переменных** прямо в момент ошибки.
+- Быстро отладить **без запуска IDE** — удобно в Docker, SSH или при работе с cron.
+
+Попробуй — один раз освоишь, и уже не захочешь возвращаться к print(). \ No newline at end of file
diff --git a/PYTHON/Программа-напоминалка на Python.md b/PYTHON/Программа-напоминалка на Python.md
new file mode 100644
index 0000000..d0c28e1
--- /dev/null
+++ b/PYTHON/Программа-напоминалка на Python.md
@@ -0,0 +1,156 @@
+### Установка и импорт библиотек
+
+Перед написанием кода нам понадобится установить библиотеку **schedule** для планирования напоминаний и **plyer** для отображения системных уведомлений Windows. Сделать это можно через **pip**. Для этого нужно перейти в терминал/командную строку, прописать **pip install schedule plyer**, нажать Enter и ждать установки.
+
+```bash
+pip install schedule plyer
+```
+
+После инсталляции импортируем все необходимые библиотеки:
+
+```python
+import schedule
+import time
+from plyer import notification
+```
+
+- **schedule** — для планирования напоминаний;
+- **time** — для работы с задержками;
+- **plyer** — для отображения системных уведомлений.
+
+### Написание кода программы-напоминалки на Python
+
+Первым делом создадим функцию **show_notification()**, которая будет вызывать системное уведомление.
+
+У функции будет присутствовать один параметр, а именно:
+
+- message — текст оповещения, переданный пользователем.
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+```
+
+Внутри функции через объект **notification** обратимся к методу **notify()**, чтобы создать уведомление с заголовком «Напоминание», переданным пользователем текстом и длительностью равной 10 секунд:
+
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+ notification.notify(
+ title="Напоминание",
+ message=message,
+ timeout=10
+ )
+```
+
+Теперь создадим функцию **add_reminder()**, которая будет создавать напоминания.
+
+У функции будет присутствовать два параметра, а именно:
+
+- text — текст напоминания;
+- reminder_time — время, в которое должно сработать напоминание.
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+ notification.notify(
+ title="Напоминание",
+ message=message,
+ timeout=10
+ )
+
+
+def add_reminder(text, reminder_time):
+```
+
+Внутри функции будет добавляться задача в планировщик, чтобы каждый день в указанное время **reminder_time** вызывалась функция **show_notification()** с переданным текстом:
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+ notification.notify(
+ title="Напоминание",
+ message=message,
+ timeout=10
+ )
+
+
+def add_reminder(text, reminder_time):
+ schedule.every().day.at(reminder_time).do(show_notification, message=text)
+ print(f"Напоминание добавлено на {reminder_time}: {text}")
+```
+
+Теперь добавим точку входа, внутри которой будет находиться бесконечный цикл:
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+ notification.notify(
+ title="Напоминание",
+ message=message,
+ timeout=10
+ )
+
+
+def add_reminder(text, reminder_time):
+ schedule.every().day.at(reminder_time).do(show_notification, message=text)
+ print(f"Напоминание добавлено на {reminder_time}: {text}")
+
+
+if __name__ == "__main__":
+ while True:
+```
+
+Внутри бесконечного цикла пользователю будет предложено ввести текст напоминания и время, в которое должно произойти напоминание. Далее будет идти вложенный бесконечный цикл, который ожидает выполнения напоминания.
+
+```python
+import schedule
+import time
+from plyer import notification
+
+
+def show_notification(message):
+ notification.notify(
+ title="Напоминание",
+ message=message,
+ timeout=10
+ )
+
+
+def add_reminder(text, reminder_time):
+ schedule.every().day.at(reminder_time).do(show_notification, message=text)
+ print(f"Напоминание добавлено на {reminder_time}: {text}")
+
+
+if __name__ == "__main__":
+ while True:
+ text = input("Введите текст напоминания: ")
+ reminder_time = input("Введите время (формат ЧЧ:ММ): ")
+ add_reminder(text, reminder_time)
+ print("Ожидание напоминания...")
+
+ while True:
+ schedule.run_pending()
+ time.sleep(1)
+``` \ No newline at end of file
diff --git a/PYTHON/Протоколы.md b/PYTHON/Протоколы.md
new file mode 100644
index 0000000..cb5465a
--- /dev/null
+++ b/PYTHON/Протоколы.md
@@ -0,0 +1,13 @@
+![[telegram-cloud-photo-size-4-5850234924966199391-w.jpg]]
+
+_Протоколы — это соглашения, которые определяют интерфейс класса и поведение его объектов._
+
+Протоколы задаются с помощью специальных методов, таких как str, len и другие.
+
+Они позволяют классам работать с различными функциями и операторами языка.
+
+Реализуя протоколы можно интегрировать классы в языке и делать их поведение естественным и интуитивным.
+
+В Python есть протоколы для чисел, итераторов, контекстных менеджеров, атрибутов и других областей.
+
+Встроенные и сторонние библиотеки полагаются на стандартные протоколы. \ No newline at end of file
diff --git a/PYTHON/Своя библиотека для Python.md b/PYTHON/Своя библиотека для Python.md
new file mode 100644
index 0000000..d1c365a
--- /dev/null
+++ b/PYTHON/Своя библиотека для Python.md
@@ -0,0 +1,161 @@
+Часто в проектах на Python мы используем внешние библиотеки — уже готовые скрипты с нужными нам функциями, которые упрощают разработку. Теперь настало время сделать свою первую библиотеку на Python с нуля.
+
+Есть несколько способов собрать свою библиотеку — и все они рабочие. Мы выбрали способ, который рекомендует Гвидо ван Россум — автор языка Python, но вы можете выбрать любой другой подход.
+
+## Подготовка
+
+Чтобы сборка библиотеки прошла без проблем, обновим менеджер пакетов pip. Для этого в командной строке выполним команду:
+
+```markup
+pip install --upgrade pip
+```
+
+Теперь установим пакет twine — он нам поможет опубликовать свою библиотеку в сети, чтобы её мог установить любой желающий:
+
+```markup
+pip install --upgrade twine
+```
+
+Затем поставим `hatchling` — пакет для сборки серверной части библиотеки:
+
+```markup
+pip install hatchling
+```
+
+И наконец, установим пакет `build`, чтобы создать загрузочные архивы с нашей библиотекой:
+
+```markup
+pip install --upgrade build
+```
+
+## Структура библиотеки
+
+Библиотека в Python — это не просто один скрипт, а целый проект, в котором есть много разных файлов, каждый из которых отвечает за что-то своё.
+
+Первое, что делаем, — создаём папку `src`, в которой будет лежать наша новая библиотека. После этого внутри этой папки создаём ещё одну — с именем создаваемой библиотеки. Как мы назовём эту вложенную папку, так и будет называться наша библиотека. Для проекта мы выбрали название `beginning_package`.
+
+Теперь внутри папки с названием создаём два файла:
+
+- __init__.py — файл, который покажет компьютеру, что beginning_package — это не просто папка, а целый модуль с определённой функциональностью;
+- main.py — тут будет код нашей библиотеки.
+
+Теперь делаем остальной обвес и внутри основной папки создаём такое:
+
+- папку tests — если к библиотеке делаются тесты, они отправляются сюда. У нас их пока не будет, но папка, даже пустая, всё равно нужна, таковы правила;
+- LICENSE — файл с лицензией. Расширения нет, только имя;
+- pyproject.toml — отвечает за конфигурацию и серверную часть библиотеки;
+- README.md — информация о библиотеке: что она делает, зачем нужна и какие у неё особенности.
+
+В итоге по структуре должно получиться что-то вроде этого:
+
+![Тут ещё есть папка venv для виртуального окружения, но нам она сегодня не понадобится](https://thecode.media/wp-content/uploads/2024/04/unnamed-3.png)
+
+Тут ещё есть папка venv для виртуального окружения, но нам она сегодня не понадобится
+
+## Оформляем лицензию и файл README.md
+
+Наша библиотека будет относиться к свободному ПО, поэтому в файл с лицензией мы вставляем стандартный текст MIT License. В первой строке указываем копирайт и название проекта:
+
+Текст лицензии
+
+![Оформляем лицензию и файл README.md](https://thecode.media/wp-content/uploads/2024/04/unnamed-1-1.png)
+
+Точно так же заполняем файл README.md. Обычно здесь показывают, какие функции доступны в пакете, причём здесь уже можно использовать маркдаун для оформления текста. У нас учебная библиотека, поэтому сделаем максимально простое описание:
+
+```markup
+This library is not that useful. Perhaps someday it will become great.
+```
+
+(Это не очень полезная библиотека. Возможно, когда-нибудь она станет лучше)
+
+![Оформляем лицензию и файл README.md](https://thecode.media/wp-content/uploads/2024/04/unnamed-2-1.png)
+
+## Заполняем pyproject.toml
+
+Рyproject.toml — это файл, который отвечает за служебную часть библиотеки: как она называется, какая версия, какая версия Python нужна, по какой лицензии распространяется и так далее. Когда мы хотим установить новую библиотеку, система сначала смотрит в этот файл и проверяет, соответствует ли ваша версия Python той, что нужна для работы.
+
+Файл состоит из блоков, каждый из которых отвечает за что-то своё. Начнём с блока `build-system` — он отвечает за параметры сборки загрузочного архива. В нашем проекте за серверную часть отвечает `Hatchling`, поэтому указываем его:
+
+```python
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+```
+
+Python
+
+Copy
+
+Дальше идут основные параметры проекта: название, выпуск, авторы, описание, имя файла readme, требования к версии Python, язык программирования, тип лицензии и совместимость с ОС:
+
+```python
+[project]
+name = ""
+version = "0.0.1"
+authors = [
+ { name="", email="" },
+]
+description = ""
+readme = "README.md"
+requires-python = ">=3.10.0"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+]
+```
+
+Python
+
+Copy
+
+В конце можно добавить разные ссылки, например, на репозиторий в GitHub, домашнюю страницу проекта или на дополнительные материалы:
+
+```python
+[project.urls]
+Homepage = ""
+Issues = ""
+```
+
+Python
+
+Copy
+
+## Пишем библиотеку
+
+В папке с библиотекой два файла — `__init.py__` и `main.py`. Файл `__init__.py` можно оставить пустым — он нужен для того, чтобы сборщик понял, что это именно цельный проект и его нужно собрать из других файлов в той же папке.
+
+В `main.py` пишем сам код библиотеки. Так как это самая первая и учебная наша библиотека, сделаем там только одну функцию с приветствием:
+
+```python
+def greeting():
+ return "Hello, I'm your first library!"
+```
+
+Python
+
+Copy
+
+## Создаём загрузочные архивы
+
+Цель этого этапа — собрать и подготовить библиотеку к загрузке в менеджер пакетов `pip`, откуда каждый сможет её установить. Для этого открываем командную строку, переходим в папку с проектом и собираем архив:
+
+```markup
+py -m build
+```
+
+![Пишем и публикуем свою первую библиотеку для Python](https://thecode.media/wp-content/uploads/2024/04/unnamed-3-1.png)
+
+Если в проводнике мы посмотрим на структуру папок, то увидим, что там появилась новая папка — `dist`, в которой и хранятся наши подготовленные для загрузки архивы:
+
+![Пишем и публикуем свою первую библиотеку для Python](https://thecode.media/wp-content/uploads/2024/04/unnamed-4-1.png)
+
+Заглянем внутрь:
+
+![Пишем и публикуем свою первую библиотеку для Python](https://thecode.media/wp-content/uploads/2024/04/unnamed-5-1.png)
+
+## Создаём токен для подписи библиотеки
+
+PyPi — это хранилище открытого программного обеспечения для Python. Чтобы опубликовать там библиотеку, нужно иметь аккаунт и получить уникальный API-токен.
+
+Для этого заходим на официальный сайт [pypi.org](https://pypi.org/), заводим новый аккаунт, привязываем Гугл-аутентификатор для безопасности и переходим [в настройки аккаунта](https://pypi.org/manage/account/). Там прокручиваем страницу вниз до блока API tokens: \ No newline at end of file
diff --git a/PYTHON/Строим линейные графики с помощью Python.md b/PYTHON/Строим линейные графики с помощью Python.md
new file mode 100644
index 0000000..e8e887c
--- /dev/null
+++ b/PYTHON/Строим линейные графики с помощью Python.md
@@ -0,0 +1,6 @@
+Визуализация данных в виде линейного графика — максимально простая задача. Достаточно передать объект в качестве аргумента функции plot() для получения графика с несколькими линиями.
+
+![[Pasted image 20250501031545.png]]
+
+![[Pasted image 20250501031558.png]]
+
diff --git a/PYTHON/Тест кода в терминале.md b/PYTHON/Тест кода в терминале.md
new file mode 100644
index 0000000..f541790
--- /dev/null
+++ b/PYTHON/Тест кода в терминале.md
@@ -0,0 +1,27 @@
+⚙️ **Тестируй код прямо в терминале с** python -i
+
+🟢 **Запуск файла Python с режимом интерактивного тестирования:**
+
+```sh
+python -i script.py
+```
+
+После выполнения script.py ты можешь вручную проверять переменные и вызывать функции прямо в консоли!
+
+⚙️ **Пример использования:**
+
+x = 10
+y = x * 2
+
+**Запускаем:**
+
+```sh
+python -i script.py
+```
+
+➡️ **Теперь в консоли доступна переменная** y**, можно тестировать:**
+
+```sh
+print(y)  # 20
+```
+🗣 **Отличный способ отладки кода на лету!**
diff --git a/PYTHON/Улучшаем код на Python.md b/PYTHON/Улучшаем код на Python.md
new file mode 100644
index 0000000..0336296
--- /dev/null
+++ b/PYTHON/Улучшаем код на Python.md
@@ -0,0 +1,650 @@
+![[telegram-cloud-photo-size-2-5382031937209757544-y.jpg]]
+
+![[telegram-cloud-photo-size-2-5382031937209757546-y.jpg]]
+
+![[telegram-cloud-photo-size-2-5399847483028401010-y.jpg]]
+
+
+Уже почти никто не называет свои переменные непонятными буквами и имена функциям стараются давать начинающиеся с глагола, однако из раза в раз замечаю общие для многих ~~ошибки~~ зоны роста.
+
+=======================================
+Например, до сих пор встречается такой зверь:
+
+```python
+admins = []
+for user in get_users(): 
+ if user.is_admin:     
+ admins.append(user)
+```
+
+### 1. Использовать comprehension
+
+Код выше можно написать короче, используя генератор списка.
+
+```python
+admins = [user for user in get_users() if user.is_admin]
+```
+
+Более того, такое решение будет работать немного быстрее, так как comprehension-ы - оптимизированы для создания объектов. Не забываем также, что подобным образом можно создавать словари и множества. 
+
+Эта же конструкция признается сообществом как более _pythonic way_ для фильтрации
+
+```python
+# Вместо
+active_users = list(filter(lambda user: user.is_active, get_users()))
+
+# Лучше
+active_users = [user for user in get_users() if user.is_active]
+```
+
+### 2. Вложенные циклы for в comprehension
+
+Предположим, у нас есть задача сформировать список комбинаций элементов двух коллекций.
+
+```python
+numbers = ["1", "2", "3"]letters = ["A", "B", "C"]
+# Хотим ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
+```
+
+Даже знакомые с генераторами списков могу не знать о небольшом сахаре и написать вложенный цикл.
+
+```python
+spots = []for letter in letters:   
+ for number in numbers:       
+ spots.append(letter + number)
+
+# Хотя и можно
+такspots = [letter + number for letter in letters for number in numbers]
+```
+
+### 3. Логические операторы вместо тернарного
+
+Логические операторы часто можно использовать для задания значения переменной. Многие знакомы с тернарным оператором и избегают лишних if-else, однако и от первого можно уйти в некоторых случаях.
+
+```python
+# Есть переменная, которая может быть не заполнена
+user: User | None
+
+# Задаем админа тернарником
+admin = user if user else get_user()
+
+# Но можно и короче
+admin = user or get_user()
+
+# Цепочку можно продолжать. Вернется первое приводимое к True значение
+admin = get_admin() or user or get_user()
+```
+
+### 4. Использование оптимизации проверки условий
+
+Если python явно узнаёт значение условия, он не производит дальнейших проверок.
+
+Пример. Мы хотим убедиться, что последний из аппрувнувших людей обладает админскими правами. Но список аппрувнувших может быть и пустым.
+
+Чтобы не получить `"IndexError: list index out of range"`,
+часто вижу, что начинающие разработчики сначала проверяют список на пустоту
+
+```python
+approvers: list[User] = []if approvers:
+ if approvers[-1].is_admin:
+ return Truereturn False
+```
+
+Однако мы можем и совместить условия, упростив наш код:
+
+```python
+approvers = []
+if approvers and approvers[-1].is_admin:
+ return Truereturn False
+```
+
+Здесь, если `approvers` пустой, то общее условие уже никак не может быть `True`. Поэтому дальнейшие проверки не производятся, и за границы списка мы не выходим.
+
+Работает также и в обратную сторону для `or`
+
+```python
+approvers = []
+# Если первое условие уже `True`, то часть с `or` не имеет значения
+approved_by_admin = True
+if approved_by_admin == True or approvers[-1].is_admin:
+ return Truereturn False
+```
+
+### 5. Использование магии bool
+
+Как ни странно, в python `True` и `1` равны. Как и `False` c `0`
+
+```python
+>>> True == 1
+>>> True
+>>> False == 0
+>>> True
+```
+
+Помимо багов, это может и здорово сыграть нам на руку. Снова пример.
+
+Представим, что мы решили бросить программирование и ударились ростовщичество. Математическое прошлое подсказывает, что нужна какая-то система принятия решения о выдаче кредита, и мы выбираем самую передовую из известных нам. 
+А именно: мы собираемся анализировать три показателя у заемщика.
+
+- наличие дома
+- наличие машины
+- наличие работы
+
+Если какие-либо два из них положительны, то клиент, скорее всего, кредит отдаст, поэтому мы дадим ему деньги. Приступаем!
+
+```python
+has_a_house:
+ boolhas_as_car:
+ boolhas_a_job:
+ boolif has_a_house and has_a_car:
+ return Trueif has_a_house and has_a_job:
+ return Trueif has_a_car and has_a_job:
+ return True
+```
+
+Вроде работает, но смущает нездоровое количество if-ов... А если факторов будет не три, а 100, и для положительного ответа нужно будет набрать минимум 60 очков? Переписываем.
+
+```python
+factors = [has_a_house, has_as_car, has_a_job]
+total = 0
+for factor in factors:
+ if factor:
+ total += 1return total >= 2
+```
+
+Уже лучше, но если мы воспользуемся тем, что булево `True` равняется единице, то получим изящный однострочник
+
+```python
+return sum(factors) >= 2
+# здесь sum сложит все единицы. По сути, посчитает количество True.
+```
+
+### 6. Использование any и all
+
+Если бы в предыдущем примере мы захотели проверить условия на
+- хотя бы одно
+- все
+
+Мы бы просто могли воспользоваться конструкциями `any` и `all`соответственно
+
+```python
+any([True, False, False])
+# Trueany([False, False, False])
+# Falseall([True, True, True])
+# Trueall([True, True, False])
+# False
+```
+
+### 7. Сниппет для проверки пути в JSON-е
+
+Когда мы работаем с JSON-ами внешних сервисов, элементы в них могут быть опциональны. Причем бывает так, что дерево пути может оборваться в любой момент. 
+
+В данном примере хотим проверить наличие прав у пользователя на создание других пользователей. Для таких случаев сталкивался с таким вот кодом:
+
+```python
+user_dict = {
+ "user": {
+ "username": "PetyaNagibator228",
+ "permissions": {
+ "create_troubles": True,
+ "create_users": True
+ }
+ }
+ }
+if user := user_dict.get("user"):
+ if permissions := user.get("permissions"):
+ if permissions.get("create_users"):
+ return Truereturn False
+```
+
+Громоздко. Еще моржовые операторы эти. Было бы здорово обращаться к элементу прям по пути
+
+`path = "user.permissions.create_users"`
+
+Такое можно реализовать при помощи однострочника
+
+```python
+path = "user.permissions.create_users"
+reduce(dict.get, path.split('.'), user_dict)
+# True
+```
+
+Однако у него есть несколько недостатков. 
+
+- reduce у новичков вызывает недопонимания
+
+- решение не гибкое, если мы хотим иметь больше возможностей по обработке элементов
+
+
+Возможно, более адаптируемым под Ваши нужды окажется прямолинейное решение:
+
+```python
+def find(path: str, dict_: dict):
+ keys = path.split(".")
+ data = dict_
+ for key in keys:
+ try:
+ data = data.get(key)
+ except AttributeError:
+ return None
+ return datafind("user.permissions.create_users", user_dict)
+
+# Truefind("user.permissions.no_key.create_users", user_dict)# None
+```
+
+### 8. Генераторы для экономии памяти и кода
+
+Уверен, что в любом уважающем себя курсе рассказывают про генераторы и про то, зачем они нужны. Однако это не мешает многим начинающим ребятам обходить пагинацию следующим образом:
+
+```python
+j = {
+ "pagination": {
+ "page": 0, # текущая страница
+ "limit": 3 # кол-во элементов на странице
+ },
+ "user_ids": [1, 2, 3]
+}
+
+users = []
+limit = 3
+page = 0
+while True:
+ rsp = client.get_all_users(page=page, limit=limit)
+ if not rsp.get("user_ids"):
+ break
+ users.extend(rsp["user_ids"])
+ page += 1return users
+```
+
+Здесь создается список пользователей и наполняется до тех пор, пока внешний сервис отдает данные. Как только пагинация будет исчерпана, возвращается созданный список. 
+
+Используя подобную конструкцию, мы должны понимать, что весь итоговый список пользователей будет храниться в памяти. Что как минимум не очень эффективно. К тому же, существуют расходы на постоянное расширение данного списка на каждой итерации.
+
+Здесь как нельзя лучше находят свое применение те самые генераторы.
+
+```
+limit = 3page = 0while True: rsp = client.get_all_users(page=page, limit=limit) if not rsp.get("user_ids"): break yield rsp["user_ids"] page += 1
+```
+
+Так мы возвращаем пользователей пачками, сэкономив ресурсы машины.
+
+### 9. Ограничение пагинации
+
+Существует такая вещь, как ограничение глубины рекурсии. Она нужна для избегания бесконечной вложенности. К сожалению, работая с внешними API, я не один раз сталкивался с багами, приводящими к бесконечным обходам пагинации. В примере выше цикл так и будет крутиться, если по какой-то причине не выполнится условие `break`. Например, если API будет всегда возвращать данные, не зависимо от передаваемого `page`.
+
+Для таких случаев не лишним было бы иметь некий предохранитель, которым часто пренебрегают.
+
+```
+max_pages = 5for i in range(max_pages + 1): rsp = client.get_all_users(page=page, limit=limit) if not rsp.get("user_ids"): break if i >= max_pages: raise RuntimeError("Too many pagination elements") yield rsp["user_ids"]
+```
+
+### 10. А так же...
+
+Последним пунктом хотел бы привести похожую [статью](https://habr.com/ru/articles/466441/), обозревающую частые ошибочные решения разработчиков.
+
+### Итог
+
+Конечно же, данный список не является исчерпывающим. Он также ни в коем разе не является никаким «топом самых страшных вещей в мире программирования» и весьма субъективен. Даже наоборот, я старался собрать больше «мелочи», незаслуженно обделяемые вниманием начинающих разработчиков. 
+
+Благодарю прочитавших статью и желаю всем никогда не останавливаться на пути развития!
+
+==========================
+
+# Глючный код на Python: 10 самых распространенных ошибок, которые допускают разработчики
+
+6 сен 2019 в 15:49
+
+### О Python
+
+
+Python — это интерпретируемый, объектно-ориентированный язык программирования высокого уровня с динамической семантикой. Встроенные структуры данных высокого уровня в сочетании с динамической типизацией и динамическим связыванием делают его очень привлекательным для БРПС (быстрой разработки прикладных средств), а также для использования в качестве скриптового и связующего языка для подключения существующих компонентов или сервисов. Python поддерживает модули и пакеты, тем самым поощряя модульность программы и повторное использование кода.
+
+
+### О данной статье
+
+
+Простота и легкость в освоении данного языка может ввести разработчиков в заблуждение (особенно тех, кто еще только начинает изучать Python), так что можно упустить из виду некоторые важные тонкости и недооценить силу разнообразия возможных решений с помощью Python.
+
+Имея это в виду, в этой статье представлен «топ-10» тонких, трудных для обнаружения ошибок, которые могут допустить даже продвинутые разработчики Python.
+
+
+### Ошибка № 1: неправильное использование выражений в качестве значений по умолчанию для аргументов функций
+
+
+Python позволяет указывать, что у функции могут быть необязательные аргументы, путем задания для них значения по умолчанию. Это, конечно, очень удобная особенность языка, но может привести к неприятным последствиям, если тип такого значения будет изменяемым. Например, рассмотрим следующее определение функции:
+
+
+```
+>>> def foo(bar=[]): # bar - это необязательный аргумент # и по умолчанию равен пустому списку.... bar.append("baz") # эта строка может стать проблемой...... return bar
+```
+
+
+Распространенная ошибка в данном случае — это думать, что значение необязательного аргумента будет устанавливаться в значение по умолчанию каждый раз, как функция будет вызываться без значения для этого аргумента. В приведенном выше коде, например, можно предположить, что повторно вызывая функцию foo() (то есть без указания значения для агрумента bar), она всегда будет возвращать «baz», поскольку предполагается, что каждый раз, когда вызывается foo () (без указания аргумента bar), bar устанавливается в [ ] (т. е. новый пустой список).
+
+Но давайте посмотрим что же будет происходить на самом деле:
+
+
+```
+>>> foo()["baz"]>>> foo()["baz", "baz"]>>> foo()["baz", "baz", "baz"]
+```
+
+
+А? Почему функция продолжает добавлять значение по умолчанию «baz» к существующему списку каждый раз, когда вызывается foo(), вместо того, чтобы каждый раз создавать новый список?
+
+Ответом на данный вопрос будет более глубокое понимание того, что творится у Python «под капотом». А именно: значение по умолчанию для функции инициализируется только один раз, во время определения функции. Таким образом, аргумент bar инициализируется по умолчанию (т. е. пустым списком) только тогда, когда foo() определен впервые, но последующие вызовы foo() (т. е. без указания аргумента bar) продолжат использовать тот же список, который был создан для аргумента bar в момент первого определения функции.
+
+Для справки, распространенным «обходным путем» для этой ошибки является следующее определение:
+
+
+```
+>>> def foo(bar=None):... if bar is None: # or if not bar:... bar = []... bar.append("baz")... return bar...>>> foo()["baz"]>>> foo()["baz"]>>> foo()["baz"]
+```
+
+
+
+### Ошибка № 2: неправильное использование переменных класса
+
+
+Рассмотрим следующий пример:
+
+
+```
+>>> class A(object):... x = 1...>>> class B(A):... pass...>>> class C(A):... pass...>>> print A.x, B.x, C.x1 1 1
+```
+
+
+Вроде все в порядке.
+
+
+```
+>>> B.x = 2>>> print A.x, B.x, C.x1 2 1
+```
+
+
+Ага, все как и ожидалось.
+
+
+```
+>>> A.x = 3>>> print A.x, B.x, C.x3 2 3
+```
+
+
+Что за черт?! Мы же только изменили A.x. Почему же C.x тоже изменилось?
+
+В Python переменные класса обрабатываются как словари и следуют тому, что часто называют Порядком разрешения методов (MRO). Таким образом, в приведенном выше коде, поскольку атрибут x не найден в классе C, он будет найден в его базовых классах (только A в приведенном выше примере, хотя Python поддерживает множественное наследование). Другими словами, C не имеет своего собственного свойства x, независимого от A. Таким образом, ссылки на C.x фактически являются ссылками на A.x. Это будет вызывать проблемы, если не обрабатывать такие случаи должным образом. Так что при изучении Python обратите особое внимание на аттрибуты класса и работу с ними.
+
+
+### Ошибка № 3: неправильное указание параметров для блока исключения
+
+
+Предположим, что у вас есть следующий кусок кода:
+
+
+```
+>>> try:... l = ["a", "b"]... int(l[2])... except ValueError, IndexError: # To catch both exceptions, right?... pass...Traceback (most recent call last): File "<stdin>", line 3, in <module>IndexError: list index out of range
+```
+
+
+Проблема здесь заключается в том, что выражение except не принимает список исключений, указанных таким образом. Скорее, в Python 2.x выражение «except Exception, e» используется для привязки исключения к необязательному второму заданному второму параметру (в данном случае e), чтобы сделать его доступным для дальнейшей проверки. В результате в приведенном выше коде исключение IndexError не перехватывается выражением except; скорее, вместо этого исключение заканчивается привязкой к параметру с именем IndexError.
+
+Правильный способ перехвата нескольких исключений с помощью выражения except — указать первый параметр в виде кортежа, содержащего все исключения, которые нужно перехватить. Кроме того, для максимальной совместимости используйте ключевое слово as, так как этот синтаксис поддерживается как в Python 2, так и в Python 3:
+
+
+```
+>>> try:... l = ["a", "b"]... int(l[2])... except (ValueError, IndexError) as e: ... pass...>>>
+```
+
+
+
+### Ошибка № 4: непонимание правил области видимости Python
+
+
+Область видимости в Python основана на так называемом правиле LEGB, которое является аббревиатурой Local (имена, назначенные любым способом внутри функции (def или lambda), и не объявленные глобальными в этой функции), Enclosing (имя в локальной области действия любых статически включающих функций (def или lambda), от внутреннего к внешнему), Global (имена, назначенные на верхнем уровне файла модуля, или путем выполнения global инструкции в def внутри файла), Built-in (имена, предварительно назначенные в модуле встроенных имен: open, range, SyntaxError ,...). Кажется достаточно просто, верно? Ну, на самом деле, есть некоторые тонкости в том, как это работает в Python, что подводит нас к общей более сложной проблеме программирования на Python ниже. Рассмотрим следующей пример: 
+
+
+```
+>>> x = 10>>> def foo():... x += 1... print x...>>> foo()Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in fooUnboundLocalError: local variable 'x' referenced before assignment
+```
+
+
+В чем проблема?
+
+Вышеуказанная ошибка возникает потому, что, когда вы присваиваете переменную в области видимости, Python автоматически считает ее локальной для этой области и скрывает любую переменную с аналогичным именем в любой вышестоящей области.
+
+Таким образом, многие удивляются, когда получают UnboundLocalError в ранее работающем коде, когда он модифицируется путем добавления оператора присваивания где-нибудь в теле функции.
+
+Эта особенность особенно сбивает разработчиков с толку при использовании списков. Рассмотрим следующий пример:
+
+
+```
+>>> lst = [1, 2, 3]>>> def foo1():... lst.append(5) # Это работает нормально......>>> foo1()>>> lst[1, 2, 3, 5]>>> lst = [1, 2, 3]>>> def foo2():... lst += [5] # ... а вот это падает!...>>> foo2()Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in fooUnboundLocalError: local variable 'lst' referenced before assignment
+```
+
+
+А? Почему foo2 падает, в то время как foo1 работает нормально?
+
+Ответ такой же, как в предыдущем примере, но, по распространенному мнению, здесь ситуация более тонкая. foo1 не применяет оператор присваивания к lst, тогда как foo2 — да. Помня, что lst + = [5] на самом деле является просто сокращением для lst = lst + [5], мы видим, что мы пытаемся присвоить значение lst (поэтому Python предполагает, что он находится в локальной области видимости). Однако значение, которое мы хотим присвоить lst, основано на самом lst (опять же, теперь предполагается, что он находится в локальной области видимости), который еще не был определен. И мы получаем ошибку.
+
+
+### Ошибка № 5: изменение списка во время итерации по нему
+
+
+Проблема в следующем куске кода должна быть достаточно очевидной:
+
+
+```
+>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> for i in range(len(numbers)):... if odd(numbers[i]):... del numbers[i] # BAD: Deleting item from a list while iterating over it...Traceback (most recent call last): File "<stdin>", line 2, in <module>IndexError: list index out of range
+```
+
+
+Удаление элемента из списка или массива во время итерации по нему — это проблема Python, которая хорошо известна любому опытному разработчику программного обеспечения. Но, хотя приведенный выше пример может быть достаточно очевидным, даже опытные разработчики могут встать на эти грабли в гораздо более сложном коде.
+
+К счастью, Python включает в себя ряд элегантных парадигм программирования, которые при правильном использовании могут привести к значительному упрощению и оптимизации кода. Дополнительным приятным следствием этого является то, что в более простом коде вероятность попасться на ошибку случайного удаления элемента списка во время итерации по нему значительно меньше. Одна из таких парадигм — генераторы списков. Кроме того, понимание работы генераторов списков особенно полезны для избежания этой конкретной проблемы, как показано в этой альтернативной реализацией приведенного выше кода, которая прекрасно работает:
+
+
+```
+>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all>>> numbers[0, 2, 4, 6, 8]
+```
+
+
+
+### Ошибка № 6: непонимание того, как Python связывает переменные в замыканиях
+
+
+Рассмотрим следующий пример:
+
+
+```
+>>> def create_multipliers():... return [lambda x : i * x for i in range(5)]>>> for multiplier in create_multipliers():... print multiplier(2)...
+```
+
+
+Вы можете ожидать следующий вывод:
+
+
+```
+02468
+```
+
+
+Но на самом деле вы получите вот что:
+
+
+```
+88888
+```
+
+
+Сюрприз!
+
+Это происходит из-за поздней привязки в Python, которое заключается в том, что значения переменных, используемых в замыканиях, ищутся во время вызова внутренней функции. Таким образом, в приведенном выше коде всякий раз, когда вызывается какая-либо из возвращаемых функций, значение i ищется в окружающей области видимости во время ее вызова (а к тому времени цикл уже завершился, поэтому i уже был присвоен конечный результат — значение 4).
+
+Решение этой распространенной проблемы с Python будет таким:
+
+
+```
+>>> def create_multipliers():... return [lambda x, i=i : i * x for i in range(5)]...>>> for multiplier in create_multipliers():... print multiplier(2)...02468
+```
+
+
+Вуаля! Мы используем здесь аргументы по умолчанию для генерации анонимных функций для достижения желаемого поведения. Некоторые назвали бы это решение элегантным. Некоторые — 
+тонким. Некоторые ненавидят подобные штуки. Но если вы разработчик Python, в любом случае, это важно понимать.
+
+
+### Ошибка № 7: создание циклических зависимостей модуля
+
+
+Допустим, у вас есть два файла, a.py и b.py, каждый из которых импортирует другой, следующим образом:
+
+В a.py:
+
+
+```
+import bdef f(): return b.x print f()
+```
+
+
+В b.py:
+
+
+```
+import ax = 1def g(): print a.f()
+```
+
+
+Сначала попробуем импортировать a.py:
+
+
+```
+>>> import a1
+```
+
+
+Сработало просто отлично. Возможно, это вас удивляет. В конце концов, модули циклически импортируют друг друга и это, вероятно, должено быть проблемой, не так ли?
+
+Ответ заключается в том, что простое наличие циклического импорта модулей само по себе не является проблемой в Python. Если модуль уже был импортирован, Python достаточно умен, чтобы не пытаться повторно импортировать его. Однако, в зависимости от точки, в которой каждый модуль пытается получить доступ к функциям или переменным, определенным в другом, вы действительно можете столкнуться с проблемами.
+
+Итак, возвращаясь к нашему примеру, когда мы импортировали a.py, у него не было проблем с импортом b.py, поскольку b.py не требует, чтобы что-либо из a.py было определено во время его импорта. Единственная ссылка в b.py на a — это вызов a.f(). Но этот вызов в g() и ничего в a.py или b.py не вызывает g(). Так что все работает прекрасно.
+
+Но что произойдет, если мы попытаемся импортировать b.py (без предварительного импорта a.py, то есть):
+
+
+```
+>>> import bTraceback (most recent call last): File "<stdin>", line 1, in <module> File "b.py", line 1, in <module> import a File "a.py", line 6, in <module> print f() File "a.py", line 4, in f return b.xAttributeError: 'module' object has no attribute 'x'
+```
+
+
+Ой-ой. Это не хорошо! Проблема здесь в том, что в процессе импорта b.py он пытается импортировать a.py, который, в свою очередь, вызывает f(), который пытается получить доступ к b.x. Но b.x еще не было определено. Отсюда исключение AttributeError.
+
+По крайней мере, одно из решений этой проблемы довольно тривиально. Просто измените b.py, чтобы импортировать a.py в g():
+
+
+```
+x = 1def g(): import a # This will be evaluated only when g() is called print a.f()
+```
+
+
+Теперь, когда мы его импортируем, все нормально:
+
+
+```
+>>> import b>>> b.g()1 # Printed a first time since module 'a' calls 'print f()' at the end1 # Printed a second time, this one is our call to 'g'
+```
+
+
+
+### Ошибка № 8: пересечение имен с именами модулями стандартной библиотеки Python
+
+
+Одна из прелестей Python — это множество модулей, которые поставляются «из коробки». Но в результате, если вы сознательно не будете за этим следить, можно столкнуться с тем, что имя вашего модуля может быть с тем же именем, что и модуль в стандартной библиотеке, поставляемой с Python (например, в вашем коде может быть модуль с именем email.py, который будет конфликтовать со модулем стандартной библиотеки с таким же именем).
+
+Это может привести к серьезным проблемам. Например, если какой-нибудь из модулей будет пытаться импортировать версию модуля из стандартной библиотеки Python, а у вас в проекте будет модуль с таким же именем, который и будет по ошибке импортирован вместо модуля из стандартной библиотеки. 
+
+Поэтому следует проявлять осторожность, чтобы не использовать те же имена, что и в модулях стандартной библиотеки Python. Гораздо проще изменить название модуля в своем проекте, нежели подать запрос на изменение имени модуля в стандартной библиотеке и получить на него одобрение.
+
+
+### Ошибка № 9: неспособность учесть различия Python 2 и Python 3
+
+
+Рассмотрим следующий файл foo.py:
+
+
+```
+import sysdef bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2)def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e)bad()
+```
+
+
+На Python 2 он отработает нормально:
+
+
+```
+$ python foo.py 1key error1$ python foo.py 2value error2
+```
+
+
+Но теперь давайте посмотрим как он будет работать в Python 3:
+
+
+```
+$ python3 foo.py 1key errorTraceback (most recent call last): File "foo.py", line 19, in <module> bad() File "foo.py", line 17, in bad print(e)UnboundLocalError: local variable 'e' referenced before assignment
+```
+
+
+Что здесь только что произошло? «Проблема» в том, что в Python 3 объект в блоке исключения недоступен за его пределами. (Причина этого заключается в том, что в противном случае объекты в этом блоке будут сохраняться в памяти до тех пор, пока сборщик мусора не запустится и не удалит ссылки на них оттуда).
+
+Один из способов избежать этой проблемы — сохранить ссылку на объект блока исключения за пределами этого блока, чтобы он оставался доступным. Вот версия предыдущего примера, которая использует эту технику, тем самым получая код, который подходит как для Python 2, так и для Python 3:
+
+
+```
+import sysdef bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2)def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception)good()
+```
+
+
+Запустим его в Python 3:
+
+
+```
+$ python3 foo.py 1key error1$ python3 foo.py 2value error2
+```
+
+
+Ураааа!
+
+
+### Ошибка № 10: неправильное использование метода __del__
+
+
+Допустим, у вас есть вот такой файл mod.py:
+
+
+```
+import fooclass Bar(object): ... def __del__(self): foo.cleanup(self.myhandle)
+```
+
+
+И вы пытаетесь сделать вот такое из другого another_mod.py:
+
+
+```
+import modmybar = mod.Bar()
+```
+
+
+И получите ужасный AttributeError.
+
+Почему? Потому что, как сообщается [здесь](https://mail.python.org/pipermail/python-bugs-list/2009-January/069209.html), когда интерпретатор отключается, глобальные переменные модуля все имеют значение None. В результате в приведенном выше примере, в момент вызова __del__, имя foo уже было установлено в None.
+
+Решением этой «задачи со звездочкой» будет использование atexit.register(). Таким образом, когда ваша программа завершает выполнение (то есть при нормальном выходе из нее), ваши handle'ы удаляются до того, как интерпретатор звершает работу.
+
+С учетом этого, исправление для приведенного выше кода mod.py может выглядеть примерно так:
+
+
+```
+import fooimport atexitdef cleanup(handle): foo.cleanup(handle)class Bar(object): def __init__(self): ... atexit.register(cleanup, self.myhandle)
+```
+
+
+Подобная реализация обеспечивает простой и надежный способ вызова любой необходимой очистки после обычного завершения программы. Очевидно, что решение о том, как поступить с объектом, который связан с имненем self.myhandle, остается за foo.cleanup, но, думаю, идею вы поняли.
+
+
+![[telegram-cloud-photo-size-2-5386535536837126461-y.jpg]] \ No newline at end of file
diff --git a/PYTHON/ФУНКЦИИ.md b/PYTHON/ФУНКЦИИ.md
new file mode 100644
index 0000000..73d132c
--- /dev/null
+++ b/PYTHON/ФУНКЦИИ.md
@@ -0,0 +1,192 @@
+```python
+def foo() -> int :
+ return x ** 2
+
+
+x = foo( 2 )
+print(x) # 5
+
+
+def foo( x : str, y : int ) -> str :
+ ...........
+
+
+name = input('Ведите имя:') # - всегда строка!
+print(dir(name)) # - вывод атрибутов
+
+# ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getstate__', '__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', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
+#
+```
+
+```python
+# собираем все параметры в кортеж:
+
+def sum_nums(*args):
+ print(args)
+ print(type(args))
+ print(args[0])
+ return sum(args)
+
+
+print(sum_nums(2, 3, 7))
+```
+
+```python
+# аргументы с ключевыми словами - порядок не важен:
+
+def get_post_info(name, post_qty):
+ info = f"{name} wrote {post_qty} posts"
+ return info
+
+
+info = get_post_info(name='Vlapa', post_qty=50)
+print(info)
+```
+
+```python
+# объединение аргументов в DICT (только именованные):
+
+def get_post_info(**person):
+ print(person)
+ print(type(person))
+ info = (
+ f"{person['name']} wrote "
+ f"{person['post_qty']} posts"
+ )
+ return info
+
+info = get_post_info(name='Vlapa', post_qty=50)
+print(info)
+```
+
+```python
+# значение по умолчанию:
+
+def mult_by(value, multiplier=1):
+ return value * multiplier
+
+print(mult_by(10, 2))
+print(mult_by(5))
+
+```
+
+```python
+#
+
+from datetime import date
+
+
+def get_weekday():
+ return date.today().strftime('%A')
+
+
+def create_new_post(post, weekday=get_weekday()):
+ post_copy = post.copy()
+ post_copy['created_on_weekday'] = weekday
+ return post_copy
+
+
+initial_post = {
+ 'id': 234,
+ 'author': 'Vlapa',
+}
+
+post_with_weekday = create_new_post(initial_post)
+```
+
+```python
+# callback function
+
+def print_number_info(num):
+ """
+ Print num information
+ Args:
+ num (int): Integer number
+ """
+ if (num % 2) == 0:
+ print("Число четное")
+else:
+ print("Число НЕ четное")
+
+
+def print_square_num(num):
+ print("Square of the num is", num * num)
+
+
+def process_number(num, callback_fn):
+ callback_fn(num)
+
+
+entered_num = int(input("Введите любое целое число: "))
+
+process_number(entered_num, print_number_info)
+process_number(entered_num, print_square_num)
+```
+
+```python
+print(dir()) # все переменные области
+```
+
+```python
+def greeting(greet):
+ return lambda name: f"{greet}, {name}!"
+
+ # def info(name):
+ # return f"{greet}, {name}!"
+ # return info
+
+morning_greeting = greeting("Good Morning")
+
+print(morning_greeting('Vlapa'))
+
+evening_greeting = greeting("Good Evening")
+
+print(evening_greeting('Vlapa'))
+```
+======================================================
+### Голобурдин:
+
+```python
+def chain_sum0(number):
+ result = number
+ def wrapper(number2=None):
+ nonlocal result
+ if number2 is None:
+ return result
+ result += number2
+ return wrapper
+ return wrapper
+
+
+def chain_sum00(number):
+ result = number
+ def wrapper(number2=None):
+ nonlocal result
+ try:
+ number2 = int(number2)
+ except TypeError:
+ return result
+ result += number2
+ return wrapper
+ return wrapper
+
+
+def chain_sum(number):
+ def wrapper(number2=None):
+ def inner():
+ wrapper.result += number2
+ return wrapper
+ logic = {
+ type(None): lambda: wrapper.result,
+ int: inner
+ }
+ return logic[type(number2)]()
+ wrapper.result = number
+ return wrapper
+
+
+print(chain_sum(5)())
+print(chain_sum(5)(2)())
+print(chain_sum(5)(100)(-10)())
+```
+