diff --git a/.github/workflows/isort.yml b/.github/workflows/isort.yml index 8a9879d0..e00614c6 100644 --- a/.github/workflows/isort.yml +++ b/.github/workflows/isort.yml @@ -2,10 +2,10 @@ name: SENATOROV on: push: branches: - - "**" - + - '**' + schedule: - - cron: "0 0 * * *" # Run every day + - cron: '0 0 * * *' # Run every day workflow_dispatch: create: delete: @@ -16,26 +16,26 @@ jobs: isort: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: isort/isort-action@v1 - with: + - uses: actions/checkout@v4 + - uses: isort/isort-action@v1 + with: python-version: ${{ matrix.python-version }} allow-prereleases: true - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install isort nbqa - - name: isort - run: | - nbqa isort --float-to-top $(git ls-files '*.ipynb') - isort --float-to-top $(git ls-files '*.py') - - name: Commit and push changes - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - run: | - git config --global user.name "GitHub Actions" - git config --global user.email "actions@github.com" - git remote set-url origin https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${{ github.repository }} - git pull origin ${{ github.ref_name }} - git add . - git diff --quiet || (git commit -m "Sort imports and apply isort fixes" && git push origin ${{ github.ref_name }}) + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install isort nbqa + - name: isort + run: | + nbqa isort --float-to-top $(git ls-files '*.ipynb') + isort --float-to-top --profile=black $(git ls-files '*.py') + - name: Commit and push changes + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git remote set-url origin https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${{ github.repository }} + git pull origin ${{ github.ref_name }} + git add . + git diff --quiet || (git commit -m "Sort imports and apply isort fixes" && git push origin ${{ github.ref_name }}) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000..b5f28b5a --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,21 @@ +name: pre-commit + +on: + pull_request: + branches: + - '*' + push: + branches: + - '*' + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.11' + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files diff --git a/.gitignore b/.gitignore index 0fa2f447..eff26a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Для задание из issue 7 Venv +/SENATOROV +NO_COMMIT.py + + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e4f007c7..9bd83d21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,26 @@ repos: - repo: https://github.com/mwouts/jupytext - rev: "v1.16.4b" + rev: 'v1.16.4b' hooks: - - id: jupytext - entry: jupytext - language: python - types: [jupyter] - args: - - --from=ipynb - - --to=py:light - - --set-formats=ipynb,py:light - - --quiet - - --sync - - --warn-only - exclude: '.*\.md$' + - id: jupytext + entry: jupytext + language: python + types: [jupyter] + args: + - --from=ipynb + - --to=py:light + - --set-formats=ipynb,py:light + - --quiet + - --sync + - --warn-only + exclude: '.*\.md$' - repo: https://github.com/PyCQA/docformatter - rev: "eb1df347edd128b30cd3368dddc3aa65edcfac38" + rev: 'eb1df347edd128b30cd3368dddc3aa65edcfac38' hooks: - id: docformatter args: [--in-place, --wrap-descriptions=90, --style=google] - repo: https://github.com/psf/black.git - rev: "24.10.0" + rev: '24.10.0' hooks: - id: black @@ -36,21 +36,21 @@ repos: types_or: [jupyter, markdown] additional_dependencies: [black] - repo: https://github.com/asottile/pyupgrade - rev: "v3.19.0" + rev: 'v3.19.0' hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/pycqa/isort - rev: "5.13.2" + rev: '5.13.2' hooks: - id: isort entry: isort args: - - --profile=black + - --profile=black - --float-to-top - repo: https://github.com/nbQA-dev/nbQA - rev: "1.9.0" + rev: '1.9.0' hooks: - id: nbqa entry: nbqa blacken-docs @@ -79,8 +79,8 @@ repos: types_or: [jupyter, markdown] additional_dependencies: [isort] args: - - --profile=black - - --float-to-top + - --profile=black + - --float-to-top - repo: https://github.com/codespell-project/codespell rev: v2.3.0 @@ -165,8 +165,6 @@ repos: - --warn-return-any - --disallow-any-explicit - - - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.13.0 hooks: @@ -195,7 +193,6 @@ repos: - --warn-return-any - --disallow-any-explicit - additional_dependencies: - mypy - pandas-stubs @@ -222,9 +219,8 @@ repos: - --const-naming-style=any - --disable=E0401,W0104,R0903,R1721,E1101,E0611,F0002,C0305,C0303,E2515 - - repo: https://github.com/pylint-dev/pylint - rev: "v3.3.1" + rev: 'v3.3.1' hooks: - id: pylint name: pylint @@ -233,10 +229,10 @@ repos: types: [python] args: [ - "--ignore=no_check*,__init__.py", - "--max-line-length=90", - "--const-naming-style=any", - "--disable=E0401,W0104,R0903,R1721,E1101,E0611,F0002,C0305,line-too-long,C0303,E2515" + '--ignore=no_check*,__init__.py', + '--max-line-length=90', + '--const-naming-style=any', + '--disable=E0401,W0104,R0903,R1721,E1101,E0611,F0002,C0305,line-too-long,C0303,E2515', ] additional_dependencies: [pylint] @@ -275,4 +271,3 @@ repos: - --ignore-regex=^\s*"image\/(jpeg|png|gif|bmp|tiff)":\s.* - --ignore-regex=[A-Za-z0-9+/]{100,} - --skip=*.js,*.html,*.css,*.svg",*.json,*.png,*.jpg,*.yml,*.yaml - diff --git a/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.ipynb b/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.ipynb new file mode 100644 index 00000000..c67ea806 --- /dev/null +++ b/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.ipynb @@ -0,0 +1,126 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "13e666a1", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Сложные вычислительные задачи.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "8011fe2e", + "metadata": {}, + "source": [ + "Функция f(n) называется полиномиально возрастающей, если она растет растёт пропорционально полиному (многочлену) от размера входных данных.\n", + "\n", + "**Полиномиально возрастающие функции** (примеры):\n", + "* f(n) = 3n + 2 → линейный рост O(n)\n", + "* f(n) = n^2 → квадратичный рост O(n^2)\n", + "* f(n) = 5n^3 + 10n → кубический рост O(n^3)\n", + "\n", + "**Не является полиномиальным ростом**:\n", + "Эти функции растут быстрее любого полинома.\n", + "\n", + "* Экспоненциальный рост: f(n) = 2^n, f(n) = e^n\n", + "* Факториальный рост: f(n) = n!\n", + "* Суперполиномиальный рост: f(n) = n * log n\n", + "\n", + "**Полиномиальный** означает, что «растёт как какая-то степень n»\n", + "\n", + "**Полиномиально возрастающий** — значит растущий как многочлен от n , например n, n^2, n^10, но не как 2^n или n! " + ] + }, + { + "cell_type": "markdown", + "id": "1f1c97e1", + "metadata": {}, + "source": [ + "**P-задача** – это класс задач, которые можно решить быстро (за полиномиальное время) на обычном компьютере (время работы алгоритма растёт как многочлен от размера входа: например, O(n), O(n^2), O(n^3) — это всё полиномиально)\n", + "\n", + "**NP-задача** — это класс задач, для которых решение можно проверить быстро (за полиномиальное время), если оно уже дано." + ] + }, + { + "cell_type": "markdown", + "id": "cd403d1f", + "metadata": {}, + "source": [ + "# NP-полнота" + ] + }, + { + "cell_type": "markdown", + "id": "156f10e6", + "metadata": {}, + "source": [ + "NР - недетерминированное полиномиальное время\n", + "\n", + "NР относится к группе вычислительных задач, которые решаются за время, полиномиально возрастающее по мере увеличения размера задачи. У этих задач есть отличительная черта: если решение предложено, то его точность может быть подтверждена или опровергнута за полиномиальное время.\n", + "\n", + "Задача считается NP-полной, если принадлежит классу NP и является такой же трудной, как и любая другая задача NP. Другими словами, NP-полные задачи — одни из самых сложных в информатике." + ] + }, + { + "cell_type": "markdown", + "id": "d177ea3f", + "metadata": {}, + "source": [ + "Редукция – преобразование известной задачи, которая, как доказано, является NР-полной, в другую. Успешно выполнив это преобразование, мы устанавливаем, что вторая задача также является NР-полной." + ] + }, + { + "cell_type": "markdown", + "id": "cff78d4a", + "metadata": {}, + "source": [ + "# NР-трудность" + ] + }, + { + "cell_type": "markdown", + "id": "c243a999", + "metadata": {}, + "source": [ + "В теории сложности вычислений задача называется NР-трудной, если ее решение за полиномиальное время означает, что любая задача из класса NР может быть решена за аналогичное время.\n", + "\n", + "NР-трудные задачи имеют характерную черту: они могут быть, а могут и не быть частью класса NР. То есть, несмотря на то что они трудноразрешимы, могут существовать решения, проверяемые за полиномиальное время. Отсутствие такого решения не исключает возможности его существования.\n", + "\n", + "Примеры:\n", + "* Задача о коммивояжере.\n", + "* Задача о наполнении рюкзака наиболее ценными предметами и пр.\n", + "\n", + "NP-трудные задачи обычно решаются с помощью **эвристических и аппроксимирующих алгоритмов**. То есть эти алгоритмы неидеальны, но **достаточно** эффективны для получения результата." + ] + }, + { + "cell_type": "markdown", + "id": "510b7528", + "metadata": {}, + "source": [ + "Изучение и решение NР-трудных и NP-пoлныx задач позволяют находить алгоритмы, которые обеспечивают равновесие между точностью и эффективностью использования времени и ресурсов." + ] + }, + { + "cell_type": "markdown", + "id": "441d2e68", + "metadata": {}, + "source": [ + "* **P** – задачу легко решить.\n", + "* **NP** – задачу сложно решить быстро, но если есть решение, его можно быстро проверить.\n", + "* **NP-полная (NP-complete)** – самые трудные из NP.\n", + "* **NP-трудные (NP-hard)** – еще труднее (или не из NP вообще). И проверить её правильность тоже невозможно, но если ты это решишь, то и все головоломки из NP тоже решишь." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.py b/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.py new file mode 100644 index 00000000..352ca4fb --- /dev/null +++ b/Python/algoritmy-i-struktury-dannykh-na-python/chapter_10.py @@ -0,0 +1,52 @@ +"""Сложные вычислительные задачи.""" + +# Функция f(n) называется полиномиально возрастающей, если она растет растёт пропорционально полиному (многочлену) от размера входных данных. +# +# **Полиномиально возрастающие функции** (примеры): +# * f(n) = 3n + 2 → линейный рост O(n) +# * f(n) = n^2 → квадратичный рост O(n^2) +# * f(n) = 5n^3 + 10n → кубический рост O(n^3) +# +# **Не является полиномиальным ростом**: +# Эти функции растут быстрее любого полинома. +# +# * Экспоненциальный рост: f(n) = 2^n, f(n) = e^n +# * Факториальный рост: f(n) = n! +# * Суперполиномиальный рост: f(n) = n * log n +# +# **Полиномиальный** означает, что «растёт как какая-то степень n» +# +# **Полиномиально возрастающий** — значит растущий как многочлен от n , например n, n^2, n^10, но не как 2^n или n! + +# **P-задача** – это класс задач, которые можно решить быстро (за полиномиальное время) на обычном компьютере (время работы алгоритма растёт как многочлен от размера входа: например, O(n), O(n^2), O(n^3) — это всё полиномиально) +# +# **NP-задача** — это класс задач, для которых решение можно проверить быстро (за полиномиальное время), если оно уже дано. + +# # NP-полнота + +# NР - недетерминированное полиномиальное время +# +# NР относится к группе вычислительных задач, которые решаются за время, полиномиально возрастающее по мере увеличения размера задачи. У этих задач есть отличительная черта: если решение предложено, то его точность может быть подтверждена или опровергнута за полиномиальное время. +# +# Задача считается NP-полной, если принадлежит классу NP и является такой же трудной, как и любая другая задача NP. Другими словами, NP-полные задачи — одни из самых сложных в информатике. + +# Редукция – преобразование известной задачи, которая, как доказано, является NР-полной, в другую. Успешно выполнив это преобразование, мы устанавливаем, что вторая задача также является NР-полной. + +# # NР-трудность + +# В теории сложности вычислений задача называется NР-трудной, если ее решение за полиномиальное время означает, что любая задача из класса NР может быть решена за аналогичное время. +# +# NР-трудные задачи имеют характерную черту: они могут быть, а могут и не быть частью класса NР. То есть, несмотря на то что они трудноразрешимы, могут существовать решения, проверяемые за полиномиальное время. Отсутствие такого решения не исключает возможности его существования. +# +# Примеры: +# * Задача о коммивояжере. +# * Задача о наполнении рюкзака наиболее ценными предметами и пр. +# +# NP-трудные задачи обычно решаются с помощью **эвристических и аппроксимирующих алгоритмов**. То есть эти алгоритмы неидеальны, но **достаточно** эффективны для получения результата. + +# Изучение и решение NР-трудных и NP-пoлныx задач позволяют находить алгоритмы, которые обеспечивают равновесие между точностью и эффективностью использования времени и ресурсов. + +# * **P** – задачу легко решить. +# * **NP** – задачу сложно решить быстро, но если есть решение, его можно быстро проверить. +# * **NP-полная (NP-complete)** – самые трудные из NP. +# * **NP-трудные (NP-hard)** – еще труднее (или не из NP вообще). И проверить её правильность тоже невозможно, но если ты это решишь, то и все головоломки из NP тоже решишь. diff --git a/Python/clean-code/chapter_13_algorithm_performance_and_complexity.ipynb b/Python/clean-code/chapter_13_algorithm_performance_and_complexity.ipynb new file mode 100644 index 00000000..2e635a3b --- /dev/null +++ b/Python/clean-code/chapter_13_algorithm_performance_and_complexity.ipynb @@ -0,0 +1,332 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8806f111", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Измерение быстродействия и анализ сложности алгоритмов.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "09894448", + "metadata": {}, + "source": [ + "# Модуль timeit" + ] + }, + { + "cell_type": "markdown", + "id": "7d16a3a3", + "metadata": {}, + "source": [ + "### Хорошее правило\n", + "Сначала добейтесь того, чтобы ваш код работал, а потом занимайтесь его ускорением.\n", + "Сначала работоспособность, потом эффективность!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "542d67e4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.05181330000050366" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import cProfile\n", + "import timeit\n", + "\n", + "# или так\n", + "# Для измерения времени выполнения кода можно воспользоваться\n", + "# модулем timeit\n", + "\n", + "timeit.timeit(\"a, b = 42, 101; a = a ^ b; b = a ^ b; a = a ^ b\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2aeac644", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.051111800014041364" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# или так\n", + "timeit.timeit(\n", + " \"\"\"a, b = 42, 101\n", + "a = a ^ b\n", + "b = a ^ b\n", + "a = a ^ b\"\"\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bc2eeb28", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.025852099992334843" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "timeit.timeit(\"a, b = 42, 101; temp = a; a = b; b = temp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e8fc6f83", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.023180100019089878" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# или так\n", + "timeit.timeit(\"a, b = 42, 101; a, b = b, a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2826b909", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n", + "hello\n", + "hello\n" + ] + }, + { + "data": { + "text/plain": [ + "0.00018620002083480358" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# По умолчанию код будет выполнен 1000000 раз и будет возвращено среднее время выполнения\n", + "# Но можно явно указать, сколько раз выполнить код\n", + "timeit.timeit('print(\"hello\")', number=3)" + ] + }, + { + "cell_type": "markdown", + "id": "82d92c29", + "metadata": {}, + "source": [ + "# Модуль cProfile" + ] + }, + { + "cell_type": "markdown", + "id": "2ba3cad7", + "metadata": {}, + "source": [ + "Хотя модуль timeit полезен для хронометража небольших фрагментов кода, модуль\n", + "cProfile более эффективен для анализа целых функций или программ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab9580a1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 113 function calls (111 primitive calls) in 0.020 seconds\n", + "\n", + " Ordered by: standard name\n", + "\n", + " ncalls tottime percall cumtime percall filename:lineno(function)\n", + " 1 0.020 0.020 0.020 0.020 1129202980.py:4(addUpNumbers)\n", + " 1 0.000 0.000 0.000 0.000 :121(__subclasscheck__)\n", + " 2 0.000 0.000 0.000 0.000 :1390(_handle_fromlist)\n", + " 1 0.000 0.000 0.020 0.020 :1()\n", + " 2 0.000 0.000 0.000 0.000 attrsettr.py:43(__getattr__)\n", + " 2 0.000 0.000 0.000 0.000 attrsettr.py:66(_get_attr_opt)\n", + " 1 0.000 0.000 0.020 0.020 base_events.py:1962(_run_once)\n", + " 2 0.000 0.000 0.000 0.000 base_events.py:772(time)\n", + " 7 0.000 0.000 0.000 0.000 enum.py:1156(__new__)\n", + " 12 0.000 0.000 0.000 0.000 enum.py:1589(_get_value)\n", + " 1 0.000 0.000 0.000 0.000 enum.py:1596(__or__)\n", + " 3 0.000 0.000 0.000 0.000 enum.py:1607(__and__)\n", + " 7 0.000 0.000 0.000 0.000 enum.py:695(__call__)\n", + " 1 0.000 0.000 0.000 0.000 events.py:87(_run)\n", + " 1 0.000 0.000 0.000 0.000 ioloop.py:750(_run_callback)\n", + " 1 0.000 0.000 0.000 0.000 iostream.py:157(_handle_event)\n", + " 1 0.000 0.000 0.000 0.000 queue.py:115(empty)\n", + " 1 0.000 0.000 0.000 0.000 queue.py:267(_qsize)\n", + " 1 0.000 0.000 0.000 0.000 selector_events.py:740(_process_events)\n", + " 1 0.000 0.000 0.000 0.000 socket.py:774(recv_multipart)\n", + " 1 0.000 0.000 0.000 0.000 typing.py:1374(__instancecheck__)\n", + " 2 0.000 0.000 0.000 0.000 typing.py:1443(__hash__)\n", + " 1 0.000 0.000 0.000 0.000 typing.py:1665(__subclasscheck__)\n", + " 1 0.000 0.000 0.000 0.000 typing.py:2371(cast)\n", + " 3 0.000 0.000 0.000 0.000 typing.py:426(inner)\n", + " 2 0.000 0.000 0.000 0.000 zmqstream.py:538(receiving)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:542(sending)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:556(_run_callback)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:583(_handle_events)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:624(_handle_recv)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:663(_rebuild_io_state)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:686(_update_handler)\n", + " 1 0.000 0.000 0.000 0.000 zmqstream.py:694()\n", + " 1 0.000 0.000 0.000 0.000 {built-in method _abc._abc_subclasscheck}\n", + " 1 0.000 0.000 0.020 0.020 {built-in method builtins.exec}\n", + " 2 0.000 0.000 0.000 0.000 {built-in method builtins.getattr}\n", + " 2 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}\n", + " 2 0.000 0.000 0.000 0.000 {built-in method builtins.hash}\n", + " 25/23 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass}\n", + " 4 0.000 0.000 0.000 0.000 {built-in method builtins.len}\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.max}\n", + " 2 0.000 0.000 0.000 0.000 {built-in method time.monotonic}\n", + " 1 0.000 0.000 0.000 0.000 {method '__exit__' of '_thread.lock' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'popleft' of 'collections.deque' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'run' of '_contextvars.Context' objects}\n", + " 2 0.000 0.000 0.000 0.000 {method 'upper' of 'str' objects}\n", + "\n", + "\n" + ] + } + ], + "source": [ + "def add_up_numbers() -> None:\n", + " \"\"\"Суммирует все числа от 1 до 1 000 000.\"\"\"\n", + " total = 0\n", + " for i in range(1, 1000001):\n", + " total += i\n", + "\n", + "\n", + "cProfile.run(\"add_up_numbers()\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3b05f7", + "metadata": {}, + "source": [ + "## Закон Амдала\n", + "Это формула, которая вычисляет, насколько ускорится выполнение программы при улучшении одного из ее фрагментов.\n", + "Согласно этой формуле ускорение всей операции равно:\n", + "\n", + "1 / ((1 — p) + (p / s))\n", + "\n", + "где s — ускорение компонента, а p — доля этого компонента в общем времени выполнения программы.\n", + "\n", + "Удвоение скорости самых медленных или длинных частей вашего кода более продуктивно, чем удвоение\n", + "скорости уже быстрых или коротких частей." + ] + }, + { + "cell_type": "markdown", + "id": "6b5d4b89", + "metadata": {}, + "source": [ + "### Анализ алгоритмов с использованием нотации «O-большое»" + ] + }, + { + "cell_type": "markdown", + "id": "57f3cb48", + "metadata": {}, + "source": [ + "Это метод анализа, который определяет «насколько замедлится код с ростом объема данных».\n", + "\n", + "1. O(1) – постоянное время (самый низкий порядок).\n", + "2. O(log n) – логарифмическое время.\n", + "3. O(n) – линейное время.\n", + "4. O(n log n) – время N-Log-N.\n", + "5. O(n^2) – полиномиальное время.\n", + "6. O(2^n) – экспоненциальное время.\n", + "7. O(n!) – факториальное время (наивысший порядок)." + ] + }, + { + "cell_type": "markdown", + "id": "718e55cf", + "metadata": {}, + "source": [ + "«О-большое» оценивает худший сценарий для любой задачи.\n", + "\n", + "«Омега-большое» оценивает лучший сценарий для алгоритма (Ω(n), Ω(log n) и т.д.).\n", + "\n", + "«Тэта-большое» описывает алгоритмы с одинаковыми порядками в лучшем и худшем случае. Например, Θ(n) описывает алгоритм с линейной эффективностью в лучшем и худшем случае — то есть алгоритм O(n) и Ω(n)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/clean-code/chapter_13_algorithm_performance_and_complexity.py b/Python/clean-code/chapter_13_algorithm_performance_and_complexity.py new file mode 100644 index 00000000..38c72c9f --- /dev/null +++ b/Python/clean-code/chapter_13_algorithm_performance_and_complexity.py @@ -0,0 +1,82 @@ +"""Измерение быстродействия и анализ сложности алгоритмов.""" + +# # Модуль timeit + +# ### Хорошее правило +# Сначала добейтесь того, чтобы ваш код работал, а потом занимайтесь его ускорением. +# Сначала работоспособность, потом эффективность! + +# + +import cProfile +import timeit + +# или так +# Для измерения времени выполнения кода можно воспользоваться +# модулем timeit + +timeit.timeit("a, b = 42, 101; a = a ^ b; b = a ^ b; a = a ^ b") +# - + +# или так +timeit.timeit( + """a, b = 42, 101 +a = a ^ b +b = a ^ b +a = a ^ b""" +) + +timeit.timeit("a, b = 42, 101; temp = a; a = b; b = temp") + +# или так +timeit.timeit("a, b = 42, 101; a, b = b, a") + +# По умолчанию код будет выполнен 1000000 раз и будет возвращено среднее время выполнения +# Но можно явно указать, сколько раз выполнить код +timeit.timeit('print("hello")', number=3) + + +# # Модуль cProfile + +# Хотя модуль timeit полезен для хронометража небольших фрагментов кода, модуль +# cProfile более эффективен для анализа целых функций или программ. + + +# + +def add_up_numbers() -> None: + """Суммирует все числа от 1 до 1 000 000.""" + total = 0 + for i in range(1, 1000001): + total += i + + +cProfile.run("add_up_numbers()") +# - + +# ## Закон Амдала +# Это формула, которая вычисляет, насколько ускорится выполнение программы при улучшении одного из ее фрагментов. +# Согласно этой формуле ускорение всей операции равно: +# +# 1 / ((1 — p) + (p / s)) +# +# где s — ускорение компонента, а p — доля этого компонента в общем времени выполнения программы. +# +# Удвоение скорости самых медленных или длинных частей вашего кода более продуктивно, чем удвоение +# скорости уже быстрых или коротких частей. + +# ### Анализ алгоритмов с использованием нотации «O-большое» + +# Это метод анализа, который определяет «насколько замедлится код с ростом объема данных». +# +# 1. O(1) – постоянное время (самый низкий порядок). +# 2. O(log n) – логарифмическое время. +# 3. O(n) – линейное время. +# 4. O(n log n) – время N-Log-N. +# 5. O(n^2) – полиномиальное время. +# 6. O(2^n) – экспоненциальное время. +# 7. O(n!) – факториальное время (наивысший порядок). + +# «О-большое» оценивает худший сценарий для любой задачи. +# +# «Омега-большое» оценивает лучший сценарий для алгоритма (Ω(n), Ω(log n) и т.д.). +# +# «Тэта-большое» описывает алгоритмы с одинаковыми порядками в лучшем и худшем случае. Например, Θ(n) описывает алгоритм с линейной эффективностью в лучшем и худшем случае — то есть алгоритм O(n) и Ω(n). diff --git a/Python/clean-code/chapter_4_choosing_variable_names.ipynb b/Python/clean-code/chapter_4_choosing_variable_names.ipynb new file mode 100644 index 00000000..723b34a6 --- /dev/null +++ b/Python/clean-code/chapter_4_choosing_variable_names.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8806f111", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Выбор понятных имен переменных.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "fe187e32", + "metadata": {}, + "source": [ + "1. Имя переменной (идентификатор) должно быть **кратким** и **содержательным**\n", + "\n", + "2. Метасинтаксически переменные используются, когда необходимо обобщенное имя, когда само имя несущественно. В Python чаще всего используют:\n", + "```\n", + "foo, bar, spam, eggs, bacon и ham\n", + "```\n", + "\n", + "3. Схемы регистра имен:\n", + "\t* snake_case - змеиный регистр\n", + "\t* camelCase - верблюжий регистр\n", + "\t* PascalCase - схема Pascal\n", + "\n", + "\t* kebab-case - схема Kebab - в Питон обычно не используется" + ] + }, + { + "cell_type": "markdown", + "id": "e5bb44a6", + "metadata": {}, + "source": [ + "### Правила формирования имен PEP 8 ###\n", + "* Все буквы должны быть буквами ASCII — то есть латинскими буквами верхнего и нижнего регистров без диакритических знаков.\n", + "* Имена модулей должны быть короткими и состоять только из букв нижнего регистра.\n", + "* Имена классов необходимо записывать в схеме Pascal.\n", + "* Имена констант следует записывать в верхнем змеином регистре.\n", + "* Имена функций, методов и переменных записывают в нижнем змеином регистре.\n", + "* Первый аргумент методов всегда должен называться self в нижнем регистре.\n", + "* Первый аргумент методов классов всегда должен называться cls в нижнем регистре.\n", + "* Приватные атрибуты классов всегда начинают с символа подчеркивания ( _ ).\n", + "* Публичные атрибуты классов никогда не начинают с символа подчеркивания ( _ )." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/clean-code/chapter_4_choosing_variable_names.py b/Python/clean-code/chapter_4_choosing_variable_names.py new file mode 100644 index 00000000..05c37159 --- /dev/null +++ b/Python/clean-code/chapter_4_choosing_variable_names.py @@ -0,0 +1,26 @@ +"""Выбор понятных имен переменных.""" + +# 1. Имя переменной (идентификатор) должно быть **кратким** и **содержательным** +# +# 2. Метасинтаксически переменные используются, когда необходимо обобщенное имя, когда само имя несущественно. В Python чаще всего используют: +# ``` +# foo, bar, spam, eggs, bacon и ham +# ``` +# +# 3. Схемы регистра имен: +# * snake_case - змеиный регистр +# * camelCase - верблюжий регистр +# * PascalCase - схема Pascal +# +# * kebab-case - схема Kebab - в Питон обычно не используется + +# ### Правила формирования имен PEP 8 ### +# * Все буквы должны быть буквами ASCII — то есть латинскими буквами верхнего и нижнего регистров без диакритических знаков. +# * Имена модулей должны быть короткими и состоять только из букв нижнего регистра. +# * Имена классов необходимо записывать в схеме Pascal. +# * Имена констант следует записывать в верхнем змеином регистре. +# * Имена функций, методов и переменных записывают в нижнем змеином регистре. +# * Первый аргумент методов всегда должен называться self в нижнем регистре. +# * Первый аргумент методов классов всегда должен называться cls в нижнем регистре. +# * Приватные атрибуты классов всегда начинают с символа подчеркивания ( _ ). +# * Публичные атрибуты классов никогда не начинают с символа подчеркивания ( _ ). diff --git a/Python/cpython.ipynb b/Python/cpython.ipynb new file mode 100644 index 00000000..b53bb699 --- /dev/null +++ b/Python/cpython.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "374fe137", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Issue #4 Cpython.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/4\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "a9093402", + "metadata": {}, + "source": [ + "1. Что такое CPython и чем он отличается от Python?\n", + "Это реализация языка Python на C.\n", + "Python - это язык, синтаксис, набор правил и стандартов, описывающих то, как работает язык, основные конструкции, функции и пр.\n", + "CPython - это программа, которая исполняет код, написанный на языке Python\n", + "\n", + "3. Сколько существует реализаций Python, и какая из них самая популярная?\n", + "В видео говорится про шесть. Но есть и другие реализации.\n", + "\n", + "4. На каком языке написан CPython?\n", + "C\n", + "\n", + "5. (опционально) Кто создал CPython?\n", + "Guido van Rossum\n", + "\n", + "6. Почему Python считается быстрым, несмотря на то, что это интерпретируемый язык?\n", + "Потому что написан на C. А язык C считается быстрым языком.\n", + "\n", + "7. Напишите путь к Интерпретатору CPython на вашем компьютере\n", + "C:\\Program Files\\Python313\\python.exe\n", + "\n", + "8. Что содержится в папке include в CPython?\n", + "Файлы, написанные на C. Те же самые, что и в репозитории CPython.\n", + "\n", + "9. Где можно найти исходный код CPython дайте ссылку на репозиторий гитхаб\n", + "https://github.com/python/cpython\n", + "\n", + "10. Как работает интерпретатор CPython при выполнении кода?\n", + "Выполняет программу построчно.\n", + "\n", + "Лексический анализ и парсинг преобразует исходный код в AST (Abstract Syntax Tree).\n", + "Компиляция в байт-код - AST компилируется в .pyc файлы (байт-код).\n", + "Интерпретация байт-кода - виртуальная машина CPython (PVM) выполняет байт-код построчно.\n", + "В отличие от PyPy, CPython не использует JIT-компиляцию — поэтому он медленнее, но стабильнее и совместимее\n", + "\n", + "11. Какая команда используется для запуска файла с помощью CPython?\n", + "python\n", + "\n", + "12. Можно ли запускать текстовые файлы через интерпретатор Python? Почему?\n", + "Можно. Интерпретатору все равно, какое расширение у файла.\n", + "\n", + "13. Как указать путь к интерпретатору и файлу для выполнения кода?\n", + "Через пробел. Например:\n", + "C:\\Program Files\\Python313\\python.exe C:\\project.txt \n", + "\n", + "14. Чем PyPy отличается от CPython?\n", + "PyPy гораздо быстрее, чем CPython (до 10 раз быстрее)\n", + "\n", + "15. Почему PyPy не может использоваться для всех проектов на Python?\n", + "PyPy достаточно новый и совместим не со всеми проектами.\n", + "\n", + "16. Где можно скачать PyPy?\n", + "https://pypy.org\n", + "\n", + "17. Как установить PyPy после скачивания?\n", + "Распаковать архив, использовать путь до файла pypy.exe / python.exe (или файлов с другими версиями интерпретатора) для запуска файлов.\n", + "Можно добавить путь к файлу в переменные окружения.\n", + "\n", + "18. Как запустить файл с помощью PyPy?\n", + "В командной строке или терминале набрать:\n", + "путь_к_интерпретатору путь_к_файлу\n", + "\n", + "19. Почему PyPy выполняет код быстрее, чем CPython?\n", + "PyPy использует JIT-компиляцию (Just-In-Time):\n", + "Он кэширует часто выполняемые участи кода, компилирует их в машинный код. В следующий раз этот код выполняется напрямую процессором.\n", + "\n", + "Практические задания\n", + "\n", + "Задание 1: Поиск и установка CPython\n", + "Установлен Python 3.13.3\n", + "\n", + "Задание 2: Исследование структуры CPython\n", + "В папке include 77 файлов *.h\n", + "\n", + "Задание 3: Запуск файла с помощью CPython\n", + "Файл запускается независимо от расширения\n", + "\n", + "Задание 4: Установка и использование PyPy\n", + "На экран выводится \"Hello from pypy!\" независимо от расширения\n", + "\n", + "Задание 5: Сравнение производительности CPython и PyPy\n", + "При использовании CPython время выполнения составило:\n", + "Execution time: 0.49318480491638184 seconds\n", + "\n", + "При использовании PyPy время выполнения составило:\n", + "Execution time: 0.008015632629394531 seconds\n", + "Что более чем в 60 раз быстрее" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/cpython.py b/Python/cpython.py new file mode 100644 index 00000000..d2453f6b --- /dev/null +++ b/Python/cpython.py @@ -0,0 +1,91 @@ +"""Issue #4 Cpython. + +https://github.com/SENATOROVAI/intro-cs/issues/4 +""" + +# 1. Что такое CPython и чем он отличается от Python? +# Это реализация языка Python на C. +# Python - это язык, синтаксис, набор правил и стандартов, описывающих то, как работает язык, основные конструкции, функции и пр. +# CPython - это программа, которая исполняет код, написанный на языке Python +# +# 3. Сколько существует реализаций Python, и какая из них самая популярная? +# В видео говорится про шесть. Но есть и другие реализации. +# +# 4. На каком языке написан CPython? +# C +# +# 5. (опционально) Кто создал CPython? +# Guido van Rossum +# +# 6. Почему Python считается быстрым, несмотря на то, что это интерпретируемый язык? +# Потому что написан на C. А язык C считается быстрым языком. +# +# 7. Напишите путь к Интерпретатору CPython на вашем компьютере +# C:\Program Files\Python313\python.exe +# +# 8. Что содержится в папке include в CPython? +# Файлы, написанные на C. Те же самые, что и в репозитории CPython. +# +# 9. Где можно найти исходный код CPython дайте ссылку на репозиторий гитхаб +# https://github.com/python/cpython +# +# 10. Как работает интерпретатор CPython при выполнении кода? +# Выполняет программу построчно. +# +# Лексический анализ и парсинг преобразует исходный код в AST (Abstract Syntax Tree). +# Компиляция в байт-код - AST компилируется в .pyc файлы (байт-код). +# Интерпретация байт-кода - виртуальная машина CPython (PVM) выполняет байт-код построчно. +# В отличие от PyPy, CPython не использует JIT-компиляцию — поэтому он медленнее, но стабильнее и совместимее +# +# 11. Какая команда используется для запуска файла с помощью CPython? +# python +# +# 12. Можно ли запускать текстовые файлы через интерпретатор Python? Почему? +# Можно. Интерпретатору все равно, какое расширение у файла. +# +# 13. Как указать путь к интерпретатору и файлу для выполнения кода? +# Через пробел. Например: +# C:\Program Files\Python313\python.exe C:\project.txt +# +# 14. Чем PyPy отличается от CPython? +# PyPy гораздо быстрее, чем CPython (до 10 раз быстрее) +# +# 15. Почему PyPy не может использоваться для всех проектов на Python? +# PyPy достаточно новый и совместим не со всеми проектами. +# +# 16. Где можно скачать PyPy? +# https://pypy.org +# +# 17. Как установить PyPy после скачивания? +# Распаковать архив, использовать путь до файла pypy.exe / python.exe (или файлов с другими версиями интерпретатора) для запуска файлов. +# Можно добавить путь к файлу в переменные окружения. +# +# 18. Как запустить файл с помощью PyPy? +# В командной строке или терминале набрать: +# путь_к_интерпретатору путь_к_файлу +# +# 19. Почему PyPy выполняет код быстрее, чем CPython? +# PyPy использует JIT-компиляцию (Just-In-Time): +# Он кэширует часто выполняемые участи кода, компилирует их в машинный код. В следующий раз этот код выполняется напрямую процессором. +# +# Практические задания +# +# Задание 1: Поиск и установка CPython +# Установлен Python 3.13.3 +# +# Задание 2: Исследование структуры CPython +# В папке include 77 файлов *.h +# +# Задание 3: Запуск файла с помощью CPython +# Файл запускается независимо от расширения +# +# Задание 4: Установка и использование PyPy +# На экран выводится "Hello from pypy!" независимо от расширения +# +# Задание 5: Сравнение производительности CPython и PyPy +# При использовании CPython время выполнения составило: +# Execution time: 0.49318480491638184 seconds +# +# При использовании PyPy время выполнения составило: +# Execution time: 0.008015632629394531 seconds +# Что более чем в 60 раз быстрее diff --git a/Python/issues.ipynb b/Python/issues.ipynb new file mode 100644 index 00000000..acdd58ad --- /dev/null +++ b/Python/issues.ipynb @@ -0,0 +1,151 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6671986b", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0be168e5", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Issue #2 issues.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/4\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "dce77516", + "metadata": {}, + "source": [ + "1. Что такое Issues на GitHub и для чего они используются?\n", + "Issues - это функционал Git, который позволяет \"подсвечивать\" и обсуждать сложные места / проблемы в коде.\n", + "\n", + "2. Чем Issues отличаются от других инструментов управления задачами?\n", + "Поддерживается возможность привязать коммит или ПР к конкретному issue.\n", + "Автоматическое закрытие issue после мержа соответствующего ПР-а.\n", + "Возможность устанавливать метки и назначать ответственных.\n", + "\n", + "3. Какие основные компоненты (поля) есть у каждого Issue?\n", + "Title, Description, Labels, Projects, Milestone, Development, Notification, Linked Pull Requests/Commits, Assignees\n", + "\n", + "4. Как создать новое Issue в репозитории?\n", + "Копируем ссылку на участок кода.\n", + "Issues -> New issue\n", + "Выбираем тип issue.\n", + "Заполняем тайл, описание и другие поля.\n", + "\n", + "5. Какие данные рекомендуется указывать в описании Issue для лучшего понимания задачи?\n", + "Основная цель при заполнении issue - максимально полно описать существующую проблему.\n", + "Следует приложить ссылку на код / коммит, код ошибки, трассировку, скриншоты терминала.\n", + "\n", + "6. Какие теги (labels) можно добавить к Issue? Какие из них стандартные?\n", + "Стандартные теги:\n", + "bug, dependencies, documentation, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix\n", + "\n", + "7. Как прикрепить Assignees (ответственных) к Issue?\n", + "Кнопка Assignees справа\n", + "\n", + "8. Как использовать Labels для классификации задач?\n", + "С помощью лейблов можно классифицировать задачи по проектам, статусам, типам и приоритетам.\n", + "\n", + "9. Для чего нужен Milestone, и как связать его с Issue?\n", + "Срок выполнения задачи. Выставляется в поле Milestone на панели справа.\n", + "\n", + "10. Как привязать Issue к пул-реквесту (Pull Request)?\n", + "Использовать ключевые слова в описании ПР или коммита с указанием номера issue (например, #7) или ссылки на issue.\n", + "Привязать конкретный ПР к issue через кнопку Development на странице ПР-а.\n", + "Привязать конкретный ПР к issue через кнопку Development на странице issue.\n", + "\n", + "11. Как добавить комментарий к существующему Issue?\n", + "На странице конкретного issue внизу страницы есть форма для добавления комментария.\n", + "\n", + "12. Как закрыть Issue вручную?\n", + "Кнопка \"Close issue\" на странице issue.\n", + "\n", + "13. Можно ли автоматически закрыть Issue с помощью сообщения в коммите или пул-реквесте? Как это сделать?\n", + "Да, используя в описании ПР или коммита ключевые слова и номер или ссылку на issue:\n", + "close\n", + "closes\n", + "closed\n", + "fix\n", + "fixes\n", + "fixed\n", + "resolve\n", + "resolves\n", + "resolved\n", + "\n", + "14. Как повторно открыть закрытое Issue, если работа ещё не завершена?\n", + "Кнопка \"Reopen issue\" на странице issue.\n", + "\n", + "15. Как найти все открытые или закрытые Issues в репозитории?\n", + "Вкладка Issues в репозитории. Фильтры Open / Closed\n", + "\n", + "16. Как использовать фильтры для поиска Issues по меткам, исполнителям или другим критериям?\n", + "В поле фильтра по параметрам state, type, author, involves, assignee, label, linked,has, org, user и пр.\n", + "Можно использовать знак - в качестве отрицания.\n", + "Или использовать соответствующие кнопки в таблице с issues.\n", + "\n", + "17. Как сортировать Issues по приоритету, дате создания или другим параметрам?\n", + "Кнопка Sort by\n", + "\n", + "18. Как настроить автоматические уведомления о новых или изменённых Issues?\n", + "Раздел Watch в репозитории.\n", + "\n", + "19. Что такое Projects в контексте GitHub, и как связать их с Issues?\n", + "Доска для планирования и отслеживания прогресса задач. В Projects можно добавить любой из имеющихся issue.\n", + "\n", + "20. Какие сторонние инструменты можно использовать для автоматизации работы с Issues (например, боты, Webhooks)?\n", + "Probot, GitHub Actions, Zapier, Make, кастомные решения на Webhooks\n", + "\n", + "21. Как упомянуть другого пользователя в комментарии к Issue?\n", + "@github-username\n", + "\n", + "22. Как запросить дополнительные данные или уточнения у автора Issue?\n", + "В комментариях к issue.\n", + "\n", + "23. Что делать, если Issue неактуально или его нужно объединить с другим?\n", + "Закрыть его с комментарием \"non-actual\" или \"duplicate of ...\" и указать номер issue (например, #7).\n", + "\n", + "24. Как использовать шаблоны для создания Issues?\n", + "В папку .github/ISSUE_TEMPLATE/ положить шаблоны с расширением *.md или *.yml.\n", + "\n", + "25. Что такое Linked Issues, и как создать связь между задачами?\n", + "Это связанные между собой issue.\n", + "Установить связь можно через кнопку Development или путем упоминания других issue в названии или описании.\n", + "\n", + "26. Какие метрики (например, время выполнения) можно отслеживать с помощью Issues?\n", + "Временные метрики (среднее время выполнения, время первого ответа).\n", + "Количество открытых / закрытых issue.\n", + "\n", + "27. Какие best practices рекомендуются при работе с Issues в команде?\n", + "Использовать информативные заголовки и описания.\n", + "Использовать метки.\n", + "Связывать issue.\n", + "Регулярно актуализировать статусы, закрывать неактуальные.\n", + "Назначать ответственных.\n", + "Использовать шаблоны.\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/issues.py b/Python/issues.py new file mode 100644 index 00000000..22ded674 --- /dev/null +++ b/Python/issues.py @@ -0,0 +1,117 @@ +# + +"""Issue #2 issues. + +https://github.com/SENATOROVAI/intro-cs/issues/4 +""" + +# 1. Что такое Issues на GitHub и для чего они используются? +# Issues - это функционал Git, который позволяет "подсвечивать" и обсуждать сложные места / проблемы в коде. +# +# 2. Чем Issues отличаются от других инструментов управления задачами? +# Поддерживается возможность привязать коммит или ПР к конкретному issue. +# Автоматическое закрытие issue после мержа соответствующего ПР-а. +# Возможность устанавливать метки и назначать ответственных. +# +# 3. Какие основные компоненты (поля) есть у каждого Issue? +# Title, Description, Labels, Projects, Milestone, Development, Notification, Linked Pull Requests/Commits, Assignees +# +# 4. Как создать новое Issue в репозитории? +# Копируем ссылку на участок кода. +# Issues -> New issue +# Выбираем тип issue. +# Заполняем тайл, описание и другие поля. +# +# 5. Какие данные рекомендуется указывать в описании Issue для лучшего понимания задачи? +# Основная цель при заполнении issue - максимально полно описать существующую проблему. +# Следует приложить ссылку на код / коммит, код ошибки, трассировку, скриншоты терминала. +# +# 6. Какие теги (labels) можно добавить к Issue? Какие из них стандартные? +# Стандартные теги: +# bug, dependencies, documentation, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix +# +# 7. Как прикрепить Assignees (ответственных) к Issue? +# Кнопка Assignees справа +# +# 8. Как использовать Labels для классификации задач? +# С помощью лейблов можно классифицировать задачи по проектам, статусам, типам и приоритетам. +# +# 9. Для чего нужен Milestone, и как связать его с Issue? +# Срок выполнения задачи. Выставляется в поле Milestone на панели справа. +# +# 10. Как привязать Issue к пул-реквесту (Pull Request)? +# Использовать ключевые слова в описании ПР или коммита с указанием номера issue (например, #7) или ссылки на issue. +# Привязать конкретный ПР к issue через кнопку Development на странице ПР-а. +# Привязать конкретный ПР к issue через кнопку Development на странице issue. +# +# 11. Как добавить комментарий к существующему Issue? +# На странице конкретного issue внизу страницы есть форма для добавления комментария. +# +# 12. Как закрыть Issue вручную? +# Кнопка "Close issue" на странице issue. +# +# 13. Можно ли автоматически закрыть Issue с помощью сообщения в коммите или пул-реквесте? Как это сделать? +# Да, используя в описании ПР или коммита ключевые слова и номер или ссылку на issue: +# close +# closes +# closed +# fix +# fixes +# fixed +# resolve +# resolves +# resolved +# +# 14. Как повторно открыть закрытое Issue, если работа ещё не завершена? +# Кнопка "Reopen issue" на странице issue. +# +# 15. Как найти все открытые или закрытые Issues в репозитории? +# Вкладка Issues в репозитории. Фильтры Open / Closed +# +# 16. Как использовать фильтры для поиска Issues по меткам, исполнителям или другим критериям? +# В поле фильтра по параметрам state, type, author, involves, assignee, label, linked,has, org, user и пр. +# Можно использовать знак - в качестве отрицания. +# Или использовать соответствующие кнопки в таблице с issues. +# +# 17. Как сортировать Issues по приоритету, дате создания или другим параметрам? +# Кнопка Sort by +# +# 18. Как настроить автоматические уведомления о новых или изменённых Issues? +# Раздел Watch в репозитории. +# +# 19. Что такое Projects в контексте GitHub, и как связать их с Issues? +# Доска для планирования и отслеживания прогресса задач. В Projects можно добавить любой из имеющихся issue. +# +# 20. Какие сторонние инструменты можно использовать для автоматизации работы с Issues (например, боты, Webhooks)? +# Probot, GitHub Actions, Zapier, Make, кастомные решения на Webhooks +# +# 21. Как упомянуть другого пользователя в комментарии к Issue? +# @github-username +# +# 22. Как запросить дополнительные данные или уточнения у автора Issue? +# В комментариях к issue. +# +# 23. Что делать, если Issue неактуально или его нужно объединить с другим? +# Закрыть его с комментарием "non-actual" или "duplicate of ..." и указать номер issue (например, #7). +# +# 24. Как использовать шаблоны для создания Issues? +# В папку .github/ISSUE_TEMPLATE/ положить шаблоны с расширением *.md или *.yml. +# +# 25. Что такое Linked Issues, и как создать связь между задачами? +# Это связанные между собой issue. +# Установить связь можно через кнопку Development или путем упоминания других issue в названии или описании. +# +# 26. Какие метрики (например, время выполнения) можно отслеживать с помощью Issues? +# Временные метрики (среднее время выполнения, время первого ответа). +# Количество открытых / закрытых issue. +# +# 27. Какие best practices рекомендуются при работе с Issues в команде? +# Использовать информативные заголовки и описания. +# Использовать метки. +# Связывать issue. +# Регулярно актуализировать статусы, закрывать неактуальные. +# Назначать ответственных. +# Использовать шаблоны. +# +# +# diff --git a/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.ipynb b/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.ipynb new file mode 100644 index 00000000..2f01217b --- /dev/null +++ b/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "7abda9b5", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Алгоритмы по заданиям из \"Python - это просто\".\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "fe187e32", + "metadata": {}, + "source": [ + "##### 1. Напишите алгоритм для расчета простых процентов от некоторой суммы.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Возьмем сумму и сохрани ее в переменную sum. \n", + "Шаг 3. Возьмем проценты и сохраним их переменную percentages. \n", + "Шаг 4. Умножаем sum на percentages и делим на 100. \n", + "Шаг 5. Результат сохраняем в переменную result. \n", + "Шаг 6. Выводим переменную result. \n", + "Шаг 7. Конец. \n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "216ecd89", + "metadata": {}, + "source": [ + "##### 2. Напишите алгоритм для вычисления площади прямоугольника.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Возьмем длину прямоугольника и сохраним ее в переменную length. \n", + "Шаг 3. Возьмем ширину прямоугольника и сохраним ее в переменную width. \n", + "Шаг 4. Умножаем length на width и сохраняем результат в переменную area. \n", + "Шаг 5. Выводим переменную area. \n", + "Шаг 6. Конец. \n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "0cbbb719", + "metadata": {}, + "source": [ + "##### 3. Напишите алгоритм вычисления периметра круга.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Возьмем радиус круга и сохраним в переменную radius. \n", + "Шаг 3. Умножаем radius на 2 и умножаем на 3.14. \n", + "Шаг 4. Результат сохраняем в переменную perimeter. \n", + "Шаг 5. Выводим переменную perimeter. \n", + "Шаг 6. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "d59849bd", + "metadata": {}, + "source": [ + "##### 4. Напишите алгоритм, который находит все простые числа меньше 100.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Создадим список чисел от 2 до 100 и сохраним в переменную array. \n", + "Шаг 3. Возьмем первое число из списка. \n", + "Шаг 4. Удалим из списка все числа, кратные текущему числу. \n", + "Шаг 5. Возьмем следующее число из списка. \n", + "Шаг 6. Повторяем шаги 4 и 5 до тех, пока не дойдем до конца списка. \n", + "Шаг 7. Выводим переменную array. \n", + "Шаг 8. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "a8cc624c", + "metadata": {}, + "source": [ + "##### 5. Напишите алгоритм превращения предложения, написанного в верхнем регистре, в обычный для предложений регистр.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Сохраним входное предложение в переменную sentence. \n", + "Шаг 3. Сохраним значение 0 в переменную index. \n", + "Шаг 4. Увеличиваем значение index на 1. \n", + "Шаг 5. Проверяем, выполняется ли условие index < длины sentence. Если да, переходим к шагу 6. Если нет, переходим к шагу 9. \n", + "Шаг 6. Проверяем, является ли символ по индексу index в строке sentence буквой. Если да, переходим к шагу 7. Если нет, переходим к шагу 4. \n", + "Шаг 7. В строке sentence по индексу index заменяем букву аналогичной буквой из нижнего регистра. \n", + "Шаг 8. Переходим к шагу 4. \n", + "Шаг 9. Выводим переменную sentence. \n", + "Шаг 10. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "3e393ef5", + "metadata": {}, + "source": [ + "##### 6. Составьте блок-схему приготовления льда из кипяченой воды с помощью холодильника.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Возьмем чайник. \n", + "Шаг 3. Нальем в чайник 500 мл воды. \n", + "Шаг 4. Закипятим воду в чайнике. \n", + "Шаг 5. Остудим воду до комнатной температуры. \n", + "Шаг 6. Нальем воду в форму для льда. \n", + "Шаг 7. Поместим форму для льда с водой в морозильную камеру на 3-6 часов до полного замерзания. \n", + "Шаг 8. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "c8f8c473", + "metadata": {}, + "source": [ + "##### 7. Составьте блок-схему для нахождения суммы всех четных чисел меньше ста.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Создадим переменную sum со значением 0. \n", + "Шаг 3. Создадим переменную index со значением 0. \n", + "Шаг 4. Проверим, выполняется ли условие: остаток от деления i на 2 равен нулю. Если да, переходим к шагу 5. Если нет, переходим к шагу 6. \n", + "Шаг 5. Увеличиваем значение sum на index. \n", + "Шаг 6. Увеличиваем значение index на 1. \n", + "Шаг 7. Проверяем, выполняется ли условие index < 100. Если да, переходим к шагу 4. Если нет, переходим к шагу 7. \n", + "Шаг 8. Выводим переменную sum. \n", + "Шаг 9. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "a6f5b83d", + "metadata": {}, + "source": [ + "##### 8. Составьте блок-схему для вычисления квадрата всех нечетных чисел от 1 до 15 \n", + "включительно.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Создадим переменную index со значением 0. \n", + "Шаг 3. Создадим неинициализированную переменную square. \n", + "Шаг 4. Проверяем, выполняется ли условие: остаток от деления i на 2 равен единице. Если да, переходим к шагу 5. Если нет, переходим к шагу 7. \n", + "Шаг 5. Сохраним в переменную square значение i*i. \n", + "Шаг 6. Выводим переменную square. \n", + "Шаг 7. Увеличиваем значение index на 1. \n", + "Шаг 8. Проверяем, выполняется ли условие i <= 15. Если да, переходим к шагу 4. Если нет, переходим к шагу 9. \n", + "Шаг 9. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "a48bd358", + "metadata": {}, + "source": [ + "##### 9. Составьте блок-схему вывода таблицы умножения на 3.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Создадим переменную x со значением 0. \n", + "Шаг 3. Создадим переменную n со значением 3. \n", + "Шаг 4. Создадим неинициализированную переменную product. \n", + "Шаг 5. Умножаем x на n. \n", + "Шаг 6. Сохраняем результат в переменную product. \n", + "Шаг 7. Выводим строку x * n = product. \n", + "Шаг 8. Увеличиваем значение x на 1. \n", + "Шаг 9. Проверяем, выполняется ли условие x <= 10. Если да, переходим к шагу 5. Если нет, переходим к шагу 10. \n", + "Шаг 10. Конец. " + ] + }, + { + "cell_type": "markdown", + "id": "fa5a7c4c", + "metadata": {}, + "source": [ + "##### 10. Составьте блок-схему для расчета сложных процентов (с капитализацией).\n", + "Расчет сложных процентов с ежемесячной капитализацией.\n", + "\n", + "Шаг 1. Начало. \n", + "Шаг 2. Введем начальную сумму вклада и сохраним ее в переменную deposit. \n", + "Шаг 3. Введем годовую процентную ставку в процентах и сохраним ее в переменную percents. \n", + "Шаг 4. Введем количество раз начисления процентов в год и сохраним результат ввода в переменную compounding_frequency. \n", + "Шаг 5. Введем количество месяцев вклада и сохраним в переменную months. \n", + "Шаг 6. Создадим неинициализированную переменную temp_result. \n", + "Шаг 7. Создадим неинициализированную переменную power. \n", + "Шаг 8. Создадим неинициализированную переменную result. \n", + "Шаг 9. Создадим неинициализированную переменную temp_result для хранения промежуточных результатов. \n", + "Шаг 10. Разделим переменную percents на 100 и сохраним результат в переменную temp_result. \n", + "Шаг 11. Разделим значение из temp_result на compounding_frequency и сохраним результат в переменную temp_result. \n", + "Шаг 12. К значению из temp_result прибавим 1 и сохраним результат в переменную temp_result. \n", + "Шаг 13. Значение из переменной months делим на 12 и умножаем на значение из переменной compounding_frequency. \n", + "Шаг 14. Сохраняем результат в переменную power.\n", + "Шаг 15. Возводим значение из temp_result в степень power и сохраним результат в переменную temp_result. \n", + "Шаг 16. Умножаем значение из переменной deposit на значение из переменной temp_result и сохраняем результат в переменную result. \n", + "Шаг 17. Выводим переменную result. \n", + "Шаг 18. Конец. " + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.py b/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.py new file mode 100644 index 00000000..e61763c5 --- /dev/null +++ b/Python/made-easy/chapter_1_introduction_to_data_science_and_programming_basics.py @@ -0,0 +1,126 @@ +"""Алгоритмы по заданиям из "Python - это просто".""" + +# ##### 1. Напишите алгоритм для расчета простых процентов от некоторой суммы. +# +# Шаг 1. Начало. +# Шаг 2. Возьмем сумму и сохрани ее в переменную sum. +# Шаг 3. Возьмем проценты и сохраним их переменную percentages. +# Шаг 4. Умножаем sum на percentages и делим на 100. +# Шаг 5. Результат сохраняем в переменную result. +# Шаг 6. Выводим переменную result. +# Шаг 7. Конец. +# + +# ##### 2. Напишите алгоритм для вычисления площади прямоугольника. +# +# Шаг 1. Начало. +# Шаг 2. Возьмем длину прямоугольника и сохраним ее в переменную length. +# Шаг 3. Возьмем ширину прямоугольника и сохраним ее в переменную width. +# Шаг 4. Умножаем length на width и сохраняем результат в переменную area. +# Шаг 5. Выводим переменную area. +# Шаг 6. Конец. +# + +# ##### 3. Напишите алгоритм вычисления периметра круга. +# +# Шаг 1. Начало. +# Шаг 2. Возьмем радиус круга и сохраним в переменную radius. +# Шаг 3. Умножаем radius на 2 и умножаем на 3.14. +# Шаг 4. Результат сохраняем в переменную perimeter. +# Шаг 5. Выводим переменную perimeter. +# Шаг 6. Конец. + +# ##### 4. Напишите алгоритм, который находит все простые числа меньше 100. +# +# Шаг 1. Начало. +# Шаг 2. Создадим список чисел от 2 до 100 и сохраним в переменную array. +# Шаг 3. Возьмем первое число из списка. +# Шаг 4. Удалим из списка все числа, кратные текущему числу. +# Шаг 5. Возьмем следующее число из списка. +# Шаг 6. Повторяем шаги 4 и 5 до тех, пока не дойдем до конца списка. +# Шаг 7. Выводим переменную array. +# Шаг 8. Конец. + +# ##### 5. Напишите алгоритм превращения предложения, написанного в верхнем регистре, в обычный для предложений регистр. +# +# Шаг 1. Начало. +# Шаг 2. Сохраним входное предложение в переменную sentence. +# Шаг 3. Сохраним значение 0 в переменную index. +# Шаг 4. Увеличиваем значение index на 1. +# Шаг 5. Проверяем, выполняется ли условие index < длины sentence. Если да, переходим к шагу 6. Если нет, переходим к шагу 9. +# Шаг 6. Проверяем, является ли символ по индексу index в строке sentence буквой. Если да, переходим к шагу 7. Если нет, переходим к шагу 4. +# Шаг 7. В строке sentence по индексу index заменяем букву аналогичной буквой из нижнего регистра. +# Шаг 8. Переходим к шагу 4. +# Шаг 9. Выводим переменную sentence. +# Шаг 10. Конец. + +# ##### 6. Составьте блок-схему приготовления льда из кипяченой воды с помощью холодильника. +# +# Шаг 1. Начало. +# Шаг 2. Возьмем чайник. +# Шаг 3. Нальем в чайник 500 мл воды. +# Шаг 4. Закипятим воду в чайнике. +# Шаг 5. Остудим воду до комнатной температуры. +# Шаг 6. Нальем воду в форму для льда. +# Шаг 7. Поместим форму для льда с водой в морозильную камеру на 3-6 часов до полного замерзания. +# Шаг 8. Конец. + +# ##### 7. Составьте блок-схему для нахождения суммы всех четных чисел меньше ста. +# +# Шаг 1. Начало. +# Шаг 2. Создадим переменную sum со значением 0. +# Шаг 3. Создадим переменную index со значением 0. +# Шаг 4. Проверим, выполняется ли условие: остаток от деления i на 2 равен нулю. Если да, переходим к шагу 5. Если нет, переходим к шагу 6. +# Шаг 5. Увеличиваем значение sum на index. +# Шаг 6. Увеличиваем значение index на 1. +# Шаг 7. Проверяем, выполняется ли условие index < 100. Если да, переходим к шагу 4. Если нет, переходим к шагу 7. +# Шаг 8. Выводим переменную sum. +# Шаг 9. Конец. + +# ##### 8. Составьте блок-схему для вычисления квадрата всех нечетных чисел от 1 до 15 +# включительно. +# +# Шаг 1. Начало. +# Шаг 2. Создадим переменную index со значением 0. +# Шаг 3. Создадим неинициализированную переменную square. +# Шаг 4. Проверяем, выполняется ли условие: остаток от деления i на 2 равен единице. Если да, переходим к шагу 5. Если нет, переходим к шагу 7. +# Шаг 5. Сохраним в переменную square значение i*i. +# Шаг 6. Выводим переменную square. +# Шаг 7. Увеличиваем значение index на 1. +# Шаг 8. Проверяем, выполняется ли условие i <= 15. Если да, переходим к шагу 4. Если нет, переходим к шагу 9. +# Шаг 9. Конец. + +# ##### 9. Составьте блок-схему вывода таблицы умножения на 3. +# +# Шаг 1. Начало. +# Шаг 2. Создадим переменную x со значением 0. +# Шаг 3. Создадим переменную n со значением 3. +# Шаг 4. Создадим неинициализированную переменную product. +# Шаг 5. Умножаем x на n. +# Шаг 6. Сохраняем результат в переменную product. +# Шаг 7. Выводим строку x * n = product. +# Шаг 8. Увеличиваем значение x на 1. +# Шаг 9. Проверяем, выполняется ли условие x <= 10. Если да, переходим к шагу 5. Если нет, переходим к шагу 10. +# Шаг 10. Конец. + +# ##### 10. Составьте блок-схему для расчета сложных процентов (с капитализацией). +# Расчет сложных процентов с ежемесячной капитализацией. +# +# Шаг 1. Начало. +# Шаг 2. Введем начальную сумму вклада и сохраним ее в переменную deposit. +# Шаг 3. Введем годовую процентную ставку в процентах и сохраним ее в переменную percents. +# Шаг 4. Введем количество раз начисления процентов в год и сохраним результат ввода в переменную compounding_frequency. +# Шаг 5. Введем количество месяцев вклада и сохраним в переменную months. +# Шаг 6. Создадим неинициализированную переменную temp_result. +# Шаг 7. Создадим неинициализированную переменную power. +# Шаг 8. Создадим неинициализированную переменную result. +# Шаг 9. Создадим неинициализированную переменную temp_result для хранения промежуточных результатов. +# Шаг 10. Разделим переменную percents на 100 и сохраним результат в переменную temp_result. +# Шаг 11. Разделим значение из temp_result на compounding_frequency и сохраним результат в переменную temp_result. +# Шаг 12. К значению из temp_result прибавим 1 и сохраним результат в переменную temp_result. +# Шаг 13. Значение из переменной months делим на 12 и умножаем на значение из переменной compounding_frequency. +# Шаг 14. Сохраняем результат в переменную power. +# Шаг 15. Возводим значение из temp_result в степень power и сохраним результат в переменную temp_result. +# Шаг 16. Умножаем значение из переменной deposit на значение из переменной temp_result и сохраняем результат в переменную result. +# Шаг 17. Выводим переменную result. +# Шаг 18. Конец. diff --git a/Python/makarov/chapter_01_variables.ipynb b/Python/makarov/chapter_01_variables.ipynb new file mode 100644 index 00000000..19ae24f9 --- /dev/null +++ b/Python/makarov/chapter_01_variables.ipynb @@ -0,0 +1,654 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров. Переменные в Питоне.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I0uPBX2mQzs7" + }, + "source": [ + "## Переменные в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "R7ZrffglCQb1" + }, + "source": [ + "### Создание (объявление) переменных" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "collapsed": true, + "id": "RFFjF2n_QvO_", + "outputId": "3583b443-75f7-4189-f969-6d07c65e23fb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15\n" + ] + } + ], + "source": [ + "# можно создать переменную, присвоив ей числовое значение\n", + "num: int = 15\n", + "print(num)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TnZXUdj7Cu93", + "outputId": "163d7540-af1c-4589-ae1d-cf773592e197" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "# кроме того, переменной можно задать строковое (текстовое) значение\n", + "text: str = \"Я программирую на Питоне\"\n", + "print(str)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "exVyB2fYCzb1", + "outputId": "d369c56c-3281-4b3d-b47d-2f772350eb7e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Питон C++ PHP\n" + ] + } + ], + "source": [ + "# в Питоне можно присвоить разные значения сразу нескольким переменным\n", + "var_a, var_b, var_c = \"Питон\", \"C++\", \"PHP\"\n", + "print(var_a, var_b, var_c)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "83oEbEXJEQP-", + "outputId": "9e833614-735a-4334-a4bd-7b5d692c1327" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "То же самое значение То же самое значение То же самое значение\n" + ] + } + ], + "source": [ + "# а также присвоить одно и то же значение нескольким переменным\n", + "x_var = y_var = z_var = \"То же самое значение\"\n", + "print(x_var, y_var, z_var)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zYAaLNLeFRmp", + "outputId": "c048011d-ee50-4a83-a702-5e8d5b4dec2a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "помидоры огурцы картофель\n" + ] + } + ], + "source": [ + "# каждый элемент списка можно \"распаковать\" в переменные\n", + "my_list: list[str] = [\"помидоры\", \"огурцы\", \"картофель\"]\n", + "a_var, b_var, c_var = my_list\n", + "print(a_var, b_var, c_var)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eVDXV2fmGynU" + }, + "source": [ + "### Автоматическое определение типа данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EpbgqBEFGPt-" + }, + "outputs": [], + "source": [ + "x_int = 256 # в этом случае переменной x присваивается тип int (целочисленное значение)\n", + "y_float = 0.25 # y становится float (десятичной дробью)\n", + "z_str = \"Просто текст\" # z становится str (строкой)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zcpZunn-Hlwe" + }, + "source": [ + "### Как узнать тип переменной в Питоне" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1LLieOW-HbfI", + "outputId": "2297e548-d0ab-408e-c0b0-bcb55717b010" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n" + ] + } + ], + "source": [ + "# узнаем тип переменных из предыдущего примера\n", + "print(type(x_int), type(y_float), type(z_str))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bKnzOjusH-HD" + }, + "source": [ + "### Присвоение и преобразование типа данных" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W65e6CJ8SmxF" + }, + "source": [ + "Присвоение типа данных" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "OHnMZJYKHs-L" + }, + "outputs": [], + "source": [ + "x_example_1 = str(25) # число 25 превратится в строку\n", + "y_example_2 = int(25) # число 25 останется целочисленным значением\n", + "z_example_3 = float(25) # число 25 превратится в десятичную дробь" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sekbYKd_JS3G", + "outputId": "edf741b7-e1d7-40d0-dc11-62199ae40ec6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n" + ] + } + ], + "source": [ + "print(type(x_example_1), type(y_example_2), type(z_example_3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rtPT684JSq6u" + }, + "source": [ + "Изменение типа данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "H70L99djJU9p", + "outputId": "6078e6ab-d753-4e62-92cb-78bbdc996a5d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "# преобразуем строку, похожую на целое число, в целое число\n", + "print(type(int(\"25\")))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Zn9ED-z9RZjJ", + "outputId": "086ad810-8bcb-405e-f389-7e7fb9692a00" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "# или строку, похожую на дробь, в настоящую десятичную дробь\n", + "print(type(float(\"2.5\")))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QlC-xDZzQ6aN", + "outputId": "727d2413-c113-4e53-8e53-e3bf96d64bb6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "36\n", + "\n" + ] + } + ], + "source": [ + "# преобразуем дробь в целочисленное значение\n", + "# обратите внимание, что округления в большую сторону не происходит\n", + "print(int(36.6))\n", + "print(type(int(36.6)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tYAJJ-foRTbF", + "outputId": "80eba5bc-c224-4ce2-a71b-be1220781683" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + } + ], + "source": [ + "# конечно, и целое число, и дробь можно преобразовать в строку\n", + "print(type(str(25)))\n", + "print(type(str(36.6)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5d6zBJQwW5Gp" + }, + "source": [ + "### Именование переменных" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EqkcH1eS2Z3J" + }, + "source": [ + "Допустимые имена переменных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IuzhrpKASPNe" + }, + "outputs": [], + "source": [ + "value = \"Просто переменная\"\n", + "_variable = \"Просто переменная\"\n", + "variable_ = \"Просто переменная\"\n", + "my_variable = \"Просто переменная\"\n", + "My_variable_123 = \"Просто переменная\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "72nuhEgw2fnv" + }, + "source": [ + "Имя переменной состоит из нескольких слов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1Vm18lbD0lmR" + }, + "outputs": [], + "source": [ + "# можно применить так называемый верблюжий регистр, camelCase\n", + "# все слова кроме первого начинаются с заглавной буквы и пишутся слитно\n", + "camelCaseVariable = \"Верблюжий регистр\" # noqa: N816\n", + "\n", + "# нотацию Паскаль, PascalCase (то же самое, только теперь все слова пишутся с заглавной)\n", + "PascalCaseVariable = \"Нотация Паскаль\"\n", + "\n", + "# змеиный стиль, snake_case (с нижними подчеркиваниями)\n", + "snake_case_variable = \"Змеиная нотация\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nzDydgtQ2kol" + }, + "source": [ + "Недопустимые названия переменной" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 106 + }, + "id": "UfYlDOrD2odd", + "outputId": "990c05ec-3c6c-4080-90aa-88d5096e1a96" + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid decimal literal (, line 2)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m 123variable = 'Так делать нельзя'\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid decimal literal\n" + ] + } + ], + "source": [ + "# my-variable = 'Так делать нельзя'\n", + "# 123variable = 'Так делать нельзя'\n", + "# my variable = 'Так делать нельзя'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3xhznH_GEZWX" + }, + "source": [ + "### Ответы на вопросы" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0lIYm5SMEcKX" + }, + "source": [ + "**Вопрос**. Как можно преобразовать список чисел таким образом, чтобы каждый элемент списка превратился в отдельную строку?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5jwegAoqEbTI" + }, + "outputs": [], + "source": [ + "# возьмем простой список\n", + "list_ = [1, 2, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "ccFKfkOSEpVm", + "outputId": "943a9e0f-aeaf-4173-a589-5c3ee0a2f04c" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'[1, 2, 3]'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# использовать только функцию str() нельзя\n", + "str(list_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mAPxwVGXFRzm", + "outputId": "23eae9f6-cd59-4df9-88d8-687077b17fb0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['1', '2', '3']" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вариант 1: объявить новый список и в цикле for помещать туда строковые значения\n", + "list_str = []\n", + "\n", + "for num in list_:\n", + " list_str.append(str(num))\n", + "\n", + "list_str" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "soCEH3jlExSX", + "outputId": "7b26d106-3226-478b-c843-f93b9a8d8d4e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['1', '2', '3']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вариант 2: использовать list comprehension\n", + "str_list = [str(num) for num in list_]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "W7TJpjR1FBZl", + "outputId": "9a94fa59-5ef4-4bad-f063-2dca9f974d54" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['1', '2', '3']" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вариант 3: функции map() и list()\n", + "list(map(str, list_))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1ZlJBM4XFOv5" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_01_variables.py b/Python/makarov/chapter_01_variables.py new file mode 100644 index 00000000..18a23b94 --- /dev/null +++ b/Python/makarov/chapter_01_variables.py @@ -0,0 +1,125 @@ +"""Макаров. + +Переменные в Питоне. +""" + +# ## Переменные в Питоне + +# ### Создание (объявление) переменных + +# можно создать переменную, присвоив ей числовое значение +num: int = 15 +print(num) + +# кроме того, переменной можно задать строковое (текстовое) значение +text: str = "Я программирую на Питоне" +print(str) + +# в Питоне можно присвоить разные значения сразу нескольким переменным +var_a, var_b, var_c = "Питон", "C++", "PHP" +print(var_a, var_b, var_c) + +# а также присвоить одно и то же значение нескольким переменным +x_var = y_var = z_var = "То же самое значение" +print(x_var, y_var, z_var) + +# каждый элемент списка можно "распаковать" в переменные +my_list: list[str] = ["помидоры", "огурцы", "картофель"] +a_var, b_var, c_var = my_list +print(a_var, b_var, c_var) + +# ### Автоматическое определение типа данных + +x_int = 256 # в этом случае переменной x присваивается тип int (целочисленное значение) +y_float = 0.25 # y становится float (десятичной дробью) +z_str = "Просто текст" # z становится str (строкой) + +# ### Как узнать тип переменной в Питоне + +# узнаем тип переменных из предыдущего примера +print(type(x_int), type(y_float), type(z_str)) + +# ### Присвоение и преобразование типа данных + +# Присвоение типа данных + +x_example_1 = str(25) # число 25 превратится в строку +y_example_2 = int(25) # число 25 останется целочисленным значением +z_example_3 = float(25) # число 25 превратится в десятичную дробь + +print(type(x_example_1), type(y_example_2), type(z_example_3)) + +# Изменение типа данных + +# преобразуем строку, похожую на целое число, в целое число +print(type(int("25"))) + +# или строку, похожую на дробь, в настоящую десятичную дробь +print(type(float("2.5"))) + +# преобразуем дробь в целочисленное значение +# обратите внимание, что округления в большую сторону не происходит +print(int(36.6)) +print(type(int(36.6))) + +# конечно, и целое число, и дробь можно преобразовать в строку +print(type(str(25))) +print(type(str(36.6))) + +# ### Именование переменных + +# Допустимые имена переменных + +value = "Просто переменная" +_variable = "Просто переменная" +variable_ = "Просто переменная" +my_variable = "Просто переменная" +My_variable_123 = "Просто переменная" + +# Имя переменной состоит из нескольких слов + +# + +# можно применить так называемый верблюжий регистр, camelCase +# все слова кроме первого начинаются с заглавной буквы и пишутся слитно +camelCaseVariable = "Верблюжий регистр" # noqa: N816 + +# нотацию Паскаль, PascalCase (то же самое, только теперь все слова пишутся с заглавной) +PascalCaseVariable = "Нотация Паскаль" + +# змеиный стиль, snake_case (с нижними подчеркиваниями) +snake_case_variable = "Змеиная нотация" +# - + +# Недопустимые названия переменной + +# + +# my-variable = 'Так делать нельзя' +# 123variable = 'Так делать нельзя' +# my variable = 'Так делать нельзя' +# - + +# ### Ответы на вопросы + +# **Вопрос**. Как можно преобразовать список чисел таким образом, чтобы каждый элемент списка превратился в отдельную строку? + +# возьмем простой список +list_ = [1, 2, 3] + +# использовать только функцию str() нельзя +str(list_) + +# + +# вариант 1: объявить новый список и в цикле for помещать туда строковые значения +list_str = [] + +for num in list_: + list_str.append(str(num)) + +list_str +# - + +# вариант 2: использовать list comprehension +str_list = [str(num) for num in list_] + +# вариант 3: функции map() и list() +list(map(str, list_)) diff --git a/Python/makarov/chapter_02_data_types.ipynb b/Python/makarov/chapter_02_data_types.ipynb new file mode 100644 index 00000000..3ef110dd --- /dev/null +++ b/Python/makarov/chapter_02_data_types.ipynb @@ -0,0 +1,806 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров. Типы данных в Питоне.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1GRQZRRMUhNV" + }, + "source": [ + "## Типы данных в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xVFt9DcGSfUI" + }, + "source": [ + "### Работа с числами" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ltvcAbzLSfFp" + }, + "outputs": [], + "source": [ + "a_1 = 25 # целое число (int)\n", + "b_1 = 2.5 # число с плавающей точкой (float)\n", + "c_1 = 3 + 25j # комплексное число (complex)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kpljlPxmUdR5", + "outputId": "ba97e614-4316-4762-c3c2-b38613b75234" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2000.0\n", + "\n" + ] + } + ], + "source": [ + "# экспоненциальная запись, 2 умножить на 10 в степени 3\n", + "d_1 = 2e3\n", + "print(d_1) # 2000.0\n", + "print(type(d_1)) # " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0ybsQNkWWgLB" + }, + "source": [ + "Арифметические операции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rdedKxrFWiO8", + "outputId": "57c5ebb5-5847-48b4-b2cd-56697a8def77" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 2 4 2.0 8\n" + ] + } + ], + "source": [ + "# сложение, вычитание, умножение, деление, возведение в степень\n", + "print(2 + 2, 4 - 2, 2 * 2, 4 / 2, 2**3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5fqcD1AGagad", + "outputId": "9cf95ea4-9d1b-419e-d4d1-00753f81a1ad" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "1\n" + ] + } + ], + "source": [ + "# новая для нас операция: разделим 7 на 2, и найдем целую часть и остаток\n", + "\n", + "# целая часть\n", + "print(7 // 2)\n", + "\n", + "# остаток от деления\n", + "print(7 % 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kl1h7PY4u518" + }, + "source": [ + "Операторы сравнения" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wsMv0ssQuyVQ", + "outputId": "95ff82a4-754a-4d34-cfaa-e20724ab5e4b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True False True False\n" + ] + } + ], + "source": [ + "# больше, меньше, больше или равно и меньше или равно\n", + "# print(4 > 2, 4 < 2, 4 >= 2, 4 <= 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RU5Qy8Cjvd8Y", + "outputId": "954465b0-d5f3-4f2c-cdb3-52a8a559ef31" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "True\n" + ] + } + ], + "source": [ + "# равенство\n", + "# print(2 == 4)\n", + "\n", + "# # и новый для нас оператор неравенства\n", + "# print(2 != 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UjKVC1j_xQAm" + }, + "source": [ + "Логические операции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vnne0pLlxSs8", + "outputId": "125788c3-1d6b-42d9-c375-c471179a1fca" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "# логическое И, обе операции должны быть истинны\n", + "# print(4 > 2 and 2 != 3)\n", + "\n", + "# # логическое ИЛИ, хотя бы одна из операций должна быть истинна\n", + "# print(4 < 2 or 2 == 2)\n", + "\n", + "# # логическое НЕ, перевод истинного значения в ложное и наоборот\n", + "# print(not 4 == 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v6CkmudbVfRm" + }, + "source": [ + "Перевод чисел в другую систему счисления" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VyooghwrT1Ak", + "outputId": "922ad58a-684a-4fe4-c8ed-2789bbefd3ae" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0b11001\n", + "25\n" + ] + } + ], + "source": [ + "# создадим число в десятичной системе\n", + "d_2 = 25\n", + "\n", + "# переведем в двоичную (binary)\n", + "bin_d = bin(d_2)\n", + "print(bin_d)\n", + "\n", + "# переведем обратно в десятичную\n", + "print(int(bin_d, 2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yflOiI0SWCYt", + "outputId": "80cb7b54-9e0c-4432-d7c0-170d88c8adc0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0o31\n", + "25\n" + ] + } + ], + "source": [ + "# создадим число в десятичной системе\n", + "d_3 = 25\n", + "\n", + "# переведем в восьмеричную (octal)\n", + "oct_d = oct(d_3)\n", + "print(oct_d)\n", + "\n", + "# переведем обратно в десятичную\n", + "print(int(oct_d, 8))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8h2UB8UgXh_U", + "outputId": "905b1600-620d-433b-8c9e-a57cfe38fafa" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0x19\n", + "25\n" + ] + } + ], + "source": [ + "# создадим число в десятичной системе\n", + "d_4 = 25\n", + "\n", + "# переведем в шестандцатеричную (hexadecimal)\n", + "hex_d = hex(d_4)\n", + "print(hex_d)\n", + "\n", + "# переведем обратно в десятичную\n", + "print(int(hex_d, 16))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nwD7zwn7cJ6w" + }, + "source": [ + "### Строковые данные" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "huuYgDFWYARX" + }, + "outputs": [], + "source": [ + "string_1 = \"это строка\"\n", + "string_2 = \"это тоже строка\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vHSc9dlqdDiB" + }, + "outputs": [], + "source": [ + "multi_string = \"\"\"Мы все учились понемногу\n", + "Чему-нибудь и как-нибудь,\n", + "Так воспитаньем, слава богу,\n", + "У нас немудрено блеснуть.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cVbPttEL1EV-" + }, + "source": [ + "Длина строки" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KcUZOvBg1GIv", + "outputId": "fb268850-07a5-4fef-b76e-df1450a939ba" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# воспользуемся функцией len()\n", + "len(multi_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EvqrYLrKaflu" + }, + "source": [ + "Объединение строк" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "oi3I2oQ7ahUA", + "outputId": "347f00e1-7876-4b1d-e61e-4b46d0601aae" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Программирование на Питоне'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим три строки\n", + "a_5, b_5, c_5 = \"Программирование\", \"на\", \"Питоне\"\n", + "\n", + "# соединим с помощью + и добавим пробелы ' '\n", + "a_5 + \" \" + b_5 + \" \" + c_5" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8yuChtkZzAsv" + }, + "source": [ + "Индекс символа в строке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6cG3PEPDdyVK", + "outputId": "eebc2370-326a-41c8-c894-eeb144dc22f1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "М\n", + ".\n" + ] + } + ], + "source": [ + "# выведем первый элемент строки multi_string\n", + "print(multi_string[0])\n", + "\n", + "# теперь выведем последний элемент\n", + "print(multi_string[-1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6G8gnYfOQf7K" + }, + "source": [ + "Срезы строк" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BjDd8-4nz_8j", + "outputId": "179c02b0-2569-4af8-b216-611278fa5969" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "все\n" + ] + } + ], + "source": [ + "# выберем элементы с четвертого по шестой\n", + "print(multi_string[3:6])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IOl7IswZQtsD", + "outputId": "29283cd4-f801-4be1-8dab-c0ad02a4f347" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Мы\n", + "все учились понемногу\n", + "Чему-нибудь и как-нибудь,\n", + "Так воспитаньем, слава богу,\n", + "У нас немудрено блеснуть.\n" + ] + } + ], + "source": [ + "# выведем все элементы вплоть до второго\n", + "print(multi_string[:2])\n", + "\n", + "# а также все элементы, начиная с четвертого\n", + "print(multi_string[3:])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Y2mpyCwPTd2w" + }, + "source": [ + "Циклы в строках" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VAwdI6KoSWOp", + "outputId": "bb6fd38c-ed8e-4fa6-f140-35fd84ef3a1f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "П\n", + "и\n", + "т\n", + "о\n", + "н\n" + ] + } + ], + "source": [ + "# выведем буквы в слове Питон\n", + "for char in \"Питон\":\n", + " print(char)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B-_cckdsT364" + }, + "source": [ + "Методы .strip() и .split()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nrgI904PTo4s", + "outputId": "aeba88e0-25df-4e4c-bdb9-0490e72d1368" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15 849 302\n", + "15 849 302\n" + ] + } + ], + "source": [ + "# применим метод .strip(), чтобы удалить *\n", + "print(\"***15 849 302*****\".strip(\"*\"))\n", + "\n", + "# если ничего не указать в качестве аргумента, то удаляются пробелы по краям строки\n", + "print(\" 15 849 302 \".strip())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YbyPzzWtW-I8", + "outputId": "3f2e9650-81e9-4623-db58-aa4669df4aad" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Мы', 'все', 'учились', 'понемногу', 'Чему-нибудь', 'и', 'как-нибудь,', 'Так', 'воспитаньем,', 'слава', 'богу,', 'У', 'нас', 'немудрено', 'блеснуть.']\n" + ] + } + ], + "source": [ + "# применим метод .split(), чтобы разделить строку на части\n", + "print(multi_string.split())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eVgqVyWAX8ks", + "outputId": "a702189a-0dfe-45a8-c836-069b6c9f5a47" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посчитаем количество слов в тексте (длину списка)\n", + "len(multi_string.split())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "da7PAlJSem_e" + }, + "source": [ + "Замена символа в строке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zUPjDpc9eogI", + "outputId": "48967de1-04fe-4767-d9c1-6b3c819a2cf8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "20.25\n", + "\n" + ] + } + ], + "source": [ + "# предположим данные содержатся вот в таком формате\n", + "data = \"20,25\"\n", + "\n", + "# теперь заменим ',' на '.'\n", + "data = data.replace(\",\", \".\")\n", + "\n", + "# и преобразуем в число\n", + "data_str = float(data)\n", + "print(data_str)\n", + "print(type(data_str))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kBxByInXf3xv" + }, + "source": [ + "### Логические значения" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jnMQ3TTYfMDS", + "outputId": "09c91c15-c4b8-4087-8348-2bd2d1658eb9" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим переменную и запишем в нее логическое значение True\n", + "# (обязательно с большой буквы)\n", + "my_var = False\n", + "type(my_var)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2faKLkNYfQWh", + "outputId": "499e99c5-671d-45cc-ce3e-c9a9a91537a6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Значение переменной ложно\n" + ] + } + ], + "source": [ + "# напишем небольшую программу, которая будет показывать,\n", + "# какое значение содержится в переменной var\n", + "\n", + "if my_var is True:\n", + " print(\"Значение переменной истинно\")\n", + "else:\n", + " print(\"Значение переменной ложно\")" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_02_data_types.py b/Python/makarov/chapter_02_data_types.py new file mode 100644 index 00000000..ee8a2ad8 --- /dev/null +++ b/Python/makarov/chapter_02_data_types.py @@ -0,0 +1,196 @@ +"""Макаров. + +Типы данных в Питоне. +""" + +# ## Типы данных в Питоне + +# ### Работа с числами + +a_1 = 25 # целое число (int) +b_1 = 2.5 # число с плавающей точкой (float) +c_1 = 3 + 25j # комплексное число (complex) + +# экспоненциальная запись, 2 умножить на 10 в степени 3 +d_1 = 2e3 +print(d_1) # 2000.0 +print(type(d_1)) # + +# Арифметические операции + +# сложение, вычитание, умножение, деление, возведение в степень +print(2 + 2, 4 - 2, 2 * 2, 4 / 2, 2**3) + +# + +# новая для нас операция: разделим 7 на 2, и найдем целую часть и остаток + +# целая часть +print(7 // 2) + +# остаток от деления +print(7 % 2) +# - + +# Операторы сравнения + +# + +# больше, меньше, больше или равно и меньше или равно +# print(4 > 2, 4 < 2, 4 >= 2, 4 <= 2) + +# + +# равенство +# print(2 == 4) + +# # и новый для нас оператор неравенства +# print(2 != 4) +# - + +# Логические операции + +# + +# логическое И, обе операции должны быть истинны +# print(4 > 2 and 2 != 3) + +# # логическое ИЛИ, хотя бы одна из операций должна быть истинна +# print(4 < 2 or 2 == 2) + +# # логическое НЕ, перевод истинного значения в ложное и наоборот +# print(not 4 == 4) +# - + +# Перевод чисел в другую систему счисления + +# + +# создадим число в десятичной системе +d_2 = 25 + +# переведем в двоичную (binary) +bin_d = bin(d_2) +print(bin_d) + +# переведем обратно в десятичную +print(int(bin_d, 2)) + +# + +# создадим число в десятичной системе +d_3 = 25 + +# переведем в восьмеричную (octal) +oct_d = oct(d_3) +print(oct_d) + +# переведем обратно в десятичную +print(int(oct_d, 8)) + +# + +# создадим число в десятичной системе +d_4 = 25 + +# переведем в шестандцатеричную (hexadecimal) +hex_d = hex(d_4) +print(hex_d) + +# переведем обратно в десятичную +print(int(hex_d, 16)) +# - + +# ### Строковые данные + +string_1 = "это строка" +string_2 = "это тоже строка" + +multi_string = """Мы все учились понемногу +Чему-нибудь и как-нибудь, +Так воспитаньем, слава богу, +У нас немудрено блеснуть.""" + +# Длина строки + +# воспользуемся функцией len() +len(multi_string) + +# Объединение строк + +# + +# создадим три строки +a_5, b_5, c_5 = "Программирование", "на", "Питоне" + +# соединим с помощью + и добавим пробелы ' ' +a_5 + " " + b_5 + " " + c_5 +# - + +# Индекс символа в строке + +# + +# выведем первый элемент строки multi_string +print(multi_string[0]) + +# теперь выведем последний элемент +print(multi_string[-1]) +# - + +# Срезы строк + +# выберем элементы с четвертого по шестой +print(multi_string[3:6]) + +# + +# выведем все элементы вплоть до второго +print(multi_string[:2]) + +# а также все элементы, начиная с четвертого +print(multi_string[3:]) +# - + +# Циклы в строках + +# выведем буквы в слове Питон +for char in "Питон": + print(char) + +# Методы .strip() и .split() + +# + +# применим метод .strip(), чтобы удалить * +print("***15 849 302*****".strip("*")) + +# если ничего не указать в качестве аргумента, то удаляются пробелы по краям строки +print(" 15 849 302 ".strip()) +# - + +# применим метод .split(), чтобы разделить строку на части +print(multi_string.split()) + +# посчитаем количество слов в тексте (длину списка) +len(multi_string.split()) + +# Замена символа в строке + +# + +# предположим данные содержатся вот в таком формате +data = "20,25" + +# теперь заменим ',' на '.' +data = data.replace(",", ".") + +# и преобразуем в число +data_str = float(data) +print(data_str) +print(type(data_str)) +# - + +# ### Логические значения + +# создадим переменную и запишем в нее логическое значение True +# (обязательно с большой буквы) +my_var = False +type(my_var) + +# + +# напишем небольшую программу, которая будет показывать, +# какое значение содержится в переменной var + +if my_var is True: + print("Значение переменной истинно") +else: + print("Значение переменной ложно") diff --git a/Python/makarov/chapter_03_if_loops.ipynb b/Python/makarov/chapter_03_if_loops.ipynb new file mode 100644 index 00000000..ed653002 --- /dev/null +++ b/Python/makarov/chapter_03_if_loops.ipynb @@ -0,0 +1,1498 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров. Условия и циклы.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D4hEPtRhhMqh" + }, + "source": [ + "## Условия и циклы. Продолжение\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vawFsPhhRgI" + }, + "source": [ + "### Еще раз про условия с if\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rWIA_xr5Cq6K" + }, + "source": [ + "Множественные условия (multi-way decisions)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KCMYjV0Cg3Zx", + "outputId": "2d3c5d62-2e77-4b09-c5fc-8a1a7471198a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Medium\n" + ] + } + ], + "source": [ + "# напишем программу, которая разобьет все числа на малые, средние и большие\n", + "# импортируем библиотеку numpy\n", + "import numpy as np\n", + "\n", + "x_1 = 42 # зададим число\n", + "\n", + "# и пропишем условия (не забывайте про двоеточие и отступ)\n", + "if x_1 < 10:\n", + " print(\"Small\")\n", + "elif x_1 < 100:\n", + " print(\"Medium\")\n", + "else:\n", + " print(\"Large\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Jik8GB36UWgh", + "outputId": "abb6e9a2-7d04-4a63-d59b-1312b36144e3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Medium\n" + ] + } + ], + "source": [ + "# запросим число у пользователя\n", + "x_str = input(\"Введите число: \")\n", + "\n", + "# преобразуем в тип int\n", + "x_2 = int(x_str)\n", + "\n", + "# и наконец классифицируем\n", + "if x_2 < 10:\n", + " print(\"Small\")\n", + "elif x_2 < 100:\n", + " print(\"Medium\")\n", + "else:\n", + " print(\"Large\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7i-2ADtRCv-Z" + }, + "source": [ + "Вложенные условия (nested decisions)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "G45V7GOfUoKw", + "outputId": "51061495-88b6-499c-b263-95edd749b950" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Medium\n" + ] + } + ], + "source": [ + "# запрашиваем число\n", + "y_str = input(\"Введите число: \")\n", + "\n", + "# проверяем первое условие (не пустая ли строка), если оно выполняется\n", + "if len(y_str) != 0:\n", + "\n", + " # преобразуем в тип int\n", + " y_int = int(y_str)\n", + "\n", + " # и классифицируем\n", + " if y_int < 10:\n", + " print(\"Small\")\n", + " elif y_int < 100:\n", + " print(\"Medium\")\n", + " else:\n", + " print(\"Large\")\n", + "\n", + "# в противном, говорим, что ввод пустой\n", + "else:\n", + " print(\"Ввод пустой\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "M72PQIj48Igc" + }, + "source": [ + "Несколько условий в одном выражении с операторами and или or\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ktlZYzKDE8Vr", + "outputId": "5b8ff016-c384-4023-a6c6-70202508f021" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Medium\n" + ] + } + ], + "source": [ + "# пример с and (логическим И)\n", + "z_int = 42\n", + "\n", + "# если z больше 10 и одновременно меньше 100\n", + "if 10 < z_int < 100:\n", + "\n", + " # у нас среднее число\n", + " print(\"Medium\")\n", + "\n", + "# в противном случае оно либо маленькое либо большое\n", + "else:\n", + " print(\"Small or Large\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BfUvmSq5E3m9", + "outputId": "1113ad79-e15d-4187-b635-3e091b070a59" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Small or Large\n" + ] + } + ], + "source": [ + "# пример с or (логическим ИЛИ)\n", + "z_int = 2\n", + "\n", + "# если z меньше 10 или больше 100\n", + "if z_int < 10 or z_int > 100:\n", + "\n", + " # оно либо маленькое либо большое\n", + " print(\"Small or Large\")\n", + "\n", + "# в противном случае оно среднее\n", + "else:\n", + " print(\"Medium\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EGZZNNr-9Fn-" + }, + "source": [ + "Проверка вхождения элемента в объект с in / not in\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LOpo6gje9Q-C", + "outputId": "a5c228ea-5f37-4907-dcdf-0eb6ee1140f3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Слово найдено\n" + ] + } + ], + "source": [ + "# можно проверить вхождение слова в строку\n", + "sentence = \"To be, or not to be, that is the question\"\n", + "word = \"question\"\n", + "\n", + "if word in sentence:\n", + " print(\"Слово найдено\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mHq-uEI4K2AI", + "outputId": "196d6df3-3d03-4edb-83af-7576dc68e844" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Такого числа в списке нет\n" + ] + } + ], + "source": [ + "# или отсутствие элемента в списке\n", + "number_list = [2, 3, 4, 6, 7]\n", + "number = 5\n", + "\n", + "if number not in number_list:\n", + " print(\"Такого числа в списке нет\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gWjobfPGNzwg" + }, + "outputs": [], + "source": [ + "# кроме того, можно проверить вхождение ключа и значения в словарь\n", + "\n", + "# возьмем очень простой словарь\n", + "d_dict = {\"apple\": 3, \"tomato\": 6, \"carrot\": 2}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Z92L7WSTPF-P", + "outputId": "5306ceb6-18a2-4af0-aeb8-317444a088de" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Нашлись\n" + ] + } + ], + "source": [ + "# вначале поищем яблоки среди ключей словаря\n", + "if \"apple\" in d_dict:\n", + " print(\"Нашлись\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YhUXxjsUPTl2", + "outputId": "4c62a3e8-6b70-48c5-d79d-9df71d26809c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Есть\n" + ] + } + ], + "source": [ + "# а затем посмотрим, нет ли числа 6 среди его значений\n", + "# с помощью метода .values()\n", + "if 6 in d_dict.values():\n", + " print(\"Есть\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fsCZohlkYH7k" + }, + "source": [ + "### Циклы в Питоне\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z7-hyiTDYOv8" + }, + "source": [ + "#### Цикл for\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SOp17Yl-fWhG" + }, + "source": [ + "##### Основные операции\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "A3GWvb12a6Ra", + "outputId": "c07c70f8-82e6-41b8-db48-1b5980e8ef35" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "# поочередно выведем элементы списка\n", + "number_list = [1, 2, 3]\n", + "\n", + "# не забывая про двоеточие и отступ\n", + "for number in number_list:\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nIrD5ArAb8ih" + }, + "outputs": [], + "source": [ + "# создадим словарь, значениями которого будут списки из двух элементов\n", + "fruits_dict = {\"apple\": [3, \"kg\"], \"tomato\": [6, \"pcs\"], \"carrot\": [2, \"kg\"]}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QD9h9pplgDVI", + "outputId": "f20d59a5-5ab5-4af9-c7ae-ebd1b3549141" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apple [3, 'kg']\n", + "tomato [6, 'pcs']\n", + "carrot [2, 'kg']\n" + ] + } + ], + "source": [ + "# затем создадим две переменные-контейнера и применим метод .items()\n", + "for key, value in fruits_dict.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QV1uyoe0gFLh", + "outputId": "126701f4-c8c6-4ba5-bbcd-778c837dc5fd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "6\n", + "2\n" + ] + } + ], + "source": [ + "# возьмем только одну переменную и применим метод .values()\n", + "for value in fruits_dict.values():\n", + " # значение представляет собой список, выведем его первый элемент с индексом [0]\n", + " print(value[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4L5ktimCcbbJ", + "outputId": "7c17e543-7ff5-4eaf-d8a4-1cf3ca8372b2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "# создадим массив и поместим в переменную number_array\n", + "number_array = np.array([1, 2, 3])\n", + "\n", + "# пройдемся по нему с помощью цикла for\n", + "for number in number_array:\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8WzO673x-n7b" + }, + "outputs": [], + "source": [ + "# предположим, что у нас есть следующая база данных клиентов\n", + "clients_2: dict[int, dict[str, int | str]] = {\n", + " 1: {\"name\": \"Анна\", \"age\": 24, \"sex\": \"male\", \"revenue\": 12000},\n", + " 2: {\"name\": \"Илья\", \"age\": 18, \"sex\": \"female\", \"revenue\": 8000},\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zYoothSRM7T9", + "outputId": "9485789d-3e10-4ffe-a990-63eb8249826d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "client ID: 1\n", + "name: Анна\n", + "age: 24\n", + "sex: male\n", + "revenue: 12000\n", + "\n", + "client ID: 2\n", + "name: Илья\n", + "age: 18\n", + "sex: female\n", + "revenue: 8000\n", + "\n" + ] + } + ], + "source": [ + "# в первом цикле for поместим id и информацию о клиентах в переменные id и info\n", + "for client_id, info in clients_2.items():\n", + "\n", + " # выведем id клиента\n", + " print(\"client ID: \" + str(client_id))\n", + "\n", + " # во втором цикле возьмем информацию об этом клиенте (это тоже словарь)\n", + " for client_key, client_value in info.items():\n", + "\n", + " # и выведем каждый ключ (название поля) и значение (саму информацию)\n", + " print(client_key + \": \" + str(client_value))\n", + "\n", + " # добавим пустую строку после того, как выведем информацию об одном клиенте\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Q3CB7PzqYZ_L" + }, + "source": [ + "##### Функция range()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HtJWgzs2OEG_", + "outputId": "c82a607f-6477-4361-d7d7-a607a4ba6bdf" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "# создадим последовательность от 0 до 4\n", + "for index in range(5):\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UI24as2nYzR1", + "outputId": "07ac7689-d18c-4267-8b47-986c10086d33" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n", + "4\n", + "5\n" + ] + } + ], + "source": [ + "# от 1 до 5\n", + "for index in range(1, 6):\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qN0SAaPXdILc", + "outputId": "b72a44c1-3c52-4f67-b635-4619ffae5366" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "2\n", + "4\n" + ] + } + ], + "source": [ + "# и от 0 до 5 с шагом 2 (то есть будем выводить числа через одно)\n", + "for index in range(0, 6, 2):\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8dUjujIG3cph", + "outputId": "e1ea261c-c714-4b25-a1c9-1b4a119fa550" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Январь 47\n", + "Февраль 75\n", + "Март 79\n", + "Апрель 94\n", + "Май 123\n", + "Июнь 209\n", + "Июль 233\n", + "Август 214\n", + "Сентябрь 197\n", + "Октябрь 130\n", + "Ноябрь 87\n", + "Декабрь 55\n" + ] + } + ], + "source": [ + "# возьмем месяцы года\n", + "# \"месяц \" добавлен для того, чтобы избежать дублирования кода\n", + "# и как следствие ошибок pylint\n", + "months = [\n", + " \"месяц Январь\",\n", + " \"месяц Февраль\",\n", + " \"месяц Март\",\n", + " \"месяц Апрель\",\n", + " \"месяц Май\",\n", + " \"месяц Июнь\",\n", + " \"месяц Июль\",\n", + " \"месяц Август\",\n", + " \"месяц Сентябрь\",\n", + " \"месяц Октябрь\",\n", + " \"месяц Ноябрь\",\n", + " \"месяц Декабрь\",\n", + "]\n", + "\n", + "# и продажи мороженого в тыс. рублей в каждый из месяцев\n", + "sales = [47, 75, 79, 94, 123, 209, 233, 214, 197, 130, 87, 55]\n", + "\n", + "length = len(months)\n", + "# задав последовательность через range(len()),\n", + "for index in range(length):\n", + " # мы можем вывести каждый из элементов обоих списков в одном цикле\n", + " print(months[index], sales[index])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0CFgV73qgUSd" + }, + "source": [ + "Последовательность в обратном порядке\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Jw0cFT6tgiPX" + }, + "source": [ + "**Способ 1**. Функция reversed()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "l51JXd5Egk8o", + "outputId": "ea9e0431-0789-4716-a272-479a95293e7d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "# создадим список\n", + "my_list = [0, 1, 2, 3, 4]\n", + "\n", + "# передадим его функции reversed() и\n", + "# выведем каждый из элементов списка с помощью цикла for\n", + "for index in reversed(my_list):\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Dj4vHmXnhGZ1", + "outputId": "56b9b18b-a7fe-4242-a2e4-232aa351e880" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "for index in reversed(range(5)):\n", + " print(index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p_p9pohzgnXz" + }, + "source": [ + "**Способ 2**. Указать $-1$ в качестве параметра шага\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cgL29MGlgzaa", + "outputId": "44596ce6-aef4-4625-88e3-8d5249d1f07a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n" + ] + } + ], + "source": [ + "# первым параметром укажем конечный элемент списка,\n", + "# а вторым - начальный\n", + "for index in range(4, 0, -1):\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IZ6veVNThxIV", + "outputId": "7ecce7ec-e249-4f43-8960-edd4cb746fe6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "# чтобы вывести 0, вторым параметром нужно указать -1\n", + "for index in range(4, -1, -1):\n", + " print(index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKTDUkY3gz7-" + }, + "source": [ + "**Способ 3**. Функция sorted()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "l_FnBDBEg7dG", + "outputId": "cd1f0812-2641-450a-ef31-8282076a4c3c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "# создадим последовательность от 0 до 4\n", + "my_range = range(5)\n", + "\n", + "# отсортируем ее по убыванию\n", + "sorted_values = sorted(my_range, reverse=True)\n", + "\n", + "# выведем элементы отсортированной последовательности\n", + "for index in sorted_values:\n", + " print(index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c4X4Nw2Yf2IH" + }, + "source": [ + "##### Функция enumerate()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5a3gqF7-foJY", + "outputId": "358e0f57-cffa-434d-ef50-e811b2c822a2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 Понедельник\n", + "1 Вторник\n", + "2 Среда\n", + "3 Четверг\n", + "4 Пятница\n", + "5 Суббота\n", + "6 Воскресенье\n" + ] + } + ], + "source": [ + "# пусть дан список с днями недели\n", + "days = [\n", + " \"Понедельник\",\n", + " \"Вторник\",\n", + " \"Среда\",\n", + " \"Четверг\",\n", + " \"Пятница\",\n", + " \"Суббота\",\n", + " \"Воскресенье\",\n", + "]\n", + "\n", + "# выведем индекс (index) и сами элементы списка (day)\n", + "for index, day in enumerate(days):\n", + " print(index, day)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qrn0qMjWlgg4", + "outputId": "7719b163-557d-4d68-9367-53e13d8cf714" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 Понедельник\n", + "2 Вторник\n", + "3 Среда\n", + "4 Четверг\n", + "5 Пятница\n", + "6 Суббота\n", + "7 Воскресенье\n" + ] + } + ], + "source": [ + "# так же выведем индекс и элементы списка, но начнем с 1\n", + "for index, day in enumerate(days, 1):\n", + " print(index, day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Nw5Km-XapZni" + }, + "source": [ + "#### Цикл while\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7dWKX2p8pZCl", + "outputId": "b20690f5-36ff-464c-e3b2-ab185409b74a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текущее значение счетчика: 0\n", + "Новое значение счетчика: 1\n", + "\n", + "Текущее значение счетчика: 1\n", + "Новое значение счетчика: 2\n", + "\n", + "Текущее значение счетчика: 2\n", + "Новое значение счетчика: 3\n", + "\n" + ] + } + ], + "source": [ + "# зададим начальное значение счетчика\n", + "index = 0\n", + "\n", + "# пока счетчик меньше трех\n", + "while index < 3:\n", + "\n", + " # в каждом цикле будем выводить его текущее значение\n", + " print(\"Текущее значение счетчика: \" + str(index))\n", + "\n", + " # внутри цикла не забудем \"нарастить\" счетчик\n", + " index = index + 1\n", + "\n", + " # и выведем новое значение\n", + " print(\"Новое значение счетчика: \" + str(index))\n", + "\n", + " # добавим пустую строку\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AsLYv9o-mpy2", + "outputId": "ca5a8611-81a4-45e1-f242-c1e6243b2a50" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n" + ] + } + ], + "source": [ + "# тот же код можно упростить\n", + "index = 0\n", + "\n", + "while index < 3:\n", + " print(index)\n", + " # в частности, оператор += сразу увеличивает и присваивает новое значение\n", + " index += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "N6Djl9Shdkxh" + }, + "source": [ + "#### Break, continue\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZCW-rwAFggXA" + }, + "source": [ + "Оператор break\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RBHU-9AMtfWx", + "outputId": "8fb98266-36f9-417b-df63-956053999ae7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 {'name': 'Анна', 'age': 24, 'sex': 'male', 'revenue': 12000}\n" + ] + } + ], + "source": [ + "# вновь возьмем словарь clients\n", + "clients_1: dict[int, dict[str, int | str]] = {\n", + " 1: {\"name\": \"Анна\", \"age\": 24, \"sex\": \"male\", \"revenue\": 12000},\n", + " 2: {\"name\": \"Илья\", \"age\": 18, \"sex\": \"female\", \"revenue\": 8000},\n", + "}\n", + "\n", + "# в цикле пройдемся по ключам и значениям словаря\n", + "for client_id, info in clients_1.items():\n", + "\n", + " # и выведем их\n", + " print(client_id, info)\n", + "\n", + " # однако уже после первого исполнения цикла, прервем его\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SFqkq_3lfqrn", + "outputId": "8a818f11-8359-4c0f-ad96-41b15f5f168c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6\n", + "5\n", + "4\n" + ] + } + ], + "source": [ + "# начальное значение счетчика\n", + "x_int = 6\n", + "\n", + "# будем исполнять цикл пока x не равен нулю\n", + "while x_int != 0:\n", + "\n", + " # выведем текущее значение счетчика\n", + " print(x_int)\n", + "\n", + " # и уменьшим (!) его на 1\n", + " x_int -= 1\n", + "\n", + " # если значение счетчика станет равным 3, прервем цикл\n", + " if x_int == 3:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L3Mf_VrMjX_9" + }, + "source": [ + "Оператор continue\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fQOiiMyTjdic", + "outputId": "3eaceef2-657a-4bfc-bcdf-d0585ebbf851" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n", + "4\n", + "6\n", + "8\n", + "10\n" + ] + } + ], + "source": [ + "# выведем все четные числа в диапазоне от 1 до 10 включительно.\n", + "\n", + "# с помощью функции range() создадим последовательность от 1 до 10\n", + "for index in range(1, 11):\n", + "\n", + " # если остаток от деления на два не равен нулю (то есть число нечетное)\n", + " if index % 2 != 0:\n", + "\n", + " # идем к следующему числу последовательности\n", + " continue\n", + "\n", + " # в противном случае выводим число\n", + " print(index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9mv5pBDHX3zT" + }, + "source": [ + "#### Форматирование строк через f-строки и метод .format()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "T3-9SwA5oyBw", + "outputId": "975ffcc1-101d-4849-bbab-f1819d3d2c3c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Понедельник'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# снова возьмем список с днями недели\n", + "days = [\n", + " \"Понедельник\",\n", + " \"Вторник\",\n", + " \"Среда\",\n", + " \"Четверг\",\n", + " \"Пятница\",\n", + " \"Суббота\",\n", + " \"Воскресенье\",\n", + "]\n", + "\n", + "# и для простоты поместим слово \"Понедельник\" в переменную Monday\n", + "Monday = days[0]\n", + "Monday" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sDm0FLLzYyqM", + "outputId": "0af15dd1-b565-485a-9010-8813efd8dfec" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Понедельник - день тяжелый\n" + ] + } + ], + "source": [ + "# теперь напишем фразу \"Понедельник - день тяжелый\" следующим образом\n", + "print(f\"{Monday} - день тяжелый\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "p85bCPM7Z2yz", + "outputId": "1f7f7c62-a86c-4a16-c234-e4d09d98eb98" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Понедельник - день тяжелый\n" + ] + } + ], + "source": [ + "# то же самое можно вывести с помощью метода .format()\n", + "print(\"{} - день тяжелый\".format(Monday)) # pylint: disable=C0209" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EYzsssCDwI5u" + }, + "source": [ + "### Ответы на вопросы к занятию\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O8weTrT8w7qh" + }, + "source": [ + "**Вопрос**. Можно ли использовать цикл while с функцией range()?\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SZXmOJa7hI6K", + "outputId": "67589a76-8a23-419e-d30a-d2de8a2e393d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Значение счетчика 1\n", + "Значение счетчика 2\n", + "Значение счетчика 3\n", + "Значение счетчика 4\n", + "Значение счетчика 5\n", + "Значение счетчика 6\n", + "Значение счетчика 7\n", + "Значение счетчика 8\n", + "Значение счетчика 9\n", + "Значение счетчика 10\n" + ] + } + ], + "source": [ + "# с функцией range() можно использовать цикл while, но такое решение не оптимально\n", + "# приведем пример с while\n", + "\n", + "index = 1 # создадим счетчик\n", + "\n", + "while index in range(1, 11): # пока счетчик в диапазоне от 1 до 10\n", + " print(\"Значение счетчика \", index) # выведем его значение и\n", + " index += 1 # увеличим счетчик на 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "T-7jFKw9wp0o", + "outputId": "6696e461-f0c5-451e-bcc3-8cdbd7f50f8e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Значение счетчика 1\n", + "Значение счетчика 2\n", + "Значение счетчика 3\n", + "Значение счетчика 4\n", + "Значение счетчика 5\n", + "Значение счетчика 6\n", + "Значение счетчика 7\n", + "Значение счетчика 8\n", + "Значение счетчика 9\n", + "Значение счетчика 10\n" + ] + } + ], + "source": [ + "# более оптимальный код\n", + "for index in range(1, 11):\n", + " print(\"Значение счетчика \", index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yrouXtHk2BiB" + }, + "source": [ + "**Вопрос**. Можно ли обойтись без оператора continue в приведенном на занятии примере?\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "g-0ophfr2QXH", + "outputId": "f220b20b-6480-4bb5-978a-99c3c388821e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n", + "4\n", + "6\n", + "8\n", + "10\n" + ] + } + ], + "source": [ + "for index in range(1, 11):\n", + " # если число четное, выведем его\n", + " if index % 2 == 0:\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "61Ih4ELB2gaC" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_03_if_loops.py b/Python/makarov/chapter_03_if_loops.py new file mode 100644 index 00000000..e8261ca8 --- /dev/null +++ b/Python/makarov/chapter_03_if_loops.py @@ -0,0 +1,458 @@ +# %% +"""Макаров. + +Условия и циклы. +""" + +# ## Условия и циклы. Продолжение +# + +# ### Еще раз про условия с if +# + +# Множественные условия (multi-way decisions) +# + +# %% +# напишем программу, которая разобьет все числа на малые, средние и большие +# импортируем библиотеку numpy +import numpy as np + +x_1 = 42 # зададим число + +# и пропишем условия (не забывайте про двоеточие и отступ) +if x_1 < 10: + print("Small") +elif x_1 < 100: + print("Medium") +else: + print("Large") + +# %% +# запросим число у пользователя +x_str = input("Введите число: ") + +# преобразуем в тип int +x_2 = int(x_str) + +# и наконец классифицируем +if x_2 < 10: + print("Small") +elif x_2 < 100: + print("Medium") +else: + print("Large") + +# Вложенные условия (nested decisions) +# + +# %% +# запрашиваем число +y_str = input("Введите число: ") + +# проверяем первое условие (не пустая ли строка), если оно выполняется +if len(y_str) != 0: + + # преобразуем в тип int + y_int = int(y_str) + + # и классифицируем + if y_int < 10: + print("Small") + elif y_int < 100: + print("Medium") + else: + print("Large") + +# в противном, говорим, что ввод пустой +else: + print("Ввод пустой") + +# Несколько условий в одном выражении с операторами and или or +# + +# %% +# пример с and (логическим И) +z_int = 42 + +# если z больше 10 и одновременно меньше 100 +if 10 < z_int < 100: + + # у нас среднее число + print("Medium") + +# в противном случае оно либо маленькое либо большое +else: + print("Small or Large") + +# %% +# пример с or (логическим ИЛИ) +z_int = 2 + +# если z меньше 10 или больше 100 +if z_int < 10 or z_int > 100: + + # оно либо маленькое либо большое + print("Small or Large") + +# в противном случае оно среднее +else: + print("Medium") + +# Проверка вхождения элемента в объект с in / not in +# + +# %% +# можно проверить вхождение слова в строку +sentence = "To be, or not to be, that is the question" +word = "question" + +if word in sentence: + print("Слово найдено") + +# %% +# или отсутствие элемента в списке +number_list = [2, 3, 4, 6, 7] +number = 5 + +if number not in number_list: + print("Такого числа в списке нет") + +# кроме того, можно проверить вхождение ключа и значения в словарь + +# возьмем очень простой словарь +d_dict = {"apple": 3, "tomato": 6, "carrot": 2} + +# %% +# вначале поищем яблоки среди ключей словаря +if "apple" in d_dict: + print("Нашлись") + +# %% +# а затем посмотрим, нет ли числа 6 среди его значений +# с помощью метода .values() +if 6 in d_dict.values(): + print("Есть") + +# ### Циклы в Питоне +# + +# #### Цикл for +# + +# ##### Основные операции +# + +# %% +# поочередно выведем элементы списка +number_list = [1, 2, 3] + +# не забывая про двоеточие и отступ +for number in number_list: + print(number) + +# %% +# создадим словарь, значениями которого будут списки из двух элементов +fruits_dict = {"apple": [3, "kg"], "tomato": [6, "pcs"], "carrot": [2, "kg"]} + +# %% +# затем создадим две переменные-контейнера и применим метод .items() +for key, value in fruits_dict.items(): + print(key, value) + +# %% +# возьмем только одну переменную и применим метод .values() +for value in fruits_dict.values(): + # значение представляет собой список, выведем его первый элемент с индексом [0] + print(value[0]) + +# %% +# создадим массив и поместим в переменную number_array +number_array = np.array([1, 2, 3]) + +# пройдемся по нему с помощью цикла for +for number in number_array: + print(number) + +# %% +# предположим, что у нас есть следующая база данных клиентов +clients_2: dict[int, dict[str, int | str]] = { + 1: {"name": "Анна", "age": 24, "sex": "male", "revenue": 12000}, + 2: {"name": "Илья", "age": 18, "sex": "female", "revenue": 8000}, +} + +# %% +# в первом цикле for поместим id и информацию о клиентах в переменные id и info +for client_id, info in clients_2.items(): + + # выведем id клиента + print("client ID: " + str(client_id)) + + # во втором цикле возьмем информацию об этом клиенте (это тоже словарь) + for client_key, client_value in info.items(): + + # и выведем каждый ключ (название поля) и значение (саму информацию) + print(client_key + ": " + str(client_value)) + + # добавим пустую строку после того, как выведем информацию об одном клиенте + print() + +# ##### Функция range() +# + +# %% +# создадим последовательность от 0 до 4 +for index in range(5): + print(index) + +# %% +# от 1 до 5 +for index in range(1, 6): + print(index) + +# %% +# и от 0 до 5 с шагом 2 (то есть будем выводить числа через одно) +for index in range(0, 6, 2): + print(index) + +# %% +# возьмем месяцы года +# "месяц " добавлен для того, чтобы избежать дублирования кода +# и как следствие ошибок pylint +months = [ + "месяц Январь", + "месяц Февраль", + "месяц Март", + "месяц Апрель", + "месяц Май", + "месяц Июнь", + "месяц Июль", + "месяц Август", + "месяц Сентябрь", + "месяц Октябрь", + "месяц Ноябрь", + "месяц Декабрь", +] + +# и продажи мороженого в тыс. рублей в каждый из месяцев +sales = [47, 75, 79, 94, 123, 209, 233, 214, 197, 130, 87, 55] + +length = len(months) +# задав последовательность через range(len()), +for index in range(length): + # мы можем вывести каждый из элементов обоих списков в одном цикле + print(months[index], sales[index]) + +# Последовательность в обратном порядке +# + +# **Способ 1**. Функция reversed() +# + +# %% +# создадим список +my_list = [0, 1, 2, 3, 4] + +# передадим его функции reversed() и +# выведем каждый из элементов списка с помощью цикла for +for index in reversed(my_list): + print(index) + +# %% +for index in reversed(range(5)): + print(index) + +# **Способ 2**. Указать $-1$ в качестве параметра шага +# + +# %% +# первым параметром укажем конечный элемент списка, +# а вторым - начальный +for index in range(4, 0, -1): + print(index) + +# %% +# чтобы вывести 0, вторым параметром нужно указать -1 +for index in range(4, -1, -1): + print(index) + +# **Способ 3**. Функция sorted() +# + +# %% +# создадим последовательность от 0 до 4 +my_range = range(5) + +# отсортируем ее по убыванию +sorted_values = sorted(my_range, reverse=True) + +# выведем элементы отсортированной последовательности +for index in sorted_values: + print(index) + +# ##### Функция enumerate() +# + +# %% +# пусть дан список с днями недели +days = [ + "Понедельник", + "Вторник", + "Среда", + "Четверг", + "Пятница", + "Суббота", + "Воскресенье", +] + +# выведем индекс (index) и сами элементы списка (day) +for index, day in enumerate(days): + print(index, day) + +# %% +# так же выведем индекс и элементы списка, но начнем с 1 +for index, day in enumerate(days, 1): + print(index, day) + +# #### Цикл while +# + +# %% +# зададим начальное значение счетчика +index = 0 + +# пока счетчик меньше трех +while index < 3: + + # в каждом цикле будем выводить его текущее значение + print("Текущее значение счетчика: " + str(index)) + + # внутри цикла не забудем "нарастить" счетчик + index = index + 1 + + # и выведем новое значение + print("Новое значение счетчика: " + str(index)) + + # добавим пустую строку + print() + +# %% +# тот же код можно упростить +index = 0 + +while index < 3: + print(index) + # в частности, оператор += сразу увеличивает и присваивает новое значение + index += 1 + +# #### Break, continue +# + +# Оператор break +# + +# %% +# вновь возьмем словарь clients +clients_1: dict[int, dict[str, int | str]] = { + 1: {"name": "Анна", "age": 24, "sex": "male", "revenue": 12000}, + 2: {"name": "Илья", "age": 18, "sex": "female", "revenue": 8000}, +} + +# в цикле пройдемся по ключам и значениям словаря +for client_id, info in clients_1.items(): + + # и выведем их + print(client_id, info) + + # однако уже после первого исполнения цикла, прервем его + break + +# %% +# начальное значение счетчика +x_int = 6 + +# будем исполнять цикл пока x не равен нулю +while x_int != 0: + + # выведем текущее значение счетчика + print(x_int) + + # и уменьшим (!) его на 1 + x_int -= 1 + + # если значение счетчика станет равным 3, прервем цикл + if x_int == 3: + break + +# Оператор continue +# + +# выведем все четные числа в диапазоне от 1 до 10 включительно. + +# с помощью функции range() создадим последовательность от 1 до 10 +for index in range(1, 11): + + # если остаток от деления на два не равен нулю (то есть число нечетное) + if index % 2 != 0: + + # идем к следующему числу последовательности + continue + + # в противном случае выводим число + print(index) + +# #### Форматирование строк через f-строки и метод .format() +# + +# %% +# снова возьмем список с днями недели +days = [ + "Понедельник", + "Вторник", + "Среда", + "Четверг", + "Пятница", + "Суббота", + "Воскресенье", +] + +# и для простоты поместим слово "Понедельник" в переменную Monday +Monday = days[0] +Monday + +# %% +# теперь напишем фразу "Понедельник - день тяжелый" следующим образом +print(f"{Monday} - день тяжелый") + +# %% +# то же самое можно вывести с помощью метода .format() +print(f"{Monday} - день тяжелый") # pylint: disable=C0209 + +# ### Ответы на вопросы к занятию +# + +# **Вопрос**. Можно ли использовать цикл while с функцией range()? +# + +# с функцией range() можно использовать цикл while, но такое решение не оптимально +# приведем пример с while + +index = 1 # создадим счетчик + +while index in range(1, 11): # пока счетчик в диапазоне от 1 до 10 + print("Значение счетчика ", index) # выведем его значение и + index += 1 # увеличим счетчик на 1 + +# %% +# более оптимальный код +for index in range(1, 11): + print("Значение счетчика ", index) + +# **Вопрос**. Можно ли обойтись без оператора continue в приведенном на занятии примере? +# + +# %% +for index in range(1, 11): + # если число четное, выведем его + if index % 2 == 0: + print(index) diff --git a/Python/makarov/chapter_04_files.ipynb b/Python/makarov/chapter_04_files.ipynb new file mode 100644 index 00000000..c8331a88 --- /dev/null +++ b/Python/makarov/chapter_04_files.ipynb @@ -0,0 +1,7552 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров. Работа с файлами в Google Colab.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wCSNo_gqmeQt" + }, + "source": [ + "## Работа с файлами в Google Colab" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oXnMiXFesjVo" + }, + "source": [ + "### Этап 1. Подгрузка файлов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Aul2gf2fCeXi" + }, + "source": [ + "Способ 1. Вручную через вкладку 'Файлы'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "J3wl2Nq1CrEu" + }, + "outputs": [], + "source": [ + "# см. материалы урока на сайте" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vT5fboDlCrkx" + }, + "source": [ + "Способ 2. Через модуль files библиотеки google.colab" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "G_diXkM5mOCB" + }, + "outputs": [], + "source": [ + "# импортируем модуль os\n", + "import os\n", + "\n", + "# импортируем библиотеку\n", + "import pandas as pd\n", + "\n", + "# для построения графиков воспользуемся новой для нас библиотекой seaborn\n", + "import seaborn as sns\n", + "\n", + "# из библиотеки google.colab импортируем класс files\n", + "from google.colab import files\n", + "\n", + "# импортируем логистическую регрессию из модуля linear_model библиотеки sklearn\n", + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "# импортируем метрику accuracy из sklearn\n", + "# построим матрицу ошибок\n", + "from sklearn.metrics import accuracy_score, confusion_matrix\n", + "\n", + "# импортируем класс StandardScaler\n", + "from sklearn.preprocessing import StandardScaler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 73 + }, + "id": "_jRYdyKdMxvX", + "outputId": "d82bd7e6-b886-4110-b161-77dc1f7cc109" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " Upload widget is only available when the cell has been executed in the\n", + " current browser session. Please rerun this cell to enable.\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saving test.csv to test.csv\n" + ] + } + ], + "source": [ + "# создаем объект этого класса, применяем метод .upload()\n", + "uploaded = files.upload()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yh8CPuhlpzL2" + }, + "outputs": [], + "source": [ + "# посмотрим на содержимое словаря uploaded\n", + "uploaded" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GRWVTwZyD5QG" + }, + "source": [ + "### Этап 2. Чтение файлов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "juHKhicPELyw" + }, + "source": [ + "#### Просмотр содержимого папки /content/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1-Qe9XS0vqIf" + }, + "source": [ + "##### Модуль os и метод .walk()" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NrbpYhSR9b0p", + "outputId": "716bb453-aa13-44d7-c8c8-acc0cfc22397" + }, + "outputs": [], + "source": [ + "# выводим пути к папкам (dirpath) и наименования файлов (filenames) и после этого\n", + "for dirpath, _, filenames in os.walk(\"/content/\"):\n", + "\n", + " # во вложенном цикле проходимся по названиям файлов\n", + " for filename in filenames:\n", + "\n", + " # и соединяем путь до папок и входящие в эти папки файлы\n", + " # с помощью метода path.join()\n", + " print(os.path.join(dirpath, filename))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tYOEkfDGu6LB" + }, + "source": [ + "##### Команда `!ls`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gGF1t5Vpu_td", + "outputId": "5e9a46c9-e2c5-48b8-9641-973e0b059aa3" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "'ls' is not recognized as an internal or external command,\n", + "operable program or batch file.\n" + ] + } + ], + "source": [ + "# посмотрим на содержимое папки content\n", + "# !ls" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Bf0b4mWBxn_n", + "outputId": "a5e3c446-5580-4f29-ee26-ce6fa42494aa" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "anscombe.json\t\t california_housing_train.csv mnist_train_small.csv\n", + "california_housing_test.csv mnist_test.csv\t\t README.md\n" + ] + } + ], + "source": [ + "# заглянем внутрь sample_data\n", + "# !ls /content/sample_data/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vusmtwpkD8R-" + }, + "source": [ + "#### Чтение из переменной uploaded" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xUqBSfBl2oRf", + "outputId": "f2251b43-e01e-409b-8d8c-205eb5418a2f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bytes" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на тип значений словаря uploaded\n", + "type(uploaded[\"test.csv\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oO_k88nfnV_P" + }, + "source": [ + "Пример работы с объектом bytes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EQze4APGmvYK", + "outputId": "96ffde8d-586b-46bc-a0dd-123f5de9fda7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "# обратимся к ключу словаря uploaded и применим метод .decode()\n", + "uploaded_str = uploaded[\"test.csv\"].decode()\n", + "\n", + "# на выходе получаем обычную строку\n", + "print(type(uploaded_str))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AyMuoDg2WyCr", + "outputId": "f277fbb3-38d0-4f95-e0d7-56039873c703" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PassengerId,Pclass,Name,Sex,Age,Sib\n" + ] + } + ], + "source": [ + "# выведем первые 35 значений\n", + "print(uploaded_str[:35])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AoBoGKGKCC3J", + "outputId": "850cc96d-f696-4d70-b2f7-d91f86d9651b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если разбить строку методом .split() по символам \\r\n", + "# (возврат к началу строки) и \\n (новая строка)\n", + "uploaded_list = uploaded_str.split(\"\\r\\n\")\n", + "\n", + "# на выходе мы получим список\n", + "type(uploaded_list)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-DiIL3dipCYe", + "outputId": "90d410f2-5189-4b4c-a356-6a2b7d64512d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PassengerId,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked\n", + "892,3,\"Kelly, Mr. James\",male,34.5,0,0,330911,7.8292,,Q\n", + "893,3,\"Wilkes, Mrs. James (Ellen Needs)\",female,47,1,0,363272,7,,S\n", + "894,2,\"Myles, Mr. Thomas Francis\",male,62,0,0,240276,9.6875,,Q\n" + ] + } + ], + "source": [ + "# пройдемся по этому списку, не забыв создать индекс с помощью функции enumerate()\n", + "for i, line in enumerate(uploaded_list):\n", + "\n", + " # начнем выводить записи\n", + " print(line)\n", + "\n", + " # когда дойдем до четвертой строки\n", + " if i == 3:\n", + "\n", + " # прервемся\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Bs9a3vc0FVAu" + }, + "source": [ + "#### Использование функции open() и конструкции with open()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "y0ljQMYw8-tt", + "outputId": "daeafd6c-a92c-4529-a123-595bd2d36977" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked\n", + "1,0,3,\"Braund, Mr. Owen Harris\",male,22,1,0,A/5 21171,7.25,,S\n", + "\n" + ] + } + ], + "source": [ + "# передадим функции open() адрес файла\n", + "# параметр 'r' означает, что мы хотим прочитать (read) файл\n", + "\n", + "# f1 = open(\"/content/train.csv\", encoding=\"utf-8\")\n", + "\n", + "# метод .read() помещает весь файл в одну строку\n", + "# выведем первые 142 символа (если параметр не указывать, выведется все содержимое)\n", + "# print(f1.read(142))\n", + "\n", + "# в конце файл необходимо закрыть\n", + "# f1.close()\n", + "\n", + "with open(\"/content/train.csv\", encoding=\"utf-8\") as f1:\n", + " print(f1.read(142))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bY54zGjAKql_", + "outputId": "d423fb97-6639-434c-d8ba-93869f06fc6c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked\n", + "1,0,3,\"Braund, Mr. Owen Harris\",male,22,1,0,A/5 21171,7.25,,S\n", + "2,1,1,\"Cumings, Mrs. John Bradley (Florence Briggs Thayer)\",female,38,1,0,PC 17599,71.2833,C85,C\n", + "3,1,3,\"Heikkinen, Miss. Laina\",female,26,0,0,STON/O2. 3101282,7.925,,S\n" + ] + } + ], + "source": [ + "# снова откроем файл\n", + "# f2 = open(\"/content/train.csv\", encoding=\"utf-8\")\n", + "\n", + "# пройдемся по нашему объекту в цикле for и параллельно создадим индекс\n", + "# for index, line in enumerate(f2):\n", + "\n", + "# выведем строки без служебных символов по краям\n", + "# print(line.strip())\n", + "\n", + "# дойдя до четвертой строки, прервемся\n", + "# if index == 3:\n", + "# break\n", + "\n", + "# не забудем закрыть файл\n", + "# f2.close()\n", + "\n", + "with open(\"/content/train.csv\", encoding=\"utf-8\") as f2:\n", + " for index, line in enumerate(f2):\n", + " print(line.strip())\n", + "\n", + " if index == 3:\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HbzFPTT__z7x", + "outputId": "3815306c-b796-4c92-9ffd-1ef14ec8f741" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PassengerId,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked\n", + "892,3,\"Kelly, Mr. James\",male,34.5,0,0,330911,7.8292,,Q\n", + "893,3,\"Wilkes, Mrs. James (Ellen Needs)\",female,47,1,0,363272,7,,S\n", + "894,2,\"Myles, Mr. Thomas Francis\",male,62,0,0,240276,9.6875,,Q\n" + ] + } + ], + "source": [ + "# скажем Питону: \"открой файл и назови его f3\"\n", + "with open(\"/content/test.csv\", encoding=\"utf-8\") as f3:\n", + "\n", + " # \"пройдись по строкам без служебных символов\"\n", + " for index, line in enumerate(f3):\n", + " print(line.strip())\n", + "\n", + " # и \"прервись на четвертой строке\"\n", + " if index == 3:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vcyff9nqEzJd" + }, + "source": [ + "#### Чтение через библиотеку Pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 320 + }, + "id": "xhqoLRKeqIfM", + "outputId": "4f5c9a0e-8366-4063-fbf2-efdd0b85161b" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"train\",\n \"rows\": 891,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 257,\n \"min\": 1,\n \"max\": 891,\n \"num_unique_values\": 891,\n \"samples\": [\n 710,\n 440,\n 841\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Pclass\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 1,\n \"max\": 3,\n \"num_unique_values\": 3,\n \"samples\": [\n 3,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 891,\n \"samples\": [\n \"Moubarek, Master. Halim Gonios (\\\"William George\\\")\",\n \"Kvillner, Mr. Johan Henrik Johannesson\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Sex\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"female\",\n \"male\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 14.526497332334044,\n \"min\": 0.42,\n \"max\": 80.0,\n \"num_unique_values\": 88,\n \"samples\": [\n 0.75,\n 22.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 6,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Ticket\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 681,\n \"samples\": [\n \"11774\",\n \"248740\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 49.693428597180905,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 248,\n \"samples\": [\n 11.2417,\n 51.8625\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Cabin\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 147,\n \"samples\": [\n \"D45\",\n \"B49\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Embarked\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"S\",\n \"C\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "train" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Survived Pclass \\\n", + "0 1 0 3 \n", + "1 2 1 1 \n", + "2 3 1 3 \n", + "\n", + " Name Sex Age SibSp \\\n", + "0 Braund, Mr. Owen Harris male 22.0 1 \n", + "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n", + "2 Heikkinen, Miss. Laina female 26.0 0 \n", + "\n", + " Parch Ticket Fare Cabin Embarked \n", + "0 0 A/5 21171 7.2500 NaN S \n", + "1 0 PC 17599 71.2833 C85 C \n", + "2 0 STON/O2. 3101282 7.9250 NaN S " + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# применим функцию read_csv() и посмотрим на первые три записи файла train.csv\n", + "train = pd.read_csv(\"/content/train.csv\")\n", + "train.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 300 + }, + "id": "V9t1TFc27k8n", + "outputId": "0b2a172e-fd76-4d23-a610-e0ca6792b1f7" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"test\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 120,\n \"min\": 892,\n \"max\": 1309,\n \"num_unique_values\": 418,\n \"samples\": [\n 1213,\n 1216,\n 1280\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Pclass\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 1,\n \"max\": 3,\n \"num_unique_values\": 3,\n \"samples\": [\n 3,\n 2,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 418,\n \"samples\": [\n \"Krekorian, Mr. Neshan\",\n \"Kreuchen, Miss. Emilie\",\n \"Canavan, Mr. Patrick\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Sex\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"female\",\n \"male\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 14.18120923562442,\n \"min\": 0.17,\n \"max\": 76.0,\n \"num_unique_values\": 79,\n \"samples\": [\n 10.0,\n 34.5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 9,\n \"num_unique_values\": 8,\n \"samples\": [\n 1,\n 6\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Ticket\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 363,\n \"samples\": [\n \"2673\",\n \"W./C. 6607\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 55.90757617997383,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 169,\n \"samples\": [\n 41.5792,\n 57.75\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Cabin\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 76,\n \"samples\": [\n \"A21\",\n \"E45\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Embarked\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"Q\",\n \"S\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "test" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
08923Kelly, Mr. Jamesmale34.5003309117.8292NaNQ
18933Wilkes, Mrs. James (Ellen Needs)female47.0103632727.0000NaNS
28942Myles, Mr. Thomas Francismale62.0002402769.6875NaNQ
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Pclass Name Sex Age SibSp \\\n", + "0 892 3 Kelly, Mr. James male 34.5 0 \n", + "1 893 3 Wilkes, Mrs. James (Ellen Needs) female 47.0 1 \n", + "2 894 2 Myles, Mr. Thomas Francis male 62.0 0 \n", + "\n", + " Parch Ticket Fare Cabin Embarked \n", + "0 0 330911 7.8292 NaN Q \n", + "1 0 363272 7.0000 NaN S \n", + "2 0 240276 9.6875 NaN Q " + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# сделаем то же самое с файлом test.csv\n", + "test = pd.read_csv(\"/content/test.csv\")\n", + "test.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wvTrjPezF1NA" + }, + "source": [ + "### Этап 3. Построение модели и прогноз" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ty07ENU862s7" + }, + "source": [ + "#### **Шаг 1**. Обработка и анализ данных" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A5CFjz478kcL" + }, + "source": [ + "Исследовательский анализ данных (EDA)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jnIyw67h9e8f", + "outputId": "3935bc3d-99ad-4be0-ec2a-b8c72aa8c5da" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 891 entries, 0 to 890\n", + "Data columns (total 12 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 PassengerId 891 non-null int64 \n", + " 1 Survived 891 non-null int64 \n", + " 2 Pclass 891 non-null int64 \n", + " 3 Name 891 non-null object \n", + " 4 Sex 891 non-null object \n", + " 5 Age 714 non-null float64\n", + " 6 SibSp 891 non-null int64 \n", + " 7 Parch 891 non-null int64 \n", + " 8 Ticket 891 non-null object \n", + " 9 Fare 891 non-null float64\n", + " 10 Cabin 204 non-null object \n", + " 11 Embarked 889 non-null object \n", + "dtypes: float64(2), int64(5), object(5)\n", + "memory usage: 83.7+ KB\n" + ] + } + ], + "source": [ + "# посмотрим на данные в целом\n", + "train.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 466 + }, + "id": "IQNVkKfV8o1q", + "outputId": "ca1cbd58-24bc-42a6-efec-0699d093193a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAGwCAYAAABPSaTdAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAL0BJREFUeJzt3XtcVXW+//H35rYFYcOgwIYjON5SUbyEpvvUeLyQiOboyUqLk1g+7ERYR2nKwzzMJm0Gs5vVMZ06x0snKccKKydvoWAlWnGGNE1HPRT2kA2OBlsxQGH//ui4f+3xkiGwtsvX8/FYjwdrfb9rrc+iXbz7ru9a2+J2u90CAAAwKT+jCwAAAGhNhB0AAGBqhB0AAGBqhB0AAGBqhB0AAGBqhB0AAGBqhB0AAGBqAUYX4Auampp09OhRhYWFyWKxGF0OAAC4DG63WydPnlRcXJz8/C4+fkPYkXT06FHFx8cbXQYAAGiGI0eOqFOnThdtJ+xICgsLk/TDL8tmsxlcDQAAuBwul0vx8fGev+MXQ9iRPLeubDYbYQcAgKvMT01BYYIyAAAwNcIOAAAwNcIOAAAwNebsAADgA5qamtTQ0GB0GT4lMDBQ/v7+V3wcwg4AAAZraGhQWVmZmpqajC7F50RERMhut1/Re/AIOwAAGMjtdquiokL+/v6Kj4+/5MvxriVut1unT59WVVWVJCk2NrbZxyLsAABgoLNnz+r06dOKi4tTSEiI0eX4lODgYElSVVWVoqOjm31Li/gIAICBGhsbJUlBQUEGV+KbzgXAM2fONPsYhB0AAHwA3814YS3xeyHsAAAAUyPsAAAAUyPsAACA8xQWFspisai6urpVzzNt2jRNnDixVc9B2AEAwIcdO3ZMmZmZSkhIkNVqld1uV2pqqj755JNWPe8//uM/qqKiQuHh4a16nrbAo+cAAPiwSZMmqaGhQatWrVLXrl1VWVmpgoICHT9+vFnHc7vdamxsVEDApSNAUFCQ7HZ7s87haxjZAQDAR1VXV+ujjz7SU089pREjRqhz58664YYblJOTo1//+tf6+uuvZbFYVFpa6rWPxWJRYWGhpP9/O2rDhg1KTk6W1WrV8uXLZbFYtH//fq/zPf/88+rWrZvXftXV1XK5XAoODtaGDRu8+ufn5yssLEynT5+WJB05ckR33HGHIiIiFBkZqQkTJujrr7/29G9sbFR2drYiIiLUoUMHPfroo3K73S3/i/s7jOwAANpM8iOvGV2CTyh5eupl9QsNDVVoaKjWrVunoUOHymq1Nvuc//7v/65nnnlGXbt21S9+8Qu9+uqrWr16tRYsWODps3r1at11113n7Wuz2XTLLbcoLy9PaWlpXv0nTpyokJAQnTlzRqmpqXI4HProo48UEBCgJ598UmPGjNHu3bsVFBSkZ599VitXrtTy5cvVu3dvPfvss8rPz9fIkSObfV2Xg5EdAAB8VEBAgFauXKlVq1YpIiJCN954o377299q9+7dP/tY8+fP180336xu3bopMjJS6enpeuONNzztf/3rX1VSUqL09PQL7p+enq5169Z5RnFcLpf+/Oc/e/qvWbNGTU1N+s///E8lJSWpd+/eWrFihcrLyz2jTIsXL1ZOTo5uvfVW9e7dW8uWLWuTOUGEHQAAfNikSZN09OhRvffeexozZowKCwt1/fXXa+XKlT/rOIMGDfJanzJlir7++mvt3LlT0g+jNNdff7169ep1wf3Hjh2rwMBAvffee5Kkt99+WzabTSkpKZKkL774QocOHVJYWJhnRCoyMlJ1dXU6fPiwampqVFFRoSFDhniOGRAQcF5drYGwAwCAj2vXrp1uvvlmPfbYY9qxY4emTZumxx9/3POloT+e93Kxr1Vo376917rdbtfIkSOVl5cnScrLy7voqI70w4Tl2267zav/5MmTPROdT506peTkZJWWlnotf/3rXy94a6wtEXYAALjKJCYmqra2VlFRUZKkiooKT9uPJyv/lPT0dK1Zs0bFxcX63//9X02ZMuUn+2/cuFF79+7V1q1bvcLR9ddfr4MHDyo6Olrdu3f3WsLDwxUeHq7Y2Fjt2rXLs8/Zs2dVUlJy2fU2F2EHAAAfdfz4cY0cOVKvv/66du/erbKyMq1du1aLFi3ShAkTFBwcrKFDh2rhwoX66quvVFRUpLlz51728W+99VadPHlSmZmZGjFihOLi4i7Zf9iwYbLb7UpPT1eXLl28bkmlp6erY8eOmjBhgj766COVlZWpsLBQDz30kL799ltJ0r/9279p4cKFWrdunfbv368HHnig1V9aKBF2AADwWaGhoRoyZIief/55DRs2TH379tVjjz2mGTNm6D/+4z8kScuXL9fZs2eVnJysWbNm6cknn7zs44eFhWn8+PH64osv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nj1b77//vtauXauioiIdPXpUt956q2f/xsZGjRs3Tg0NDdqxY4dWrVqllStXat68eUZdEgAA8DEWt49981hkZKSefvpp3XbbbYqKilJeXp5uu+02SdL+/fvVu3dvFRcXa+jQodqwYYNuueUWHT16VDExMZKkZcuWac6cOTp27JiCgoIu65wul0vh4eGqqamRzWZrtWsDgGtd8iOvGV2CTyh5eqrRJZjC5f799pk5O42NjXrzzTdVW1srh8OhkpISnTlzRikpKZ4+vXr1UkJCgoqLiyVJxcXFSkpK8gQdSUpNTZXL5fKMDl1IfX29XC6X1wIAAMzJ8LCzZ88ehYaGymq16v7771d+fr4SExPldDoVFBSkiIgIr/4xMTFyOp2SJKfT6RV0zrWfa7uY3NxchYeHe5b4+PiWvSgAAOAzDA87PXv2VGlpqXbt2qXMzExlZGRo3759rXrOnJwc1dTUeJYjR4606vkAAIBxDP9urKCgIHXv3l2SlJycrM8++0wvvPCCJk+erIaGBlVXV3uN7lRWVnq+/dRut+vTTz/1Ot65p7Uu9Q2pVqtVVqu1ha8EAAD4IsNHdv5eU1OT6uvrlZycrMDAQBUUFHjaDhw4oPLycjkcDkmSw+HQnj17VFVV5emzZcsW2Ww2JSYmtnntAADA9xg6spOTk6O0tDQlJCTo5MmTysvLU2FhoTZt2qTw8HBNnz5d2dnZioyMlM1m04MPPiiHw6GhQ4dKkkaPHq3ExETdfffdWrRokZxOp+bOnausrCxGbgAAgCSDw05VVZWmTp2qiooKhYeHq1+/ftq0aZNuvvlmSdLzzz8vPz8/TZo0SfX19UpNTdXLL7/s2d/f31/r169XZmamHA6H2rdvr4yMDM2fP9+oSwIAAD7G596zYwTeswMAbYP37PyA9+y0jKvuPTsAAACtgbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbADAABMjbAD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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# посмотрим насколько значим класс билета для выживания пассажира\n", + "# с помощью x и hue мы можем уместить две категориальные переменные на одном графике\n", + "sns.countplot(x=\"Pclass\", hue=\"Survived\", data=train)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 466 + }, + "id": "HoCtj0FG9MiS", + "outputId": "ec3950c0-f606-4e71-a1c6-d319b0f0e030" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# кто выживал чаще, мужчины или женщины?\n", + "sns.countplot(x=\"Sex\", hue=\"Survived\", data=train)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mFcc_bwf8Xmd" + }, + "source": [ + "Пропущенные значения" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 460 + }, + "id": "eKEsQM9ytpSw", + "outputId": "bf2734b8-ccbb-4b25-f60e-29712f081e1b" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
0
PassengerId0
Survived0
Pclass0
Name0
Sex0
Age177
SibSp0
Parch0
Ticket0
Fare0
Cabin687
Embarked2
\n", + "

" + ], + "text/plain": [ + "PassengerId 0\n", + "Survived 0\n", + "Pclass 0\n", + "Name 0\n", + "Sex 0\n", + "Age 177\n", + "SibSp 0\n", + "Parch 0\n", + "Ticket 0\n", + "Fare 0\n", + "Cabin 687\n", + "Embarked 2\n", + "dtype: int64" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# выявим пропущенные значения с помощью .isnull() и посчитаем их количество через sum()\n", + "train.isnull().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1jnUnuwguYie" + }, + "outputs": [], + "source": [ + "# переменная Cabin (номер каюты), скорее всего, не является самой важной\n", + "# избавимся от нее с помощью метода .drop()\n", + "# (параметр axis = 1 отвечает за столбцы, inplace = True сохраняет изменения)\n", + "train.drop(columns=\"Cabin\", axis=1, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_vgHXnzb7V0K" + }, + "outputs": [], + "source": [ + "# а вот Age (возраст) скорее важен, заменим пустые значения средним арифметическим\n", + "train[\"Age\"] = train[\"Age\"].fillna(train[\"Age\"].mean())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RsfSaXH87tLX" + }, + "outputs": [], + "source": [ + "# у нас остаются две пустые строки в Embarked, удалим их\n", + "train.dropna(inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 429 + }, + "id": "pL_1E3Ug8Q9X", + "outputId": "363e114f-bfac-4b77-abb8-46bfdce2f828" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
0
PassengerId0
Survived0
Pclass0
Name0
Sex0
Age0
SibSp0
Parch0
Ticket0
Fare0
Embarked0
\n", + "

" + ], + "text/plain": [ + "PassengerId 0\n", + "Survived 0\n", + "Pclass 0\n", + "Name 0\n", + "Sex 0\n", + "Age 0\n", + "SibSp 0\n", + "Parch 0\n", + "Ticket 0\n", + "Fare 0\n", + "Embarked 0\n", + "dtype: int64" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на результат\n", + "train.isnull().sum()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XwG-o31i8bTY" + }, + "source": [ + "Категориальные переменные" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "WA8nUK_v8bEp", + "outputId": "2067b08a-9b36-43a4-e331-bbbfe6df9298" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"pd\",\n \"rows\": 3,\n \"fields\": [\n {\n \"column\": \"female\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
femalemale
0FalseTrue
1TrueFalse
2TrueFalse
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " female male\n", + "0 False True\n", + "1 True False\n", + "2 True False" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# применим one-hot encoding к переменной Sex (пол) с помощью функции pd.get_dummies()\n", + "pd.get_dummies(train[\"Sex\"]).head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "id": "SP7mJ14CGhTa", + "outputId": "055c29ae-2499-4113-ca5f-0c60ed726c60" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"previous\",\n \"rows\": 891,\n \"fields\": [\n {\n \"column\": \"Sex\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"female\",\n \"male\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "previous" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Sex
0male
1female
2female
3female
4male
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Sex\n", + "0 male\n", + "1 female\n", + "2 female\n", + "3 female\n", + "4 male" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# снова скачаем столбец Sex из датасета train в формате датафрейма\n", + "previous = pd.read_csv(\"/content/train.csv\")[[\"Sex\"]]\n", + "previous.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "rOH6dGYYHvc9", + "outputId": "d9ddfc7f-3898-4594-cc65-50bfd75b7e7c" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"pd\",\n \"rows\": 3,\n \"fields\": [\n {\n \"column\": \"female\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
femalemale
001
110
210
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " female male\n", + "0 0 1\n", + "1 1 0\n", + "2 1 0" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# закодируем переменную через 0 и 1\n", + "pd.get_dummies(previous[\"Sex\"], dtype=int).head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "2A85ZWKZAQG7", + "outputId": "edb54969-2362-47f9-8a05-81086a74fec2" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"sex\",\n \"rows\": 889,\n \"fields\": [\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "sex" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
male
0True
1False
2False
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " male\n", + "0 True\n", + "1 False\n", + "2 False" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# удалим первый столбец, он избыточен\n", + "sex = pd.get_dummies(train[\"Sex\"], drop_first=True)\n", + "sex.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "swp7EciGAX66" + }, + "outputs": [], + "source": [ + "# сделаем то же самое для переменных Pclass и Embarked\n", + "embarked = pd.get_dummies(train[\"Embarked\"], drop_first=True)\n", + "pclass = pd.get_dummies(train[\"Pclass\"], drop_first=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 320 + }, + "id": "bxeqXOzPAeeX", + "outputId": "eb544df0-638b-43c7-b34c-fb1a464a25bd" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"train\",\n \"rows\": 889,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 256,\n \"min\": 1,\n \"max\": 891,\n \"num_unique_values\": 889,\n \"samples\": [\n 282,\n 436,\n 40\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Pclass\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 1,\n \"max\": 3,\n \"num_unique_values\": 3,\n \"samples\": [\n 3,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 889,\n \"samples\": [\n \"Olsson, Mr. Nils Johan Goransson\",\n \"Carter, Miss. Lucile Polk\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Sex\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"female\",\n \"male\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 12.968366309252332,\n \"min\": 0.42,\n \"max\": 80.0,\n \"num_unique_values\": 89,\n \"samples\": [\n 59.0,\n 36.5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 6,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Ticket\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 680,\n \"samples\": [\n \"11774\",\n \"29105\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 49.69750431670801,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 247,\n \"samples\": [\n 11.2417,\n 51.8625\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Embarked\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"S\",\n \"C\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 2,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 3,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "train" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareEmbarked23maleQS
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500SFalseTrueTrueFalseTrue
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833CFalseFalseFalseFalseFalse
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250SFalseTrueFalseFalseTrue
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Survived Pclass \\\n", + "0 1 0 3 \n", + "1 2 1 1 \n", + "2 3 1 3 \n", + "\n", + " Name Sex Age SibSp \\\n", + "0 Braund, Mr. Owen Harris male 22.0 1 \n", + "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n", + "2 Heikkinen, Miss. Laina female 26.0 0 \n", + "\n", + " Parch Ticket Fare Embarked 2 3 male Q \\\n", + "0 0 A/5 21171 7.2500 S False True True False \n", + "1 0 PC 17599 71.2833 C False False False False \n", + "2 0 STON/O2. 3101282 7.9250 S False True False False \n", + "\n", + " S \n", + "0 True \n", + "1 False \n", + "2 True " + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# присоединим закодированные через one-hot encoding переменные\n", + "# к исходному датафрейму через функцию .concat()\n", + "train = pd.concat([train, pclass, sex, embarked], axis=1)\n", + "train.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sIlbuEWVA7oz" + }, + "source": [ + "Отбор признаков" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "lkw47WMXA6dI", + "outputId": "da3d8cc2-cb32-4c96-c1ac-103ff7a3585a" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"train\",\n \"rows\": 889,\n \"fields\": [\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 12.968366309252332,\n \"min\": 0.42,\n \"max\": 80.0,\n \"num_unique_values\": 89,\n \"samples\": [\n 59.0,\n 36.5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 6,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 49.69750431670801,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 247,\n \"samples\": [\n 11.2417,\n 51.8625\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 2,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 3,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "train" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SurvivedAgeSibSpParchFare23maleQS
0022.0107.2500FalseTrueTrueFalseTrue
1138.01071.2833FalseFalseFalseFalseFalse
2126.0007.9250FalseTrueFalseFalseTrue
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Survived Age SibSp Parch Fare 2 3 male Q S\n", + "0 0 22.0 1 0 7.2500 False True True False True\n", + "1 1 38.0 1 0 71.2833 False False False False False\n", + "2 1 26.0 0 0 7.9250 False True False False True" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# удалим те столбцы, которые нам теперь не нужны\n", + "train.drop(\n", + " [\"PassengerId\", \"Pclass\", \"Name\", \"Sex\", \"Ticket\", \"Embarked\"],\n", + " axis=1,\n", + " inplace=True,\n", + ")\n", + "train.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V0ww6yllFVEd" + }, + "source": [ + "Нормализация данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "r38gNBNKFUf-", + "outputId": "b5641f86-f2b9-46b5-9c8f-673b22bf71a8" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"train\",\n \"rows\": 889,\n \"fields\": [\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.0005629046322482,\n \"min\": -2.255480861315933,\n \"max\": 3.8844440954479698,\n \"num_unique_values\": 89,\n \"samples\": [\n 2.2642075524466883,\n 0.5282398278024586\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 6,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.0005629046322495,\n \"min\": -0.6462044460638905,\n \"max\": 9.668550782149426,\n \"num_unique_values\": 247,\n \"samples\": [\n -0.41987460999558585,\n 0.39794644953841574\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 2,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 3,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "train" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SurvivedAgeSibSpParchFare23maleQS
00-0.59049510-0.500240FalseTrueTrueFalseTrue
110.643971100.788947FalseFalseFalseFalseFalse
21-0.28187800-0.486650FalseTrueFalseFalseTrue
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Survived Age SibSp Parch Fare 2 3 male Q \\\n", + "0 0 -0.590495 1 0 -0.500240 False True True False \n", + "1 1 0.643971 1 0 0.788947 False False False False \n", + "2 1 -0.281878 0 0 -0.486650 False True False False \n", + "\n", + " S \n", + "0 True \n", + "1 False \n", + "2 True " + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект этого класса\n", + "scaler = StandardScaler()\n", + "\n", + "# выберем те столбцы, которые мы хотим масштабировать\n", + "cols_to_scale = [\"Age\", \"Fare\"]\n", + "\n", + "# рассчитаем среднее арифметическое и СКО для масштабирования данных\n", + "scaler.fit(train[cols_to_scale])\n", + "\n", + "# применим их\n", + "train[cols_to_scale] = scaler.transform(train[cols_to_scale])\n", + "\n", + "# посмотрим на результат\n", + "train.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DXWFh0_-gW8p", + "outputId": "188898bd-a112-4b7e-e5e9-812d794117e9" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['Survived', 'Age', 'SibSp', 'Parch', 'Fare', 2, 3, 'male', 'Q', 'S'], dtype='object')" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# некоторые названия столбцов теперь представляют собой числа,\n", + "# так быть не должно\n", + "train.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yzsCbS5CE00Q", + "outputId": "e98f9961-9ade-452f-c166-b42ff5e3faef" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['Survived', 'Age', 'SibSp', 'Parch', 'Fare', '2', '3', 'male', 'Q',\n", + " 'S'],\n", + " dtype='object')" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# преобразуем эти переменные в тип str через функцию map()\n", + "train.columns = train.columns.map(str)\n", + "train.columns" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7b96LsxzBhIw" + }, + "source": [ + "#### **Шаг 2**. Разделение обучающей выборки на признаки (X_train) и целевую переменную (y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NUdfd8rDBcr7" + }, + "outputs": [], + "source": [ + "# поместим в X_train все кроме столбца Survived\n", + "X_train = train.drop(\"Survived\", axis=1)\n", + "\n", + "# столбец 'Survived' станет нашей целевой переменной (y_train)\n", + "y_train = train[\"Survived\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "Y9InikfbKKYp", + "outputId": "a5a76203-fad3-44d0-fe7e-80a75f56e77f" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"X_train\",\n \"rows\": 889,\n \"fields\": [\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.0005629046322482,\n \"min\": -2.255480861315933,\n \"max\": 3.8844440954479698,\n \"num_unique_values\": 89,\n \"samples\": [\n 2.2642075524466883,\n 0.5282398278024586,\n -0.08899314095993428\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 1,\n 0,\n 5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 6,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1,\n 4\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.0005629046322495,\n \"min\": -0.6462044460638905,\n \"max\": 9.668550782149426,\n \"num_unique_values\": 247,\n \"samples\": [\n -0.41987460999558585,\n 0.39794644953841574,\n -0.4906765469358535\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"2\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"3\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "X_train" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AgeSibSpParchFare23maleQS
0-0.59049510-0.500240FalseTrueTrueFalseTrue
10.643971100.788947FalseFalseFalseFalseFalse
2-0.28187800-0.486650FalseTrueFalseFalseTrue
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Age SibSp Parch Fare 2 3 male Q S\n", + "0 -0.590495 1 0 -0.500240 False True True False True\n", + "1 0.643971 1 0 0.788947 False False False False False\n", + "2 -0.281878 0 0 -0.486650 False True False False True" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "X_train.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ORiI_6fIB7Aj" + }, + "source": [ + "#### **Шаг 3**. Обучение модели логистической регрессии" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "keeoPQF0j1GY" + }, + "source": [ + "Обучим модель" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 80 + }, + "id": "5M0qRHiiCAqX", + "outputId": "d3338093-5780-4f01-a750-931e743b6ad2" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "LogisticRegression()" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект этого класса и запишем его в переменную model\n", + "model = LogisticRegression()\n", + "\n", + "# обучим нашу модель\n", + "model.fit(X_train, y_train)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yOZ8P8Wpj4Hl" + }, + "source": [ + "Сделаем прогноз на обучающей выборке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MSPmtjSdCOa1" + }, + "outputs": [], + "source": [ + "# сделаем предсказание класса на обучающей выборке\n", + "y_pred_train = model.predict(X_train)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XC5UoACekDgh" + }, + "source": [ + "Оценка качества модели на обучающей выборке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 125 + }, + "id": "YkFl1ZQPkI0N", + "outputId": "29eb7740-1596-4789-ed4a-ad76f5cf3cef" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"conf_matrix_df\",\n \"rows\": 2,\n \"fields\": [\n {\n \"column\": 0,\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 265,\n \"min\": 103,\n \"max\": 479,\n \"num_unique_values\": 2,\n \"samples\": [\n 103,\n 479\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 1,\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 118,\n \"min\": 70,\n \"max\": 237,\n \"num_unique_values\": 2,\n \"samples\": [\n 237,\n 70\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "conf_matrix_df" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
01
047970
1103237
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " 0 1\n", + "0 479 70\n", + "1 103 237" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# передадим ей фактические и прогнозные значения\n", + "conf_matrix = confusion_matrix(y_train, y_pred_train)\n", + "\n", + "# преобразуем в датафрейм\n", + "conf_matrix_df = pd.DataFrame(conf_matrix)\n", + "conf_matrix_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 125 + }, + "id": "n4aAsdffPw5x", + "outputId": "0d169887-68bd-4fb6-94cb-4a701186dce7" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"conf_matrix_labels\",\n \"rows\": 2,\n \"fields\": [\n {\n \"column\": \"\\u041f\\u0440\\u043e\\u0433\\u043d\\u043e\\u0437 \\u043f\\u043e\\u0433\\u0438\\u0431\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 265,\n \"min\": 103,\n \"max\": 479,\n \"num_unique_values\": 2,\n \"samples\": [\n 103,\n 479\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"\\u041f\\u0440\\u043e\\u0433\\u043d\\u043e\\u0437 \\u0432\\u044b\\u0436\\u0438\\u043b\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 118,\n \"min\": 70,\n \"max\": 237,\n \"num_unique_values\": 2,\n \"samples\": [\n 237,\n 70\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "conf_matrix_labels" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Прогноз погибПрогноз выжил
Факт погиб47970
Факт выжил103237
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Прогноз погиб Прогноз выжил\n", + "Факт погиб 479 70\n", + "Факт выжил 103 237" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для удобства можем добавить подписи\n", + "conf_matrix_labels = pd.DataFrame(\n", + " conf_matrix,\n", + " columns=[\"Прогноз погиб\", \"Прогноз выжил\"],\n", + " index=[\"Факт погиб\", \"Факт выжил\"],\n", + ")\n", + "conf_matrix_labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SDhyIGg1P6J1", + "outputId": "723d95d7-6ccf-4b2a-c527-7b7b4f2fd5d1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.805" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# рассчитаем метрику accuracy вручную\n", + "round((479 + 237) / (479 + 237 + 70 + 103), 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "clM-Cs9bJHIC", + "outputId": "2d0c2cdc-4f4b-444e-b413-b558efc1f9a1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.805" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# так же передадим ей фактические и прогнозные значения\n", + "model_accuracy = accuracy_score(y_train, y_pred_train)\n", + "\n", + "# округлим до трех знаков после запятой\n", + "round(model_accuracy, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dRaWW4wlCSuT" + }, + "source": [ + "#### **Шаг 4**. Построение прогноза на тестовой выборке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I9cwgY9ZkQpj", + "outputId": "d2408e17-cfe5-43ae-d23f-9d99d04add72" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 418 entries, 0 to 417\n", + "Data columns (total 11 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 PassengerId 418 non-null int64 \n", + " 1 Pclass 418 non-null int64 \n", + " 2 Name 418 non-null object \n", + " 3 Sex 418 non-null object \n", + " 4 Age 332 non-null float64\n", + " 5 SibSp 418 non-null int64 \n", + " 6 Parch 418 non-null int64 \n", + " 7 Ticket 418 non-null object \n", + " 8 Fare 417 non-null float64\n", + " 9 Cabin 91 non-null object \n", + " 10 Embarked 418 non-null object \n", + "dtypes: float64(2), int64(4), object(5)\n", + "memory usage: 36.1+ KB\n" + ] + } + ], + "source": [ + "# посмотрим на тестовые данные\n", + "test.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 300 + }, + "id": "pLjKCcT3JQTU", + "outputId": "1e00c651-c260-4f60-df81-f8117bb06f74" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"test\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 120,\n \"min\": 892,\n \"max\": 1309,\n \"num_unique_values\": 418,\n \"samples\": [\n 1213,\n 1216,\n 1280\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Pclass\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 1,\n \"max\": 3,\n \"num_unique_values\": 3,\n \"samples\": [\n 3,\n 2,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 418,\n \"samples\": [\n \"Krekorian, Mr. Neshan\",\n \"Kreuchen, Miss. Emilie\",\n \"Canavan, Mr. Patrick\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Sex\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"female\",\n \"male\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 14.18120923562442,\n \"min\": 0.17,\n \"max\": 76.0,\n \"num_unique_values\": 79,\n \"samples\": [\n 10.0,\n 34.5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 9,\n \"num_unique_values\": 8,\n \"samples\": [\n 1,\n 6\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Ticket\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 363,\n \"samples\": [\n \"2673\",\n \"W./C. 6607\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 55.90757617997383,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 169,\n \"samples\": [\n 41.5792,\n 57.75\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Cabin\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 76,\n \"samples\": [\n \"A21\",\n \"E45\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Embarked\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"Q\",\n \"S\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "test" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
08923Kelly, Mr. Jamesmale34.5003309117.8292NaNQ
18933Wilkes, Mrs. James (Ellen Needs)female47.0103632727.0000NaNS
28942Myles, Mr. Thomas Francismale62.0002402769.6875NaNQ
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Pclass Name Sex Age SibSp \\\n", + "0 892 3 Kelly, Mr. James male 34.5 0 \n", + "1 893 3 Wilkes, Mrs. James (Ellen Needs) female 47.0 1 \n", + "2 894 2 Myles, Mr. Thomas Francis male 62.0 0 \n", + "\n", + " Parch Ticket Fare Cabin Embarked \n", + "0 0 330911 7.8292 NaN Q \n", + "1 0 363272 7.0000 NaN S \n", + "2 0 240276 9.6875 NaN Q " + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ld01LwRkkjn2" + }, + "outputs": [], + "source": [ + "# теперь нам нужно создать тестовую выборку с теми же признаками\n", + "# и для начала дадим датасету привычное название\n", + "X_test = test" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NIp8FGRuLFac" + }, + "outputs": [], + "source": [ + "# заполним пропуски в переменных Age и Fare средним арифметическим\n", + "X_test[\"Age\"] = X_test[\"Age\"].fillna(test[\"Age\"].mean())\n", + "X_test[\"Fare\"] = X_test[\"Fare\"].fillna(test[\"Fare\"].mean())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2DNOfhSFJop-" + }, + "outputs": [], + "source": [ + "# выполним one-hot encoding категориальных переменных\n", + "sex = pd.get_dummies(X_test[\"Sex\"], drop_first=True)\n", + "embarked = pd.get_dummies(X_test[\"Embarked\"], drop_first=True)\n", + "pclass = pd.get_dummies(X_test[\"Pclass\"], drop_first=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "2DF_oO36lWOQ", + "outputId": "4db5e827-8c7a-406c-9d05-7a172eb622ea" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"X_test\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 12.63453416832505,\n \"min\": 0.17,\n \"max\": 76.0,\n \"num_unique_values\": 80,\n \"samples\": [\n 28.0,\n 34.5,\n 41.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1,\n 5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 9,\n \"num_unique_values\": 8,\n \"samples\": [\n 1,\n 6,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 55.84050047954103,\n \"min\": 0.0,\n \"max\": 512.3292,\n \"num_unique_values\": 170,\n \"samples\": [\n 41.5792,\n 57.75,\n 13.8583\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 2,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 3,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "X_test" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AgeSibSpParchFare23maleQS
034.5007.8292FalseTrueTrueTrueFalse
147.0107.0000FalseTrueFalseFalseTrue
262.0009.6875TrueFalseTrueTrueFalse
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Age SibSp Parch Fare 2 3 male Q S\n", + "0 34.5 0 0 7.8292 False True True True False\n", + "1 47.0 1 0 7.0000 False True False False True\n", + "2 62.0 0 0 9.6875 True False True True False" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# присоединим новые столбцы к исходному датафрейму\n", + "X_test = pd.concat([test, pclass, sex, embarked], axis=1)\n", + "\n", + "# и удалим данные, которые теперь не нужны\n", + "X_test.drop(\n", + " [\"PassengerId\", \"Pclass\", \"Name\", \"Sex\", \"Cabin\", \"Ticket\", \"Embarked\"],\n", + " axis=1,\n", + " inplace=True,\n", + ")\n", + "\n", + "# посмотрим на результат\n", + "X_test.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "MN7KsFjdKAo5", + "outputId": "d7af52d1-c3b2-4e33-c91e-c98bcfac46c6" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"X_test\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"Age\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.9748063792056462,\n \"min\": -2.274769391589758,\n \"max\": 3.5758276110667735,\n \"num_unique_values\": 80,\n \"samples\": [\n -0.12757020150758383,\n 0.37393158561186035,\n 0.8754333727313045\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SibSp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 8,\n \"num_unique_values\": 7,\n \"samples\": [\n 0,\n 1,\n 5\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Parch\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 9,\n \"num_unique_values\": 8,\n \"samples\": [\n 1,\n 6,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Fare\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.1242402234098576,\n \"min\": -0.6462044460638905,\n \"max\": 9.668550782149426,\n \"num_unique_values\": 170,\n \"samples\": [\n 0.1909121394926757,\n 0.5164798483884245,\n -0.36719444159967374\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 2,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": 3,\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Q\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"S\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "X_test" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AgeSibSpParchFare23maleQS
00.37393200-0.488579FalseTrueTrueTrueFalse
11.33835810-0.505273FalseTrueFalseFalseTrue
22.49567000-0.451165TrueFalseTrueTrueFalse
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Age SibSp Parch Fare 2 3 male Q S\n", + "0 0.373932 0 0 -0.488579 False True True True False\n", + "1 1.338358 1 0 -0.505273 False True False False True\n", + "2 2.495670 0 0 -0.451165 True False True True False" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# применим среднее арифметическое и СКО обучающей выборки\n", + "# для масштабирования тестовых данных\n", + "X_test[cols_to_scale] = scaler.transform(X_test[cols_to_scale])\n", + "X_test.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NuUUaRMdK3pA" + }, + "outputs": [], + "source": [ + "# превратим названия столбцов в строки\n", + "X_test.columns = X_test.columns.map(str)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8G_XY6q1Ko7O" + }, + "outputs": [], + "source": [ + "# сделаем прогноз на тестовой выборке\n", + "y_pred_test = model.predict(X_test)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AI5LCobdYrDO", + "outputId": "41d5f1ad-4202-4de5-a4f0-62f217db2c28" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 0, 0, 0, 1, 0, 1, 0, 1, 0])" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на первые 10 прогнозных значений\n", + "y_pred_test[:10]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NTycl5V_F_hH" + }, + "source": [ + "### Этап 4. Сохранение нового файла на сервере Google" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "91QH6AASnDvY" + }, + "source": [ + "Пример оформления результата" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "3FS-mYmpakpy", + "outputId": "58fc596a-bd33-4af1-f2d0-f00cfddc25ff" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"example\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 120,\n \"min\": 892,\n \"max\": 1309,\n \"num_unique_values\": 418,\n \"samples\": [\n 1213,\n 1216,\n 1280\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "example" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdSurvived
08920
18931
28940
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Survived\n", + "0 892 0\n", + "1 893 1\n", + "2 894 0" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# файл с примером можно загрузить не с локального компьютера, а из Интернета\n", + "url = \"https://www.dmitrymakarov.ru/wp-content/uploads/2021/11/titanic_example.csv\"\n", + "\n", + "# просто поместим его url в функцию read_csv()\n", + "example = pd.read_csv(url)\n", + "example.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f0zks67QoCB9" + }, + "source": [ + "Создание файла с прогнозом" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "id": "MEouy36oEb03", + "outputId": "d4cf5976-a36f-4a20-d50e-a5719c85b50a" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"result\",\n \"rows\": 418,\n \"fields\": [\n {\n \"column\": \"PassengerId\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 120,\n \"min\": 892,\n \"max\": 1309,\n \"num_unique_values\": 418,\n \"samples\": [\n 1213,\n 1216,\n 1280\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Survived\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 0,\n \"max\": 1,\n \"num_unique_values\": 2,\n \"samples\": [\n 1,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "result" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdSurvived
08920
18930
28940
38950
48961
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " PassengerId Survived\n", + "0 892 0\n", + "1 893 0\n", + "2 894 0\n", + "3 895 0\n", + "4 896 1" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# возьмем индекс пассажиров из столбца PassengerId тестовой выборки\n", + "ids = test[\"PassengerId\"]\n", + "\n", + "# создадим датафрейм из словаря, в котором\n", + "# первая пара ключа и значения - это id пассажира, вторая - прогноз \"на тесте\"\n", + "result = pd.DataFrame({\"PassengerId\": ids, \"Survived\": y_pred_test})\n", + "\n", + "# посмотрим, что получилось\n", + "result.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VTfh0VeOApyi", + "outputId": "251ed321-755f-4adc-97a4-bd1ca36392d3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Файл успешно сохранился в сессионное хранилище!\n" + ] + } + ], + "source": [ + "# создадим новый файл result.csv с помощью функции to_csv(),\n", + "# удалив при этом индекс\n", + "result.to_csv(\"result.csv\", index=False)\n", + "\n", + "# файл будет сохранен в 'Сессионном хранилище' и,\n", + "# если все пройдет успешно, выведем следующий текст:\n", + "print(\"Файл успешно сохранился в сессионное хранилище!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "N2dAO4cOGE41" + }, + "source": [ + "### Этап 5. Скачивание обратно на жесткий диск" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 17 + }, + "id": "aBW1PkwEGJuu", + "outputId": "a9691a28-0625-4eb0-fb5d-9be05f1b6161" + }, + "outputs": [ + { + "data": { + "application/javascript": "\n async function download(id, filename, size) {\n if (!google.colab.kernel.accessAllowed) {\n return;\n }\n const div = document.createElement('div');\n const label = document.createElement('label');\n label.textContent = `Downloading \"${filename}\": `;\n div.appendChild(label);\n const progress = document.createElement('progress');\n progress.max = size;\n div.appendChild(progress);\n document.body.appendChild(div);\n\n const buffers = [];\n let downloaded = 0;\n\n const channel = await google.colab.kernel.comms.open(id);\n // Send a message to notify the kernel that we're ready.\n channel.send({})\n\n for await (const message of channel.messages) {\n // Send a message to notify the kernel that we're ready.\n channel.send({})\n if (message.buffers) {\n for (const buffer of message.buffers) {\n buffers.push(buffer);\n downloaded += buffer.byteLength;\n progress.value = downloaded;\n }\n }\n }\n const blob = new Blob(buffers, {type: 'application/binary'});\n const a = document.createElement('a');\n a.href = window.URL.createObjectURL(blob);\n a.download = filename;\n div.appendChild(a);\n a.click();\n div.remove();\n }\n ", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "download(\"download_ef40caa2-77de-4c19-bc0c-35cdcc7747ed\", \"result.csv\", 2839)", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# применим метод .download() объекта files\n", + "files.download(\"/content/result.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Q81eQt2WCfeu" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_04_files.py b/Python/makarov/chapter_04_files.py new file mode 100644 index 00000000..b7d369df --- /dev/null +++ b/Python/makarov/chapter_04_files.py @@ -0,0 +1,423 @@ +"""Макаров. + +Работа с файлами в Google Colab. +""" + +# ## Работа с файлами в Google Colab + +# ### Этап 1. Подгрузка файлов + +# Способ 1. Вручную через вкладку 'Файлы' + +# + +# см. материалы урока на сайте +# - + +# Способ 2. Через модуль files библиотеки google.colab + +# + +# импортируем модуль os +import os + +# импортируем библиотеку +import pandas as pd + +# для построения графиков воспользуемся новой для нас библиотекой seaborn +import seaborn as sns + +# из библиотеки google.colab импортируем класс files +from google.colab import files + +# импортируем логистическую регрессию из модуля linear_model библиотеки sklearn +from sklearn.linear_model import LogisticRegression + +# импортируем метрику accuracy из sklearn +# построим матрицу ошибок +from sklearn.metrics import accuracy_score, confusion_matrix + +# импортируем класс StandardScaler +from sklearn.preprocessing import StandardScaler + +# - + +# создаем объект этого класса, применяем метод .upload() +uploaded = files.upload() + +# посмотрим на содержимое словаря uploaded +uploaded + +# ### Этап 2. Чтение файлов + +# #### Просмотр содержимого папки /content/ + +# ##### Модуль os и метод .walk() + +# выводим пути к папкам (dirpath) и наименования файлов (filenames) и после этого +for dirpath, _, filenames in os.walk("/content/"): + + # во вложенном цикле проходимся по названиям файлов + for filename in filenames: + + # и соединяем путь до папок и входящие в эти папки файлы + # с помощью метода path.join() + print(os.path.join(dirpath, filename)) + +# ##### Команда `!ls` + +# + +# посмотрим на содержимое папки content +# # !ls + +# + +# заглянем внутрь sample_data +# # !ls /content/sample_data/ +# - + +# #### Чтение из переменной uploaded + +# посмотрим на тип значений словаря uploaded +type(uploaded["test.csv"]) + +# Пример работы с объектом bytes + +# + +# обратимся к ключу словаря uploaded и применим метод .decode() +uploaded_str = uploaded["test.csv"].decode() + +# на выходе получаем обычную строку +print(type(uploaded_str)) +# - + +# выведем первые 35 значений +print(uploaded_str[:35]) + +# + +# если разбить строку методом .split() по символам \r +# (возврат к началу строки) и \n (новая строка) +uploaded_list = uploaded_str.split("\r\n") + +# на выходе мы получим список +type(uploaded_list) +# - + +# пройдемся по этому списку, не забыв создать индекс с помощью функции enumerate() +for i, line in enumerate(uploaded_list): + + # начнем выводить записи + print(line) + + # когда дойдем до четвертой строки + if i == 3: + + # прервемся + break + +# #### Использование функции open() и конструкции with open() + +# + +# передадим функции open() адрес файла +# параметр 'r' означает, что мы хотим прочитать (read) файл + +# f1 = open("/content/train.csv", encoding="utf-8") + +# метод .read() помещает весь файл в одну строку +# выведем первые 142 символа (если параметр не указывать, выведется все содержимое) +# print(f1.read(142)) + +# в конце файл необходимо закрыть +# f1.close() + +with open("/content/train.csv", encoding="utf-8") as f1: + print(f1.read(142)) + +# + +# снова откроем файл +# f2 = open("/content/train.csv", encoding="utf-8") + +# пройдемся по нашему объекту в цикле for и параллельно создадим индекс +# for index, line in enumerate(f2): + +# выведем строки без служебных символов по краям +# print(line.strip()) + +# дойдя до четвертой строки, прервемся +# if index == 3: +# break + +# не забудем закрыть файл +# f2.close() + +with open("/content/train.csv", encoding="utf-8") as f2: + for index, line in enumerate(f2): + print(line.strip()) + + if index == 3: + break +# - + +# скажем Питону: "открой файл и назови его f3" +with open("/content/test.csv", encoding="utf-8") as f3: + + # "пройдись по строкам без служебных символов" + for index, line in enumerate(f3): + print(line.strip()) + + # и "прервись на четвертой строке" + if index == 3: + break + +# #### Чтение через библиотеку Pandas + +# применим функцию read_csv() и посмотрим на первые три записи файла train.csv +train = pd.read_csv("/content/train.csv") +train.head(3) + +# сделаем то же самое с файлом test.csv +test = pd.read_csv("/content/test.csv") +test.head(3) + +# ### Этап 3. Построение модели и прогноз + +# #### **Шаг 1**. Обработка и анализ данных + +# Исследовательский анализ данных (EDA) + +# посмотрим на данные в целом +train.info() + +# посмотрим насколько значим класс билета для выживания пассажира +# с помощью x и hue мы можем уместить две категориальные переменные на одном графике +sns.countplot(x="Pclass", hue="Survived", data=train) + +# кто выживал чаще, мужчины или женщины? +sns.countplot(x="Sex", hue="Survived", data=train) + +# Пропущенные значения + +# выявим пропущенные значения с помощью .isnull() и посчитаем их количество через sum() +train.isnull().sum() + +# переменная Cabin (номер каюты), скорее всего, не является самой важной +# избавимся от нее с помощью метода .drop() +# (параметр axis = 1 отвечает за столбцы, inplace = True сохраняет изменения) +train.drop(columns="Cabin", axis=1, inplace=True) + +# а вот Age (возраст) скорее важен, заменим пустые значения средним арифметическим +train["Age"] = train["Age"].fillna(train["Age"].mean()) + +# у нас остаются две пустые строки в Embarked, удалим их +train.dropna(inplace=True) + +# посмотрим на результат +train.isnull().sum() + +# Категориальные переменные + +# применим one-hot encoding к переменной Sex (пол) с помощью функции pd.get_dummies() +pd.get_dummies(train["Sex"]).head(3) + +# снова скачаем столбец Sex из датасета train в формате датафрейма +previous = pd.read_csv("/content/train.csv")[["Sex"]] +previous.head() + +# закодируем переменную через 0 и 1 +pd.get_dummies(previous["Sex"], dtype=int).head(3) + +# удалим первый столбец, он избыточен +sex = pd.get_dummies(train["Sex"], drop_first=True) +sex.head(3) + +# сделаем то же самое для переменных Pclass и Embarked +embarked = pd.get_dummies(train["Embarked"], drop_first=True) +pclass = pd.get_dummies(train["Pclass"], drop_first=True) + +# присоединим закодированные через one-hot encoding переменные +# к исходному датафрейму через функцию .concat() +train = pd.concat([train, pclass, sex, embarked], axis=1) +train.head(3) + +# Отбор признаков + +# удалим те столбцы, которые нам теперь не нужны +train.drop( + ["PassengerId", "Pclass", "Name", "Sex", "Ticket", "Embarked"], + axis=1, + inplace=True, +) +train.head(3) + +# Нормализация данных + +# + +# создадим объект этого класса +scaler = StandardScaler() + +# выберем те столбцы, которые мы хотим масштабировать +cols_to_scale = ["Age", "Fare"] + +# рассчитаем среднее арифметическое и СКО для масштабирования данных +scaler.fit(train[cols_to_scale]) + +# применим их +train[cols_to_scale] = scaler.transform(train[cols_to_scale]) + +# посмотрим на результат +train.head(3) +# - + +# некоторые названия столбцов теперь представляют собой числа, +# так быть не должно +train.columns + +# преобразуем эти переменные в тип str через функцию map() +train.columns = train.columns.map(str) +train.columns + +# #### **Шаг 2**. Разделение обучающей выборки на признаки (X_train) и целевую переменную (y_train) + +# + +# поместим в X_train все кроме столбца Survived +X_train = train.drop("Survived", axis=1) + +# столбец 'Survived' станет нашей целевой переменной (y_train) +y_train = train["Survived"] +# - + +X_train.head(3) + +# #### **Шаг 3**. Обучение модели логистической регрессии + +# Обучим модель + +# + +# создадим объект этого класса и запишем его в переменную model +model = LogisticRegression() + +# обучим нашу модель +model.fit(X_train, y_train) +# - + +# Сделаем прогноз на обучающей выборке + +# сделаем предсказание класса на обучающей выборке +y_pred_train = model.predict(X_train) + +# Оценка качества модели на обучающей выборке + +# + +# передадим ей фактические и прогнозные значения +conf_matrix = confusion_matrix(y_train, y_pred_train) + +# преобразуем в датафрейм +conf_matrix_df = pd.DataFrame(conf_matrix) +conf_matrix_df +# - + +# для удобства можем добавить подписи +conf_matrix_labels = pd.DataFrame( + conf_matrix, + columns=["Прогноз погиб", "Прогноз выжил"], + index=["Факт погиб", "Факт выжил"], +) +conf_matrix_labels + +# рассчитаем метрику accuracy вручную +round((479 + 237) / (479 + 237 + 70 + 103), 3) + +# + +# так же передадим ей фактические и прогнозные значения +model_accuracy = accuracy_score(y_train, y_pred_train) + +# округлим до трех знаков после запятой +round(model_accuracy, 3) +# - + +# #### **Шаг 4**. Построение прогноза на тестовой выборке + +# посмотрим на тестовые данные +test.info() + +test.head(3) + +# теперь нам нужно создать тестовую выборку с теми же признаками +# и для начала дадим датасету привычное название +X_test = test + +# заполним пропуски в переменных Age и Fare средним арифметическим +X_test["Age"] = X_test["Age"].fillna(test["Age"].mean()) +X_test["Fare"] = X_test["Fare"].fillna(test["Fare"].mean()) + +# выполним one-hot encoding категориальных переменных +sex = pd.get_dummies(X_test["Sex"], drop_first=True) +embarked = pd.get_dummies(X_test["Embarked"], drop_first=True) +pclass = pd.get_dummies(X_test["Pclass"], drop_first=True) + +# + +# присоединим новые столбцы к исходному датафрейму +X_test = pd.concat([test, pclass, sex, embarked], axis=1) + +# и удалим данные, которые теперь не нужны +X_test.drop( + ["PassengerId", "Pclass", "Name", "Sex", "Cabin", "Ticket", "Embarked"], + axis=1, + inplace=True, +) + +# посмотрим на результат +X_test.head(3) +# - + +# применим среднее арифметическое и СКО обучающей выборки +# для масштабирования тестовых данных +X_test[cols_to_scale] = scaler.transform(X_test[cols_to_scale]) +X_test.head(3) + +# превратим названия столбцов в строки +X_test.columns = X_test.columns.map(str) + +# сделаем прогноз на тестовой выборке +y_pred_test = model.predict(X_test) + +# посмотрим на первые 10 прогнозных значений +y_pred_test[:10] + +# ### Этап 4. Сохранение нового файла на сервере Google + +# Пример оформления результата + +# + +# файл с примером можно загрузить не с локального компьютера, а из Интернета +url = "https://www.dmitrymakarov.ru/wp-content/uploads/2021/11/titanic_example.csv" + +# просто поместим его url в функцию read_csv() +example = pd.read_csv(url) +example.head(3) +# - + +# Создание файла с прогнозом + +# + +# возьмем индекс пассажиров из столбца PassengerId тестовой выборки +ids = test["PassengerId"] + +# создадим датафрейм из словаря, в котором +# первая пара ключа и значения - это id пассажира, вторая - прогноз "на тесте" +result = pd.DataFrame({"PassengerId": ids, "Survived": y_pred_test}) + +# посмотрим, что получилось +result.head() + +# + +# создадим новый файл result.csv с помощью функции to_csv(), +# удалив при этом индекс +result.to_csv("result.csv", index=False) + +# файл будет сохранен в 'Сессионном хранилище' и, +# если все пройдет успешно, выведем следующий текст: +print("Файл успешно сохранился в сессионное хранилище!") +# - + +# ### Этап 5. Скачивание обратно на жесткий диск + +# применим метод .download() объекта files +files.download("/content/result.csv") diff --git a/Python/makarov/chapter_05_datetime.ipynb b/Python/makarov/chapter_05_datetime.ipynb new file mode 100644 index 00000000..796ce553 --- /dev/null +++ b/Python/makarov/chapter_05_datetime.ipynb @@ -0,0 +1,2204 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Дата и время в Питоне.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gQkolpFlve1y" + }, + "source": [ + "## Дата и время в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FYsC5TYTyazW" + }, + "source": [ + "### Модуль datetime" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TSeEV1bV2vnj" + }, + "source": [ + "Импорт модуля и класса datetime" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nxtEc-BavPKZ", + "outputId": "bb9075d5-baa2-41d2-92ba-8895fce037b0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-03-11 12:40:47.722373\n" + ] + } + ], + "source": [ + "# импортируем весь модуль\n", + "# import datetime\n", + "\n", + "# чтобы получить доступ к функции now(), сначала обратимся к модулю, потом к классу\n", + "# print(datetime.datetime.now())\n", + "\n", + "# для этого вначале импортируем соответствующий класс\n", + "# часто из модуля datetime удобнее импортировать только класс datetime\n", + "from datetime import datetime, timedelta\n", + "\n", + "# у нас есть данные по среднемесячной температуре в Нью-Йорке в 2002 году\n", + "import pandas as pd\n", + "\n", + "# для изменения часового пояса нужно импортировать модуль pytz\n", + "import pytz" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "du3H87ntzAw9", + "outputId": "ca6ceb3c-68a1-4907-9114-fa67525e47ad" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-10-13 09:38:37.380829\n" + ] + } + ], + "source": [ + "# и обращаться непосредственно к нему\n", + "print(datetime.now())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Z1xtQ8AV2y-B" + }, + "source": [ + "Объект datetime и функция `now()`" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "98cvjJ8zzZnl", + "outputId": "bd99406a-1c6d-4675-cfbe-c930787a3703" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-10-13 09:38:39.499086\n" + ] + } + ], + "source": [ + "# поместим созданный с помощью функции now() объект datetime в переменную cur_dt\n", + "cur_dt = datetime.now()\n", + "print(cur_dt)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SdIrLFCP3BRS", + "outputId": "d224fe02-1a6c-4a3b-a997-9ce35d08e461" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025 10 13 9 38 39 499086\n" + ] + } + ], + "source": [ + "# с помощью соответствующих атрибутов выведем каждый из компонентов объекта по отдельности\n", + "print(\n", + " cur_dt.year,\n", + " cur_dt.month,\n", + " cur_dt.day,\n", + " cur_dt.hour,\n", + " cur_dt.minute,\n", + " cur_dt.second,\n", + " cur_dt.microsecond,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "x-FZ6xl_Tn9a", + "outputId": "c9a3d44f-fd53-453e-f419-67e0b9e53b08" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2\n" + ] + } + ], + "source": [ + "# также можно посмотреть на день недели\n", + "# метод .weekday() начинает индекс недели с нуля, .isoweekday() - с единицы\n", + "print(cur_dt.weekday(), cur_dt.isoweekday())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YiTIBHFGjdvR", + "outputId": "e1ec254e-660c-4108-e510-cee43e10996e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "# посмотрим на часовой пояс с помощью атрибута tzinfo\n", + "print(cur_dt.tzinfo)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Kf7Dp1Vr4_DB", + "outputId": "a6e42ef6-feec-4caa-a7b4-0b2501f826ac" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-10-13 09:41:32.037466+03:00\n" + ] + } + ], + "source": [ + "# выведем текущее время в Москве\n", + "dt_moscow = datetime.now(pytz.timezone(\"Europe/Moscow\"))\n", + "print(dt_moscow)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "317feKJE3bq7", + "outputId": "70c99656-b3bc-4ed4-e397-769ee8785cff" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Europe/Moscow\n" + ] + } + ], + "source": [ + "# снова посмотрим на атрибут часового пояса\n", + "print(dt_moscow.tzinfo)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Y-V3a7ZUoVnR" + }, + "source": [ + "Timestamp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2TNSk7hEoU-D" + }, + "outputs": [], + "source": [ + "# получим timestamp текущего времени с помощью метода .timestamp()\n", + "timestamp = datetime.now().timestamp()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZCeooK3Tv7GH", + "outputId": "4342bd68-e605-428c-964c-d1c2e2d64c87" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1741696847.801249\n" + ] + } + ], + "source": [ + "# выведем количество секунд, прошедшее с 01.01.1970 до исполнения кода\n", + "print(timestamp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dmkfZp7Evj3W", + "outputId": "8c5155da-7415-4dc3-a521-71c95b546eb2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-03-11 12:40:47.801249\n" + ] + } + ], + "source": [ + "# вернем timestamp в прежний формат с помощью метода .fromtimestamp()\n", + "print(datetime.fromtimestamp(timestamp))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bpYReaGi6iQD" + }, + "source": [ + "Создание объекта datetime вручную" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6XVGJFxV5Nws", + "outputId": "628dfbb9-ea00-44b9-f0d1-8351a0013ea0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1991-02-20 00:00:00\n" + ] + } + ], + "source": [ + "# передадим объекту datetime 20 февраля 1991 года\n", + "hb = datetime(1991, 2, 20)\n", + "print(hb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oHPz-UYb7GzX", + "outputId": "2788e7c7-c4d1-475a-b0a4-ec2b389e7d31" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1991\n" + ] + } + ], + "source": [ + "# извлечем год с помощью атрибута year\n", + "print(hb.year)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fbtdZpi6wKrP", + "outputId": "17d9465f-9cba-47b5-bf22-b4f76a8e05b0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "667008000.0\n" + ] + } + ], + "source": [ + "# создадим timestamp\n", + "print(datetime.timestamp(hb))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dTkMHBejzz3r" + }, + "source": [ + "### Преобразование строки в объект datetime и обратно" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L0tp_c2bc_Ma" + }, + "source": [ + "Строка ➞ datetime через `.strptime()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hRuMEZe4wXi5", + "outputId": "721f628e-30ac-4d48-b138-922036f80097" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# дана строка с датой 2 декабря 2007 года и временем 12 часов 30 минут и 45 секунд\n", + "str_to_dt = \"2007-12-02 12:30:45\"\n", + "type(str_to_dt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sfM4trm008IJ", + "outputId": "43933acf-18d7-41e6-bd17-6741c4b6325a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2007-12-02 12:30:45\n", + "\n" + ] + } + ], + "source": [ + "# преобразуем ее в datetime с помощью метода .strptime()\n", + "res_dt = datetime.strptime(str_to_dt, \"%Y-%m-%d %H:%M:%S\")\n", + "\n", + "print(res_dt)\n", + "print(type(res_dt))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FE1YFP-ydtaG" + }, + "source": [ + "Datetime ➞ строка через `.strftime()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EuA4wuLbds4k", + "outputId": "40eb394b-c837-429a-b2ff-6b80f8bf8abe" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вначале создадим объект datetime и передадим ему 19 ноября 2002 года\n", + "dt_to_str = datetime(2002, 11, 19)\n", + "type(dt_to_str)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "r7WXYXRmc17Z", + "outputId": "9a996781-9e1f-40c0-b9ca-14c93db8f9d2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tuesday, November 19, 2002\n", + "\n" + ] + } + ], + "source": [ + "# преобразуем объект в строку в формате \"день недели, месяц число, год\"\n", + "res_str = datetime.strftime(dt_to_str, \"%A, %B %d, %Y\")\n", + "\n", + "print(res_str)\n", + "print(type(res_str))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "shboqnqE2qNH", + "outputId": "bc5c5981-a7a9-43ef-8002-b92e02b7e713" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Tuesday, November 19, 2002'" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# .strftime() можно применять непосредственно к объекту datetime\n", + "dt_to_str.strftime(\"%A, %B %d, %Y\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "gzLSq5M14BLJ", + "outputId": "391f050e-ffe2-4520-f63d-2389950cb9ce" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'2025-03-11'" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# можно и так\n", + "datetime.now().strftime(\"%Y-%m-%d\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "h0NiDIWKEU8B", + "outputId": "22a503bc-76b3-4c5c-9479-b68bbdc2e5ac" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Tue Mar 11 12:40:47 2025'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# а еще так\n", + "datetime.now().strftime(\"%c\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KbxUl-Xe7To-" + }, + "source": [ + "Форматирование даты и времени через `.strptime()` и `.strftime()`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tSi--Po_82L2" + }, + "source": [ + "|Код | Описание | Пример |\n", + "| --- | --- | --- |\n", + "| `%a` | Сокращенное название дня недели | Sun, Mon, … |\n", + "| `%A` | Полное название дня недели | Sunday, Monday, … |\n", + "| `%w` | День недели как число, Вс - 0, Пн - 1, ... Сб - 6 | 0, 1, …, 6 |\n", + "| `%d` | День месяца в виде числа с нулями | 01, 02, …, 31 |\n", + "| `%-d` | День месяца в виде числа без нулей | 1, 2, …, 31 |\n", + "| `%b` | Сокращенное название месяца | Jan, Feb, …, Dec |\n", + "| `%B` | Полное название месяца | January, February, … |\n", + "| `%m` | Месяц в виде числа с нулями | 01, 02, …, 12 |\n", + "| `%-m` | Месяц в виде числа без нулей | 1, 2, …, 12 |\n", + "| `%y` | Год без века как число с нулями | 00, 01, …, 99 |\n", + "| `%-y` | Год без века как число без нулей | 0, 1, …, 99 |\n", + "| `%Y` | Год с веком | 1999, 2019, ... |\n", + "| `%H` | Час (в 24-часовом формате) в виде числа с нулями | 00, 01, …, 23 |\n", + "| `%-H` | Час (в 24-часовом формате) в виде числа без нулей | 0, 1, …, 23 |\n", + "| `%I` | Час (12-часовой формат) в виде числа с нулями | 01, 02, …, 12 |\n", + "| `%-I` | Час (12-часовой формат) в виде числа без нулей | 1, 2, …, 12 |\n", + "| `%p` | AM или PM | AM, PM |\n", + "| `%M` | Минуты в виде числа с нулями | 00, 01, …, 59 |\n", + "| `%-M` | Минуты в виде числа без нулей | 0, 1, …, 59 |\n", + "| `%S` | Секунды в виде числа с нулями | 00, 01, …, 59 |\n", + "| `%-S` | Секунды в виде числа без нулей | 0, 1, …, 59 |\n", + "| `%j` | День года в виде числа с нулями | 001, 002, …, 366 |\n", + "| `%-j` | День года в виде числа без нулей | 1, 2, …, 366 |\n", + "| `%c` | Полная дата и время | Sun Nov 21 10:38:12 2021 |\n", + "| `%x` | Дата | 11/21/21 |\n", + "| `%X` | Время | 10:43:51 |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P3Mq8tzhgS7S" + }, + "source": [ + "### Сравнение и арифметика дат" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O49Iw50ggiet" + }, + "source": [ + "Сравнение дат" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ge_6VJYEfvM4" + }, + "outputs": [], + "source": [ + "# сравним две даты публикации работ Эйнштейна\n", + "date1 = datetime(1905, 6, 30) # \"К электродинамике движущихся тел\"\n", + "date2 = datetime(1916, 5, 11) # Общая теория относительности" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k1k7OhWJrVTu", + "outputId": "d8db0667-3908-4cd7-af89-a1b81dd8f493" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# большей считается более поздняя дата\n", + "date1 < date2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FXZTPJ_Nrebd", + "outputId": "d0307f84-ea01-4729-e8c1-d34fc72d670f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# обратное будет признано ложным\n", + "date1 > date2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9xI-dfch12iX" + }, + "source": [ + "Календарный и алфавитный порядок дат" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qdv9abZq1qs4", + "outputId": "87c2a586-b3f1-4b19-ea5a-7313eb81049f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если даты записаны в виде строки в формате ГГГГ.ММ.ДД,\n", + "# то мы можем их сравнивать, как если бы мы сравнивали объекты datetime\n", + "\n", + "# вначале запишем даты в виде строки и сравним их\n", + "date_1 = \"2007-12-02\"\n", + "date_2 = \"2002-11-19\"\n", + "print(date_1 > date_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tsldkuKj11Pj", + "outputId": "011d1d88-5ed2-4276-ea88-3ba7a2cc942e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# теперь в виде объекта datetime\n", + "print(datetime(2007, 12, 2) > datetime(2002, 11, 19))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eBCy30h6sGlR" + }, + "source": [ + "Промежуток времени и класс timedelta" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "C5TvKlGwrhBi", + "outputId": "2218b9c3-f9cc-4560-da95-a8043fef5763" + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'date2' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[8], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# если из большей даты вычесть меньшую, то мы получим временной промежуток между датами\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m diff \u001b[38;5;241m=\u001b[39m date2 \u001b[38;5;241m-\u001b[39m date1\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(diff)\n", + "\u001b[1;31mNameError\u001b[0m: name 'date2' is not defined" + ] + } + ], + "source": [ + "# если из большей даты вычесть меньшую, то мы получим\n", + "# временной промежуток между датами\n", + "diff = date2 - date1\n", + "print(diff)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "J-NTeeEhup9m", + "outputId": "a14a600b-1fd7-4a22-d982-2ccf4a96433c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.timedelta" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# при этом результат будет храниться в специальном объекте timedelta\n", + "type(diff)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aFNEGOxcsMF3", + "outputId": "59d0a6da-8dc1-4bda-e833-7c2b371367fd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3968\n" + ] + } + ], + "source": [ + "# атрибут days позволяет посмотреть только дни\n", + "print(diff.days)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EiefdopEvouW", + "outputId": "f6150ba8-aacf-4caf-a7b1-083e56d559a0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.timedelta(days=1)" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# объект timedelta можно также создать вручную\n", + "\n", + "\n", + "# а затем создадим объект timedelta продолжительностью 1 день\n", + "timedelta(days=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pyI8hHfnvg3N" + }, + "source": [ + "Арифметика дат" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nkffSQKXuGCU", + "outputId": "ec5d4ccc-9379-4218-b4eb-052b7428db8e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2070, 1, 1, 0, 0)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# смотрите, что получается,\n", + "# объединив объекты datetime и timedelta, мы можем \"путешествовать во времени\"\n", + "\n", + "# допустим сейчас 1 января 2070 года\n", + "future = datetime(2070, 1, 1)\n", + "future" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u33VnF9FwdOK", + "outputId": "db91b0e6-2d95-4005-e741-2d37cf5fb018" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(1900, 2, 12, 0, 0)" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# а мы хотим отправиться в 1 января 1900 года, т.е. на 170 лет назад\n", + "\n", + "# сначала просто умножим 365 дней на 170\n", + "time_travel = timedelta(days=365) * 170\n", + "\n", + "# а потом переместимся из будущего в прошлое\n", + "past = future - time_travel\n", + "\n", + "# к сожалению, мы немного \"не долетим\", потому что не учли високосные годы,\n", + "# в которых 366 дней\n", + "past" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IWpNXDwu9x3W", + "outputId": "c37fff62-1418-45ea-901f-6c1d2b594580" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "62050" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# мы пролетели 62050 дней\n", + "365 * 170" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sHiGMhj77tgo", + "outputId": "c33a8fd2-1e9f-4c0d-9a5d-c0ef0b406d9c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.timedelta(days=62092)" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# но мы уже умеем вычислять точное количество дней (с учетом високосных годов)\n", + "print(datetime(2070, 1, 1) - datetime(1900, 1, 1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MWZY2OQN8G-7", + "outputId": "88ad8f91-deac-45a0-cdf6-0450ca51b603" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(1900, 1, 1, 0, 0)" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# теперь снова совершим путешествие во времени,\n", + "# но на этот раз укажем правильное количество дней\n", + "time_travel = timedelta(days=62092)\n", + "\n", + "past = future - time_travel\n", + "past" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nkSTbuvexi9g", + "outputId": "d3489414-84f5-4ca6-fb73-8707dd0dc2ec" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jan 01, 2021\n", + "Jan 02, 2021\n", + "Jan 03, 2021\n", + "Jan 04, 2021\n", + "Jan 05, 2021\n", + "Jan 06, 2021\n", + "Jan 07, 2021\n", + "Jan 08, 2021\n", + "Jan 09, 2021\n", + "Jan 10, 2021\n" + ] + } + ], + "source": [ + "# иногда бывает удобно воспользоваться циклом while, чтобы создать перечень дат\n", + "# например, список праздничных новогодних дней в 2021 году\n", + "\n", + "cur_date = datetime(2021, 1, 1) # эту дату мы будем выводить\n", + "end_date = datetime(2021, 1, 10) # это граница (условие в цикле while)\n", + "\n", + "# пока верно условие\n", + "while cur_date <= end_date:\n", + "\n", + " # выведем cur_date в формате \"месяц число, год\"\n", + " print(cur_date.strftime(\"%b %d, %Y\"))\n", + "\n", + " # прибавим к выводимой дате один день\n", + " cur_date += timedelta(days=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3cOKyoFdBtZm" + }, + "source": [ + "### Дата и обработка ошибок" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lrt4HeBBB29k" + }, + "source": [ + "Конструкция try/except и оператор pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FKxevIYyB84_", + "outputId": "bec690e7-0d3d-4809-839c-477ccadd68f1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# пусть дан список чисел в строковом формате, и мы хотим посчитать их сумму\n", + "# предположим, буква \"а\" попала в список случайно\n", + "numbers = [\"5\", \"10\", \"a\", \"15\", \"10\"]\n", + "\n", + "# объявим переменную суммы\n", + "total = 0\n", + "\n", + "# пройдемся по числам\n", + "for number in numbers:\n", + "\n", + " # попробуем прибавить число к переменной total\n", + " try:\n", + " total += int(number)\n", + "\n", + " # если же этого сделать не удастся\n", + " except ValueError:\n", + " # перейдем к следующему числу\n", + " continue\n", + "\n", + "# выведем сумму\n", + "total" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uTWdPszlOnft", + "outputId": "690633b3-11f3-45b8-dc40-8ac507710aab" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Элемент 'a' обработать не удалось\n" + ] + }, + { + "data": { + "text/plain": [ + "40" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# напишем этот же код\n", + "total = 0\n", + "\n", + "for number in numbers:\n", + " try:\n", + " total += int(number)\n", + " except ValueError:\n", + " # но выведем предупреждение, если число прибавить не удалось\n", + " print(f\"Элемент '{number}' обработать не удалось\")\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "40mo2GvDUeuB" + }, + "source": [ + "Обработка нескольких форматов дат" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 426 + }, + "id": "Dk8u6vXsUd0V", + "outputId": "53afbaf6-51c5-4cdf-dc09-040a7a90584e" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"temp\",\n \"rows\": 12,\n \"fields\": [\n {\n \"column\": \"Date\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 12,\n \"samples\": [\n \"11-2002\",\n \"2002-10-01\",\n \"2002-01-01\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Temp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 16.202122943577113,\n \"min\": 44.1,\n \"max\": 88.8,\n \"num_unique_values\": 12,\n \"samples\": [\n 53.9,\n 64.1,\n 47.1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "temp" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateTemp
02002-01-0147.1
12002-0250.5
22002-03-154.9
32002-0466.1
42002-05-0172.7
52002-0683.2
62002-0788.8
72002-08-187.8
82002-09-0180.2
92002-10-0164.1
1011-200253.9
112002-12-0144.1
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Date Temp\n", + "0 2002-01-01 47.1\n", + "1 2002-02 50.5\n", + "2 2002-03-1 54.9\n", + "3 2002-04 66.1\n", + "4 2002-05-01 72.7\n", + "5 2002-06 83.2\n", + "6 2002-07 88.8\n", + "7 2002-08-1 87.8\n", + "8 2002-09-01 80.2\n", + "9 2002-10-01 64.1\n", + "10 11-2002 53.9\n", + "11 2002-12-01 44.1" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp = pd.read_csv(\"temperature.csv\")\n", + "temp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eJwEsl-KTOcI", + "outputId": "99997f56-4990-49d9-c38b-f1ff157b9ba4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2002-01-01 00:00:00\n", + "2002-02-01 00:00:00\n", + "2002-03-01 00:00:00\n", + "2002-04-01 00:00:00\n", + "2002-05-01 00:00:00\n", + "2002-06-01 00:00:00\n", + "2002-07-01 00:00:00\n", + "2002-08-01 00:00:00\n", + "2002-09-01 00:00:00\n", + "2002-10-01 00:00:00\n", + "2002-12-01 00:00:00\n", + "Не обработалось записей: 1\n" + ] + } + ], + "source": [ + "# предположим, проанализировав все форматы дат, мы выявили следующие шаблоны\n", + "formats = [\"%Y-%m-%d\", \"%Y-%m-%-d\", \"%Y-%m\"]\n", + "\n", + "# создадим счетчик для записей, которые не обработались\n", + "counter = 0\n", + "\n", + "# пройдемся в цикле по столбцу Date\n", + "for date_value in temp.Date:\n", + "\n", + " # затем пройдемся по известным нам форматам\n", + " for format_str in formats:\n", + "\n", + " # попробуем, применив каждый из форматов,\n", + " # преобразовать строку с датой в объект datetime\n", + " try:\n", + " print(datetime.strptime(date_value, format_str))\n", + " counter += 1\n", + "\n", + " # если что-то пошло не так\n", + " except ValueError:\n", + " # перейдем к следующему формату (второй цикл for) или записи (первый цикл for)\n", + " continue\n", + "\n", + "# посмотрим, сколько записей не обработалось\n", + "print(\"Не обработалось записей:\", len(temp) - counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 457 + }, + "id": "qLD4bzk8d0_x", + "outputId": "63230b62-db94-4291-8459-2bd066f3d97d" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"temp_parsed\",\n \"rows\": 12,\n \"fields\": [\n {\n \"column\": \"Date\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 12,\n \"samples\": [\n \"11-2002\",\n \"2002-10-01\",\n \"2002-01-01\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Temp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 16.202122943577113,\n \"min\": 44.1,\n \"max\": 88.8,\n \"num_unique_values\": 12,\n \"samples\": [\n 53.9,\n 64.1,\n 47.1\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe", + "variable_name": "temp_parsed" + }, + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Temp
Date
2002-01-0147.1
2002-0250.5
2002-03-154.9
2002-0466.1
2002-05-0172.7
2002-0683.2
2002-0788.8
2002-08-187.8
2002-09-0180.2
2002-10-0164.1
11-200253.9
2002-12-0144.1
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Temp\n", + "Date \n", + "2002-01-01 47.1\n", + "2002-02 50.5\n", + "2002-03-1 54.9\n", + "2002-04 66.1\n", + "2002-05-01 72.7\n", + "2002-06 83.2\n", + "2002-07 88.8\n", + "2002-08-1 87.8\n", + "2002-09-01 80.2\n", + "2002-10-01 64.1\n", + "11-2002 53.9\n", + "2002-12-01 44.1" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# воспользуемся решением \"из коробки\" библиотеки Pandas\n", + "# передадим функции read_csv() параметр parse_dates\n", + "temp_parsed = pd.read_csv(\n", + " \"temperature.csv\", index_col=\"Date\", parse_dates=True\n", + ")\n", + "temp_parsed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "t0J8yIEBe5RX", + "outputId": "40401823-db9a-4924-c778-2ddc87252c0a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "pandas.core.indexes.base.Index" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# индекс превратился в объект datetime\n", + "type(temp_parsed.index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6bc29scvIkez" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_05_datetime.py b/Python/makarov/chapter_05_datetime.py new file mode 100644 index 00000000..edb3d156 --- /dev/null +++ b/Python/makarov/chapter_05_datetime.py @@ -0,0 +1,345 @@ +"""Макаров. + +Дата и время в Питоне. +""" + +# ## Дата и время в Питоне + +# ### Модуль datetime + +# Импорт модуля и класса datetime + +# + +# импортируем весь модуль +# import datetime + +# чтобы получить доступ к функции now(), сначала обратимся к модулю, потом к классу +# print(datetime.datetime.now()) + +# для этого вначале импортируем соответствующий класс +# часто из модуля datetime удобнее импортировать только класс datetime +from datetime import datetime, timedelta + +# у нас есть данные по среднемесячной температуре в Нью-Йорке в 2002 году +import pandas as pd + +# для изменения часового пояса нужно импортировать модуль pytz +import pytz + +# - + +# и обращаться непосредственно к нему +print(datetime.now()) + +# Объект datetime и функция `now()` + +# поместим созданный с помощью функции now() объект datetime в переменную cur_dt +cur_dt = datetime.now() +print(cur_dt) + +# с помощью соответствующих атрибутов выведем каждый из компонентов объекта по отдельности +print( + cur_dt.year, + cur_dt.month, + cur_dt.day, + cur_dt.hour, + cur_dt.minute, + cur_dt.second, + cur_dt.microsecond, +) + +# также можно посмотреть на день недели +# метод .weekday() начинает индекс недели с нуля, .isoweekday() - с единицы +print(cur_dt.weekday(), cur_dt.isoweekday()) + +# посмотрим на часовой пояс с помощью атрибута tzinfo +print(cur_dt.tzinfo) + +# выведем текущее время в Москве +dt_moscow = datetime.now(pytz.timezone("Europe/Moscow")) +print(dt_moscow) + +# снова посмотрим на атрибут часового пояса +print(dt_moscow.tzinfo) + +# Timestamp + +# получим timestamp текущего времени с помощью метода .timestamp() +timestamp = datetime.now().timestamp() + +# выведем количество секунд, прошедшее с 01.01.1970 до исполнения кода +print(timestamp) + +# вернем timestamp в прежний формат с помощью метода .fromtimestamp() +print(datetime.fromtimestamp(timestamp)) + +# Создание объекта datetime вручную + +# передадим объекту datetime 20 февраля 1991 года +hb = datetime(1991, 2, 20) +print(hb) + +# извлечем год с помощью атрибута year +print(hb.year) + +# создадим timestamp +print(datetime.timestamp(hb)) + +# ### Преобразование строки в объект datetime и обратно + +# Строка ➞ datetime через `.strptime()` + +# дана строка с датой 2 декабря 2007 года и временем 12 часов 30 минут и 45 секунд +str_to_dt = "2007-12-02 12:30:45" +type(str_to_dt) + +# + +# преобразуем ее в datetime с помощью метода .strptime() +res_dt = datetime.strptime(str_to_dt, "%Y-%m-%d %H:%M:%S") + +print(res_dt) +print(type(res_dt)) +# - + +# Datetime ➞ строка через `.strftime()` + +# вначале создадим объект datetime и передадим ему 19 ноября 2002 года +dt_to_str = datetime(2002, 11, 19) +type(dt_to_str) + +# + +# преобразуем объект в строку в формате "день недели, месяц число, год" +res_str = datetime.strftime(dt_to_str, "%A, %B %d, %Y") + +print(res_str) +print(type(res_str)) +# - + +# .strftime() можно применять непосредственно к объекту datetime +dt_to_str.strftime("%A, %B %d, %Y") + +# можно и так +datetime.now().strftime("%Y-%m-%d") + +# а еще так +datetime.now().strftime("%c") + +# Форматирование даты и времени через `.strptime()` и `.strftime()` + +# |Код | Описание | Пример | +# | --- | --- | --- | +# | `%a` | Сокращенное название дня недели | Sun, Mon, … | +# | `%A` | Полное название дня недели | Sunday, Monday, … | +# | `%w` | День недели как число, Вс - 0, Пн - 1, ... Сб - 6 | 0, 1, …, 6 | +# | `%d` | День месяца в виде числа с нулями | 01, 02, …, 31 | +# | `%-d` | День месяца в виде числа без нулей | 1, 2, …, 31 | +# | `%b` | Сокращенное название месяца | Jan, Feb, …, Dec | +# | `%B` | Полное название месяца | January, February, … | +# | `%m` | Месяц в виде числа с нулями | 01, 02, …, 12 | +# | `%-m` | Месяц в виде числа без нулей | 1, 2, …, 12 | +# | `%y` | Год без века как число с нулями | 00, 01, …, 99 | +# | `%-y` | Год без века как число без нулей | 0, 1, …, 99 | +# | `%Y` | Год с веком | 1999, 2019, ... | +# | `%H` | Час (в 24-часовом формате) в виде числа с нулями | 00, 01, …, 23 | +# | `%-H` | Час (в 24-часовом формате) в виде числа без нулей | 0, 1, …, 23 | +# | `%I` | Час (12-часовой формат) в виде числа с нулями | 01, 02, …, 12 | +# | `%-I` | Час (12-часовой формат) в виде числа без нулей | 1, 2, …, 12 | +# | `%p` | AM или PM | AM, PM | +# | `%M` | Минуты в виде числа с нулями | 00, 01, …, 59 | +# | `%-M` | Минуты в виде числа без нулей | 0, 1, …, 59 | +# | `%S` | Секунды в виде числа с нулями | 00, 01, …, 59 | +# | `%-S` | Секунды в виде числа без нулей | 0, 1, …, 59 | +# | `%j` | День года в виде числа с нулями | 001, 002, …, 366 | +# | `%-j` | День года в виде числа без нулей | 1, 2, …, 366 | +# | `%c` | Полная дата и время | Sun Nov 21 10:38:12 2021 | +# | `%x` | Дата | 11/21/21 | +# | `%X` | Время | 10:43:51 | + +# ### Сравнение и арифметика дат + +# Сравнение дат + +# сравним две даты публикации работ Эйнштейна +date1 = datetime(1905, 6, 30) # "К электродинамике движущихся тел" +date2 = datetime(1916, 5, 11) # Общая теория относительности + +# большей считается более поздняя дата +date1 < date2 + +# обратное будет признано ложным +date1 > date2 + +# Календарный и алфавитный порядок дат + +# + +# если даты записаны в виде строки в формате ГГГГ.ММ.ДД, +# то мы можем их сравнивать, как если бы мы сравнивали объекты datetime + +# вначале запишем даты в виде строки и сравним их +date_1 = "2007-12-02" +date_2 = "2002-11-19" +print(date_1 > date_2) +# - + +# теперь в виде объекта datetime +print(datetime(2007, 12, 2) > datetime(2002, 11, 19)) + +# Промежуток времени и класс timedelta + +# если из большей даты вычесть меньшую, то мы получим +# временной промежуток между датами +diff = date2 - date1 +print(diff) + +# при этом результат будет храниться в специальном объекте timedelta +type(diff) + +# атрибут days позволяет посмотреть только дни +print(diff.days) + +# + +# объект timedelta можно также создать вручную + + +# а затем создадим объект timedelta продолжительностью 1 день +timedelta(days=1) +# - + +# Арифметика дат + +# + +# смотрите, что получается, +# объединив объекты datetime и timedelta, мы можем "путешествовать во времени" + +# допустим сейчас 1 января 2070 года +future = datetime(2070, 1, 1) +future + +# + +# а мы хотим отправиться в 1 января 1900 года, т.е. на 170 лет назад + +# сначала просто умножим 365 дней на 170 +time_travel = timedelta(days=365) * 170 + +# а потом переместимся из будущего в прошлое +past = future - time_travel + +# к сожалению, мы немного "не долетим", потому что не учли високосные годы, +# в которых 366 дней +past +# - + +# мы пролетели 62050 дней +365 * 170 + +# но мы уже умеем вычислять точное количество дней (с учетом високосных годов) +print(datetime(2070, 1, 1) - datetime(1900, 1, 1)) + +# + +# теперь снова совершим путешествие во времени, +# но на этот раз укажем правильное количество дней +time_travel = timedelta(days=62092) + +past = future - time_travel +past + +# + +# иногда бывает удобно воспользоваться циклом while, чтобы создать перечень дат +# например, список праздничных новогодних дней в 2021 году + +cur_date = datetime(2021, 1, 1) # эту дату мы будем выводить +end_date = datetime(2021, 1, 10) # это граница (условие в цикле while) + +# пока верно условие +while cur_date <= end_date: + + # выведем cur_date в формате "месяц число, год" + print(cur_date.strftime("%b %d, %Y")) + + # прибавим к выводимой дате один день + cur_date += timedelta(days=1) +# - + +# ### Дата и обработка ошибок + +# Конструкция try/except и оператор pass + +# + +# пусть дан список чисел в строковом формате, и мы хотим посчитать их сумму +# предположим, буква "а" попала в список случайно +numbers = ["5", "10", "a", "15", "10"] + +# объявим переменную суммы +total = 0 + +# пройдемся по числам +for number in numbers: + + # попробуем прибавить число к переменной total + try: + total += int(number) + + # если же этого сделать не удастся + except ValueError: + # перейдем к следующему числу + continue + +# выведем сумму +total + +# + +# напишем этот же код +total = 0 + +for number in numbers: + try: + total += int(number) + except ValueError: + # но выведем предупреждение, если число прибавить не удалось + print(f"Элемент '{number}' обработать не удалось") + +total +# - + +# Обработка нескольких форматов дат + +temp = pd.read_csv("temperature.csv") +temp + +# + +# предположим, проанализировав все форматы дат, мы выявили следующие шаблоны +formats = ["%Y-%m-%d", "%Y-%m-%-d", "%Y-%m"] + +# создадим счетчик для записей, которые не обработались +counter = 0 + +# пройдемся в цикле по столбцу Date +for date_value in temp.Date: + + # затем пройдемся по известным нам форматам + for format_str in formats: + + # попробуем, применив каждый из форматов, + # преобразовать строку с датой в объект datetime + try: + print(datetime.strptime(date_value, format_str)) + counter += 1 + + # если что-то пошло не так + except ValueError: + # перейдем к следующему формату (второй цикл for) или записи (первый цикл for) + continue + +# посмотрим, сколько записей не обработалось +print("Не обработалось записей:", len(temp) - counter) +# - + +# воспользуемся решением "из коробки" библиотеки Pandas +# передадим функции read_csv() параметр parse_dates +temp_parsed = pd.read_csv( + "temperature.csv", index_col="Date", parse_dates=True +) +temp_parsed + +# индекс превратился в объект datetime +type(temp_parsed.index) diff --git a/Python/makarov/chapter_06_functions.ipynb b/Python/makarov/chapter_06_functions.ipynb new file mode 100644 index 00000000..ab313b48 --- /dev/null +++ b/Python/makarov/chapter_06_functions.ipynb @@ -0,0 +1,2084 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Функции в Питоне.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0AoPSXyUTTLc" + }, + "source": [ + "## Функции в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W3QeADnwrI_g" + }, + "source": [ + "### Встроенные функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mmuSaN9wrMpL" + }, + "outputs": [], + "source": [ + "from collections.abc import ItemsView\n", + "from typing import Callable\n", + "\n", + "# импортируем библиотеки\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# перед вызовом функции нужно не забыть импортировать\n", + "# соответствующую библиотеку\n", + "import numpy as np\n", + "\n", + "# установим точку отсчета\n", + "np.random.seed(42)\n", + "# и снова сгенерируем данные о росте\n", + "# (как мы делали на восьмом занятии вводного курса)\n", + "height = list(np.round(np.random.normal(180, 10, 1000)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4R42tP5dJflr" + }, + "source": [ + "#### Параметры и аргументы функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 430 + }, + "id": "GZzCE-GXsD8H", + "outputId": "0a6e6454-e150-48b1-bc92-e54be971a738" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIm5JREFUeJzt3XtwVOXh//FP7hDIJg2QLBlCBC9c5CICjVsppZLJhRRB0hmhqOBQGGliB+I1FkG0841aqlYGoToKOhWrzAhy0VTkksgQUCgMghgBsYDJBgWT5SIhIc/vj/440xUwCSTsk837NXNm2HOenDzPHGPeOdndhBhjjAAAACwSGugJAAAA/BiBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA64YGewOWor69XeXm5YmJiFBISEujpAACARjDG6MSJE0pKSlJo6E/fI2mVgVJeXq7k5ORATwMAAFyGw4cPq1u3bj85plUGSkxMjKT/LtDlcgV4NgAAoDF8Pp+Sk5Od7+M/pVUGyvlf67hcLgIFAIBWpjFPz+BJsgAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsE54oCcAoGVd8+iaQE+hyb5+OjvQUwAQYNxBAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHWaFCiFhYUaOnSoYmJilJCQoLFjx6qsrMxvzIgRIxQSEuK33XfffX5jDh06pOzsbEVHRyshIUEPPfSQ6urqrnw1AAAgKIQ3ZXBxcbFyc3M1dOhQ1dXV6bHHHlN6ero+//xzdejQwRk3depUPfnkk87j6Oho59/nzp1Tdna23G63Nm/erIqKCt1zzz2KiIjQ//3f/zXDkgAAQGvXpEApKirye7xkyRIlJCRo+/btGj58uLM/Ojpabrf7ouf48MMP9fnnn+ujjz5SYmKibrrpJj311FN65JFH9MQTTygyMvIylgEAAILJFT0Hpbq6WpIUHx/vt//NN99U586d1a9fPxUUFOj06dPOsdLSUvXv31+JiYnOvoyMDPl8Pu3Zs+ein6empkY+n89vAwAAwatJd1D+V319vWbMmKFbb71V/fr1c/b/7ne/U0pKipKSkrRr1y498sgjKisr07vvvitJ8nq9fnEiyXns9Xov+rkKCws1d+7cy50qAABoZS47UHJzc7V7925t2rTJb/+0adOcf/fv319du3bVyJEjdeDAAV177bWX9bkKCgqUn5/vPPb5fEpOTr68iQMAAOtd1q948vLytHr1am3YsEHdunX7ybGpqamSpP3790uS3G63Kisr/cacf3yp561ERUXJ5XL5bQAAIHg1KVCMMcrLy9Py5cu1fv169ejRo8GP2blzpySpa9eukiSPx6PPPvtMR48edcasXbtWLpdLffv2bcp0AABAkGrSr3hyc3O1dOlSvffee4qJiXGeMxIbG6v27dvrwIEDWrp0qUaNGqVOnTpp165dmjlzpoYPH64BAwZIktLT09W3b1/dfffdevbZZ+X1ejVr1izl5uYqKiqq+VcIAABanSbdQVm4cKGqq6s1YsQIde3a1dnefvttSVJkZKQ++ugjpaenq3fv3nrggQeUk5OjVatWOecICwvT6tWrFRYWJo/Ho7vuukv33HOP3/umAACAtq1Jd1CMMT95PDk5WcXFxQ2eJyUlRe+//35TPjUAAGhD+Fs8AADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKwTHugJAK3JNY+uCfQUAKBN4A4KAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA64QHegIA8GPXPLom0FNosq+fzg70FICgwh0UAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHWaFCiFhYUaOnSoYmJilJCQoLFjx6qsrMxvzJkzZ5Sbm6tOnTqpY8eOysnJUWVlpd+YQ4cOKTs7W9HR0UpISNBDDz2kurq6K18NAAAICk0KlOLiYuXm5mrLli1au3atamtrlZ6erlOnTjljZs6cqVWrVmnZsmUqLi5WeXm5xo0b5xw/d+6csrOzdfbsWW3evFmvv/66lixZotmzZzffqgAAQKsWYowxl/vB3377rRISElRcXKzhw4erurpaXbp00dKlS/Xb3/5WkvTFF1+oT58+Ki0t1S233KIPPvhAv/nNb1ReXq7ExERJ0qJFi/TII4/o22+/VWRkZIOf1+fzKTY2VtXV1XK5XJc7faDJWuNbsOPq4K3ugYY15fv3FT0Hpbq6WpIUHx8vSdq+fbtqa2uVlpbmjOndu7e6d++u0tJSSVJpaan69+/vxIkkZWRkyOfzac+ePRf9PDU1NfL5fH4bAAAIXpcdKPX19ZoxY4ZuvfVW9evXT5Lk9XoVGRmpuLg4v7GJiYnyer3OmP+Nk/PHzx+7mMLCQsXGxjpbcnLy5U4bAAC0ApcdKLm5udq9e7f++c9/Nud8LqqgoEDV1dXOdvjw4Rb/nAAAIHDCL+eD8vLytHr1apWUlKhbt27OfrfbrbNnz6qqqsrvLkplZaXcbrcz5pNPPvE73/lX+Zwf82NRUVGKioq6nKkCAIBWqEl3UIwxysvL0/Lly7V+/Xr16NHD7/jgwYMVERGhdevWOfvKysp06NAheTweSZLH49Fnn32mo0ePOmPWrl0rl8ulvn37XslaAABAkGjSHZTc3FwtXbpU7733nmJiYpznjMTGxqp9+/aKjY3VlClTlJ+fr/j4eLlcLt1///3yeDy65ZZbJEnp6enq27ev7r77bj377LPyer2aNWuWcnNzuUsCAAAkNTFQFi5cKEkaMWKE3/7Fixdr8uTJkqTnn39eoaGhysnJUU1NjTIyMvTSSy85Y8PCwrR69WpNnz5dHo9HHTp00KRJk/Tkk09e2UoAAEDQuKL3QQkU3gcFgcL7oOBSeB8UoGFX7X1QAAAAWgKBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6zQ5UEpKSjR69GglJSUpJCREK1as8Ds+efJkhYSE+G2ZmZl+Y44fP66JEyfK5XIpLi5OU6ZM0cmTJ69oIQAAIHg0OVBOnTqlgQMHasGCBZcck5mZqYqKCmd76623/I5PnDhRe/bs0dq1a7V69WqVlJRo2rRpTZ89AAAISuFN/YCsrCxlZWX95JioqCi53e6LHtu7d6+Kior06aefasiQIZKk+fPna9SoUZo3b56SkpKaOiUAABBkWuQ5KBs3blRCQoJ69eql6dOn69ixY86x0tJSxcXFOXEiSWlpaQoNDdXWrVsver6amhr5fD6/DQAABK9mD5TMzEy98cYbWrdunZ555hkVFxcrKytL586dkyR5vV4lJCT4fUx4eLji4+Pl9Xoves7CwkLFxsY6W3JycnNPGwAAWKTJv+JpyPjx451/9+/fXwMGDNC1116rjRs3auTIkZd1zoKCAuXn5zuPfT4fkQIAQBBr8ZcZ9+zZU507d9b+/fslSW63W0ePHvUbU1dXp+PHj1/yeStRUVFyuVx+GwAACF4tHihHjhzRsWPH1LVrV0mSx+NRVVWVtm/f7oxZv3696uvrlZqa2tLTAQAArUCTf8Vz8uRJ526IJB08eFA7d+5UfHy84uPjNXfuXOXk5MjtduvAgQN6+OGHdd111ykjI0OS1KdPH2VmZmrq1KlatGiRamtrlZeXp/Hjx/MKHgAAIOky7qBs27ZNgwYN0qBBgyRJ+fn5GjRokGbPnq2wsDDt2rVLt99+u2644QZNmTJFgwcP1scff6yoqCjnHG+++aZ69+6tkSNHatSoURo2bJhefvnl5lsVAABo1Zp8B2XEiBEyxlzy+L/+9a8GzxEfH6+lS5c29VMDAIA2gr/FAwAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA64QHegJou655dE2gpwAAsBR3UAAAgHWaHCglJSUaPXq0kpKSFBISohUrVvgdN8Zo9uzZ6tq1q9q3b6+0tDTt27fPb8zx48c1ceJEuVwuxcXFacqUKTp58uQVLQQAAASPJgfKqVOnNHDgQC1YsOCix5999lm9+OKLWrRokbZu3aoOHTooIyNDZ86cccZMnDhRe/bs0dq1a7V69WqVlJRo2rRpl78KAAAQVJr8HJSsrCxlZWVd9JgxRi+88IJmzZqlMWPGSJLeeOMNJSYmasWKFRo/frz27t2roqIiffrppxoyZIgkaf78+Ro1apTmzZunpKSkK1gOAAAIBs36HJSDBw/K6/UqLS3N2RcbG6vU1FSVlpZKkkpLSxUXF+fEiSSlpaUpNDRUW7duveh5a2pq5PP5/DYAABC8mjVQvF6vJCkxMdFvf2JionPM6/UqISHB73h4eLji4+OdMT9WWFio2NhYZ0tOTm7OaQMAAMu0ilfxFBQUqLq62tkOHz4c6CkBAIAW1KyB4na7JUmVlZV++ysrK51jbrdbR48e9TteV1en48ePO2N+LCoqSi6Xy28DAADBq1kDpUePHnK73Vq3bp2zz+fzaevWrfJ4PJIkj8ejqqoqbd++3Rmzfv161dfXKzU1tTmnAwAAWqkmv4rn5MmT2r9/v/P44MGD2rlzp+Lj49W9e3fNmDFDf/7zn3X99derR48eevzxx5WUlKSxY8dKkvr06aPMzExNnTpVixYtUm1trfLy8jR+/HhewQOg1WqN74z89dPZgZ4CcElNDpRt27bp17/+tfM4Pz9fkjRp0iQtWbJEDz/8sE6dOqVp06apqqpKw4YNU1FRkdq1a+d8zJtvvqm8vDyNHDlSoaGhysnJ0YsvvtgMywEAAMEgxBhjAj2JpvL5fIqNjVV1dTXPR2nFWuNPnEAw4Q4KrramfP9uFa/iAQAAbQuBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6zR7oDzxxBMKCQnx23r37u0cP3PmjHJzc9WpUyd17NhROTk5qqysbO5pAACAVqxF7qDceOONqqiocLZNmzY5x2bOnKlVq1Zp2bJlKi4uVnl5ucaNG9cS0wAAAK1UeIucNDxcbrf7gv3V1dV69dVXtXTpUt12222SpMWLF6tPnz7asmWLbrnllpaYDgAAaGVa5A7Kvn37lJSUpJ49e2rixIk6dOiQJGn79u2qra1VWlqaM7Z3797q3r27SktLW2IqAACgFWr2OyipqalasmSJevXqpYqKCs2dO1e//OUvtXv3bnm9XkVGRiouLs7vYxITE+X1ei95zpqaGtXU1DiPfT5fc08bAABYpNkDJSsry/n3gAEDlJqaqpSUFL3zzjtq3779ZZ2zsLBQc+fOba4pAgAAy7X4y4zj4uJ0ww03aP/+/XK73Tp79qyqqqr8xlRWVl70OSvnFRQUqLq62tkOHz7cwrMGAACB1OKBcvLkSR04cEBdu3bV4MGDFRERoXXr1jnHy8rKdOjQIXk8nkueIyoqSi6Xy28DAADBq9l/xfPggw9q9OjRSklJUXl5uebMmaOwsDBNmDBBsbGxmjJlivLz8xUfHy+Xy6X7779fHo+HV/AAAABHswfKkSNHNGHCBB07dkxdunTRsGHDtGXLFnXp0kWS9Pzzzys0NFQ5OTmqqalRRkaGXnrppeaeBgAAaMVCjDEm0JNoKp/Pp9jYWFVXV/PrnlbsmkfXBHoKQJv29dPZgZ4C2pimfP/mb/EAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsE6zv1EbAKB1aI3vRcR7t7Qd3EEBAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYJD/QE0DyueXRNoKcAAECz4Q4KAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDrhgZ4AAACNdc2jawI9hSb7+unsQE+hVeIOCgAAsA6BAgAArEOgAAAA6/AclItojb/jBAAgmHAHBQAAWCeggbJgwQJdc801ateunVJTU/XJJ58EcjoAAMASAQuUt99+W/n5+ZozZ47+/e9/a+DAgcrIyNDRo0cDNSUAAGCJgAXKc889p6lTp+ree+9V3759tWjRIkVHR+u1114L1JQAAIAlAvIk2bNnz2r79u0qKChw9oWGhiotLU2lpaUXjK+pqVFNTY3zuLq6WpLk8/laZH71Nadb5LwAgLan+8xlgZ7CZdk9N6PZz3n++7YxpsGxAQmU7777TufOnVNiYqLf/sTERH3xxRcXjC8sLNTcuXMv2J+cnNxicwQAoC2LfaHlzn3ixAnFxsb+5JhW8TLjgoIC5efnO4/r6+t1/PhxderUSSEhIQGc2X9rMDk5WYcPH5bL5QroXAKhLa+/La9datvrb8trl9r2+tvy2qUrX78xRidOnFBSUlKDYwMSKJ07d1ZYWJgqKyv99ldWVsrtdl8wPioqSlFRUX774uLiWnKKTeZyudrkf6znteX1t+W1S217/W157VLbXn9bXrt0Zetv6M7JeQF5kmxkZKQGDx6sdevWOfvq6+u1bt06eTyeQEwJAABYJGC/4snPz9ekSZM0ZMgQ/fznP9cLL7ygU6dO6d577w3UlAAAgCUCFih33nmnvv32W82ePVter1c33XSTioqKLnjirO2ioqI0Z86cC34F1Va05fW35bVLbXv9bXntUttef1teu3R11x9iGvNaHwAAgKuIv8UDAACsQ6AAAADrECgAAMA6BAoAALAOgXIJJSUlGj16tJKSkhQSEqIVK1b4HZ88ebJCQkL8tszMTL8xx48f18SJE+VyuRQXF6cpU6bo5MmTV3EVl6ehtUvS3r17dfvttys2NlYdOnTQ0KFDdejQIef4mTNnlJubq06dOqljx47Kycm54I35bNXQ+n983c9vf/nLX5wxwXrtT548qby8PHXr1k3t27d3/tDn/wrma19ZWanJkycrKSlJ0dHRyszM1L59+/zGtNb1FxYWaujQoYqJiVFCQoLGjh2rsrIyvzGNWduhQ4eUnZ2t6OhoJSQk6KGHHlJdXd3VXEqTNWbtL7/8skaMGCGXy6WQkBBVVVVdcJ7W+nXf0PqPHz+u+++/X7169VL79u3VvXt3/fGPf3T+Lt55zX3tCZRLOHXqlAYOHKgFCxZcckxmZqYqKiqc7a233vI7PnHiRO3Zs0dr167V6tWrVVJSomnTprX01K9YQ2s/cOCAhg0bpt69e2vjxo3atWuXHn/8cbVr184ZM3PmTK1atUrLli1TcXGxysvLNW7cuKu1hCvS0Pr/95pXVFTotddeU0hIiHJycpwxwXrt8/PzVVRUpH/84x/au3evZsyYoby8PK1cudIZE6zX3hijsWPH6quvvtJ7772nHTt2KCUlRWlpaTp16pQzrrWuv7i4WLm5udqyZYvWrl2r2tpapaenN2lt586dU3Z2ts6ePavNmzfr9ddf15IlSzR79uxALKnRGrP206dPKzMzU4899tglz9Nav+4bWn95ebnKy8s1b9487d69W0uWLFFRUZGmTJninKNFrr1BgySZ5cuX++2bNGmSGTNmzCU/5vPPPzeSzKeffurs++CDD0xISIj55ptvWmimze9ia7/zzjvNXXfddcmPqaqqMhEREWbZsmXOvr179xpJprS0tKWm2iIutv4fGzNmjLntttucx8F87W+88Ubz5JNP+u27+eabzZ/+9CdjTHBf+7KyMiPJ7N6929l37tw506VLF/PKK68YY4Jr/UePHjWSTHFxsTGmcWt7//33TWhoqPF6vc6YhQsXGpfLZWpqaq7uAq7Aj9f+vzZs2GAkme+//95vf7B83Rvz0+s/75133jGRkZGmtrbWGNMy1547KFdg48aNSkhIUK9evTR9+nQdO3bMOVZaWqq4uDgNGTLE2ZeWlqbQ0FBt3bo1ENNtFvX19VqzZo1uuOEGZWRkKCEhQampqX63wrdv367a2lqlpaU5+3r37q3u3burtLQ0ALNuOZWVlVqzZo3fTxLBeu0l6Re/+IVWrlypb775RsYYbdiwQV9++aXS09MlBfe1r6mpkSS/O4WhoaGKiorSpk2bJAXX+s/fvo+Pj5fUuLWVlpaqf//+fm+4mZGRIZ/Ppz179lzF2V+ZH6+9MYLp674x66+urpbL5VJ4+H/f77Ulrj2BcpkyMzP1xhtvaN26dXrmmWdUXFysrKwsnTt3TpLk9XqVkJDg9zHh4eGKj4+X1+sNxJSbxdGjR3Xy5Ek9/fTTyszM1Icffqg77rhD48aNU3FxsaT/rj0yMvKCP+iYmJjYqtd+Ma+//rpiYmL8bnMH67WXpPnz56tv377q1q2bIiMjlZmZqQULFmj48OGSgvvan/9mXFBQoO+//15nz57VM888oyNHjqiiokJS8Ky/vr5eM2bM0K233qp+/fpJatzavF7vBe8Gfv5xa1n/xdbeGMHydd+Y9X/33Xd66qmn/H591RLXPmBvdd/ajR8/3vl3//79NWDAAF177bXauHGjRo4cGcCZtaz6+npJ0pgxYzRz5kxJ0k033aTNmzdr0aJF+tWvfhXI6V11r732miZOnOj3U3Uwmz9/vrZs2aKVK1cqJSVFJSUlys3NVVJSkt9P1sEoIiJC7777rqZMmaL4+HiFhYUpLS1NWVlZMkH2hty5ubnavXu3c2eoLWnLa5caXr/P51N2drb69u2rJ554okXnwh2UZtKzZ0917txZ+/fvlyS53W4dPXrUb0xdXZ2OHz8ut9sdiCk2i86dOys8PFx9+/b129+nTx/nVTxut1tnz5694FnulZWVrXrtP/bxxx+rrKxMv//97/32B+u1/+GHH/TYY4/pueee0+jRozVgwADl5eXpzjvv1Lx58yQF/7UfPHiwdu7cqaqqKlVUVKioqEjHjh1Tz549JQXH+vPy8rR69Wpt2LBB3bp1c/Y3Zm1ut/uCV/Wcf9wa1n+ptTdGMHzdN7T+EydOKDMzUzExMVq+fLkiIiKcYy1x7QmUZnLkyBEdO3ZMXbt2lSR5PB5VVVVp+/btzpj169ervr5eqampgZrmFYuMjNTQoUMveAnel19+qZSUFEn//Z94RESE1q1b5xwvKyvToUOH5PF4rup8W9Krr76qwYMHa+DAgX77g/Xa19bWqra2VqGh/v/bCAsLc+6stZVrHxsbqy5dumjfvn3atm2bxowZI6l1r98Yo7y8PC1fvlzr169Xjx49/I43Zm0ej0efffaZ3zfqtWvXyuVyXfBDjU0aWntjtOav+8as3+fzKT09XZGRkVq5cuUFd41b5Npf1lNr24ATJ06YHTt2mB07dhhJ5rnnnjM7duww//nPf8yJEyfMgw8+aEpLS83BgwfNRx99ZG6++WZz/fXXmzNnzjjnyMzMNIMGDTJbt241mzZtMtdff72ZMGFCAFfVOD+1dmOMeffdd01ERIR5+eWXzb59+8z8+fNNWFiY+fjjj51z3HfffaZ79+5m/fr1Ztu2bcbj8RiPxxOoJTVJQ+s3xpjq6moTHR1tFi5ceNFzBOu1/9WvfmVuvPFGs2HDBvPVV1+ZxYsXm3bt2pmXXnrJOUcwX/t33nnHbNiwwRw4cMCsWLHCpKSkmHHjxvmdo7Wuf/r06SY2NtZs3LjRVFRUONvp06edMQ2tra6uzvTr18+kp6ebnTt3mqKiItOlSxdTUFAQiCU1WmPWXlFRYXbs2GFeeeUVI8mUlJSYHTt2mGPHjjljWuvXfUPrr66uNqmpqaZ///5m//79fmPq6uqMMS1z7QmUSzj/UrIfb5MmTTKnT5826enppkuXLiYiIsKkpKSYqVOn+r28yhhjjh07ZiZMmGA6duxoXC6Xuffee82JEycCtKLG+6m1n/fqq6+a6667zrRr184MHDjQrFixwu8cP/zwg/nDH/5gfvazn5no6Ghzxx13mIqKiqu8ksvTmPX//e9/N+3btzdVVVUXPUewXvuKigozefJkk5SUZNq1a2d69epl/vrXv5r6+nrnHMF87f/2t7+Zbt26mYiICNO9e3cza9asC15C2VrXf7F1SzKLFy92xjRmbV9//bXJysoy7du3N507dzYPPPCA81JUWzVm7XPmzGlwTGv9um9o/Zf6upBkDh486Jynua99yP+fHAAAgDV4DgoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6/w8i5bjdJBsKNgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# теперь построим гистограмму передав ей два параметра,\n", + "# данные о росте и количество интервалов\n", + "# первый параметр у нас позиционный, второй - именованный\n", + "plt.hist(height, bins=10)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 430 + }, + "id": "If9utZiWZYeq", + "outputId": "47fde034-9efb-4986-ce67-ae2bf202e257" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIm5JREFUeJzt3XtwVOXh//FP7hDIJg2QLBlCBC9c5CICjVsppZLJhRRB0hmhqOBQGGliB+I1FkG0841aqlYGoToKOhWrzAhy0VTkksgQUCgMghgBsYDJBgWT5SIhIc/vj/440xUwCSTsk837NXNm2HOenDzPHGPeOdndhBhjjAAAACwSGugJAAAA/BiBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA64YGewOWor69XeXm5YmJiFBISEujpAACARjDG6MSJE0pKSlJo6E/fI2mVgVJeXq7k5ORATwMAAFyGw4cPq1u3bj85plUGSkxMjKT/LtDlcgV4NgAAoDF8Pp+Sk5Od7+M/pVUGyvlf67hcLgIFAIBWpjFPz+BJsgAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsE54oCcAoGVd8+iaQE+hyb5+OjvQUwAQYNxBAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHWaFCiFhYUaOnSoYmJilJCQoLFjx6qsrMxvzIgRIxQSEuK33XfffX5jDh06pOzsbEVHRyshIUEPPfSQ6urqrnw1AAAgKIQ3ZXBxcbFyc3M1dOhQ1dXV6bHHHlN6ero+//xzdejQwRk3depUPfnkk87j6Oho59/nzp1Tdna23G63Nm/erIqKCt1zzz2KiIjQ//3f/zXDkgAAQGvXpEApKirye7xkyRIlJCRo+/btGj58uLM/Ojpabrf7ouf48MMP9fnnn+ujjz5SYmKibrrpJj311FN65JFH9MQTTygyMvIylgEAAILJFT0Hpbq6WpIUHx/vt//NN99U586d1a9fPxUUFOj06dPOsdLSUvXv31+JiYnOvoyMDPl8Pu3Zs+ein6empkY+n89vAwAAwatJd1D+V319vWbMmKFbb71V/fr1c/b/7ne/U0pKipKSkrRr1y498sgjKisr07vvvitJ8nq9fnEiyXns9Xov+rkKCws1d+7cy50qAABoZS47UHJzc7V7925t2rTJb/+0adOcf/fv319du3bVyJEjdeDAAV177bWX9bkKCgqUn5/vPPb5fEpOTr68iQMAAOtd1q948vLytHr1am3YsEHdunX7ybGpqamSpP3790uS3G63Kisr/cacf3yp561ERUXJ5XL5bQAAIHg1KVCMMcrLy9Py5cu1fv169ejRo8GP2blzpySpa9eukiSPx6PPPvtMR48edcasXbtWLpdLffv2bcp0AABAkGrSr3hyc3O1dOlSvffee4qJiXGeMxIbG6v27dvrwIEDWrp0qUaNGqVOnTpp165dmjlzpoYPH64BAwZIktLT09W3b1/dfffdevbZZ+X1ejVr1izl5uYqKiqq+VcIAABanSbdQVm4cKGqq6s1YsQIde3a1dnefvttSVJkZKQ++ugjpaenq3fv3nrggQeUk5OjVatWOecICwvT6tWrFRYWJo/Ho7vuukv33HOP3/umAACAtq1Jd1CMMT95PDk5WcXFxQ2eJyUlRe+//35TPjUAAGhD+Fs8AADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKwTHugJAK3JNY+uCfQUAKBN4A4KAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA64QHegIA8GPXPLom0FNosq+fzg70FICgwh0UAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHWaFCiFhYUaOnSoYmJilJCQoLFjx6qsrMxvzJkzZ5Sbm6tOnTqpY8eOysnJUWVlpd+YQ4cOKTs7W9HR0UpISNBDDz2kurq6K18NAAAICk0KlOLiYuXm5mrLli1au3atamtrlZ6erlOnTjljZs6cqVWrVmnZsmUqLi5WeXm5xo0b5xw/d+6csrOzdfbsWW3evFmvv/66lixZotmzZzffqgAAQKsWYowxl/vB3377rRISElRcXKzhw4erurpaXbp00dKlS/Xb3/5WkvTFF1+oT58+Ki0t1S233KIPPvhAv/nNb1ReXq7ExERJ0qJFi/TII4/o22+/VWRkZIOf1+fzKTY2VtXV1XK5XJc7faDJWuNbsOPq4K3ugYY15fv3FT0Hpbq6WpIUHx8vSdq+fbtqa2uVlpbmjOndu7e6d++u0tJSSVJpaan69+/vxIkkZWRkyOfzac+ePRf9PDU1NfL5fH4bAAAIXpcdKPX19ZoxY4ZuvfVW9evXT5Lk9XoVGRmpuLg4v7GJiYnyer3OmP+Nk/PHzx+7mMLCQsXGxjpbcnLy5U4bAAC0ApcdKLm5udq9e7f++c9/Nud8LqqgoEDV1dXOdvjw4Rb/nAAAIHDCL+eD8vLytHr1apWUlKhbt27OfrfbrbNnz6qqqsrvLkplZaXcbrcz5pNPPvE73/lX+Zwf82NRUVGKioq6nKkCAIBWqEl3UIwxysvL0/Lly7V+/Xr16NHD7/jgwYMVERGhdevWOfvKysp06NAheTweSZLH49Fnn32mo0ePOmPWrl0rl8ulvn37XslaAABAkGjSHZTc3FwtXbpU7733nmJiYpznjMTGxqp9+/aKjY3VlClTlJ+fr/j4eLlcLt1///3yeDy65ZZbJEnp6enq27ev7r77bj377LPyer2aNWuWcnNzuUsCAAAkNTFQFi5cKEkaMWKE3/7Fixdr8uTJkqTnn39eoaGhysnJUU1NjTIyMvTSSy85Y8PCwrR69WpNnz5dHo9HHTp00KRJk/Tkk09e2UoAAEDQuKL3QQkU3gcFgcL7oOBSeB8UoGFX7X1QAAAAWgKBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6zQ5UEpKSjR69GglJSUpJCREK1as8Ds+efJkhYSE+G2ZmZl+Y44fP66JEyfK5XIpLi5OU6ZM0cmTJ69oIQAAIHg0OVBOnTqlgQMHasGCBZcck5mZqYqKCmd76623/I5PnDhRe/bs0dq1a7V69WqVlJRo2rRpTZ89AAAISuFN/YCsrCxlZWX95JioqCi53e6LHtu7d6+Kior06aefasiQIZKk+fPna9SoUZo3b56SkpKaOiUAABBkWuQ5KBs3blRCQoJ69eql6dOn69ixY86x0tJSxcXFOXEiSWlpaQoNDdXWrVsver6amhr5fD6/DQAABK9mD5TMzEy98cYbWrdunZ555hkVFxcrKytL586dkyR5vV4lJCT4fUx4eLji4+Pl9Xoves7CwkLFxsY6W3JycnNPGwAAWKTJv+JpyPjx451/9+/fXwMGDNC1116rjRs3auTIkZd1zoKCAuXn5zuPfT4fkQIAQBBr8ZcZ9+zZU507d9b+/fslSW63W0ePHvUbU1dXp+PHj1/yeStRUVFyuVx+GwAACF4tHihHjhzRsWPH1LVrV0mSx+NRVVWVtm/f7oxZv3696uvrlZqa2tLTAQAArUCTf8Vz8uRJ526IJB08eFA7d+5UfHy84uPjNXfuXOXk5MjtduvAgQN6+OGHdd111ykjI0OS1KdPH2VmZmrq1KlatGiRamtrlZeXp/Hjx/MKHgAAIOky7qBs27ZNgwYN0qBBgyRJ+fn5GjRokGbPnq2wsDDt2rVLt99+u2644QZNmTJFgwcP1scff6yoqCjnHG+++aZ69+6tkSNHatSoURo2bJhefvnl5lsVAABo1Zp8B2XEiBEyxlzy+L/+9a8GzxEfH6+lS5c29VMDAIA2gr/FAwAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA64QHegJou655dE2gpwAAsBR3UAAAgHWaHCglJSUaPXq0kpKSFBISohUrVvgdN8Zo9uzZ6tq1q9q3b6+0tDTt27fPb8zx48c1ceJEuVwuxcXFacqUKTp58uQVLQQAAASPJgfKqVOnNHDgQC1YsOCix5999lm9+OKLWrRokbZu3aoOHTooIyNDZ86cccZMnDhRe/bs0dq1a7V69WqVlJRo2rRpl78KAAAQVJr8HJSsrCxlZWVd9JgxRi+88IJmzZqlMWPGSJLeeOMNJSYmasWKFRo/frz27t2roqIiffrppxoyZIgkaf78+Ro1apTmzZunpKSkK1gOAAAIBs36HJSDBw/K6/UqLS3N2RcbG6vU1FSVlpZKkkpLSxUXF+fEiSSlpaUpNDRUW7duveh5a2pq5PP5/DYAABC8mjVQvF6vJCkxMdFvf2JionPM6/UqISHB73h4eLji4+OdMT9WWFio2NhYZ0tOTm7OaQMAAMu0ilfxFBQUqLq62tkOHz4c6CkBAIAW1KyB4na7JUmVlZV++ysrK51jbrdbR48e9TteV1en48ePO2N+LCoqSi6Xy28DAADBq1kDpUePHnK73Vq3bp2zz+fzaevWrfJ4PJIkj8ejqqoqbd++3Rmzfv161dfXKzU1tTmnAwAAWqkmv4rn5MmT2r9/v/P44MGD2rlzp+Lj49W9e3fNmDFDf/7zn3X99derR48eevzxx5WUlKSxY8dKkvr06aPMzExNnTpVixYtUm1trfLy8jR+/HhewQOg1WqN74z89dPZgZ4CcElNDpRt27bp17/+tfM4Pz9fkjRp0iQtWbJEDz/8sE6dOqVp06apqqpKw4YNU1FRkdq1a+d8zJtvvqm8vDyNHDlSoaGhysnJ0YsvvtgMywEAAMEgxBhjAj2JpvL5fIqNjVV1dTXPR2nFWuNPnEAw4Q4KrramfP9uFa/iAQAAbQuBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6zR7oDzxxBMKCQnx23r37u0cP3PmjHJzc9WpUyd17NhROTk5qqysbO5pAACAVqxF7qDceOONqqiocLZNmzY5x2bOnKlVq1Zp2bJlKi4uVnl5ucaNG9cS0wAAAK1UeIucNDxcbrf7gv3V1dV69dVXtXTpUt12222SpMWLF6tPnz7asmWLbrnllpaYDgAAaGVa5A7Kvn37lJSUpJ49e2rixIk6dOiQJGn79u2qra1VWlqaM7Z3797q3r27SktLW2IqAACgFWr2OyipqalasmSJevXqpYqKCs2dO1e//OUvtXv3bnm9XkVGRiouLs7vYxITE+X1ei95zpqaGtXU1DiPfT5fc08bAABYpNkDJSsry/n3gAEDlJqaqpSUFL3zzjtq3779ZZ2zsLBQc+fOba4pAgAAy7X4y4zj4uJ0ww03aP/+/XK73Tp79qyqqqr8xlRWVl70OSvnFRQUqLq62tkOHz7cwrMGAACB1OKBcvLkSR04cEBdu3bV4MGDFRERoXXr1jnHy8rKdOjQIXk8nkueIyoqSi6Xy28DAADBq9l/xfPggw9q9OjRSklJUXl5uebMmaOwsDBNmDBBsbGxmjJlivLz8xUfHy+Xy6X7779fHo+HV/AAAABHswfKkSNHNGHCBB07dkxdunTRsGHDtGXLFnXp0kWS9Pzzzys0NFQ5OTmqqalRRkaGXnrppeaeBgAAaMVCjDEm0JNoKp/Pp9jYWFVXV/PrnlbsmkfXBHoKQJv29dPZgZ4C2pimfP/mb/EAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsE6zv1EbAKB1aI3vRcR7t7Qd3EEBAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYJD/QE0DyueXRNoKcAAECz4Q4KAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDrhgZ4AAACNdc2jawI9hSb7+unsQE+hVeIOCgAAsA6BAgAArEOgAAAA6/AclItojb/jBAAgmHAHBQAAWCeggbJgwQJdc801ateunVJTU/XJJ58EcjoAAMASAQuUt99+W/n5+ZozZ47+/e9/a+DAgcrIyNDRo0cDNSUAAGCJgAXKc889p6lTp+ree+9V3759tWjRIkVHR+u1114L1JQAAIAlAvIk2bNnz2r79u0qKChw9oWGhiotLU2lpaUXjK+pqVFNTY3zuLq6WpLk8/laZH71Nadb5LwAgLan+8xlgZ7CZdk9N6PZz3n++7YxpsGxAQmU7777TufOnVNiYqLf/sTERH3xxRcXjC8sLNTcuXMv2J+cnNxicwQAoC2LfaHlzn3ixAnFxsb+5JhW8TLjgoIC5efnO4/r6+t1/PhxderUSSEhIQGc2X9rMDk5WYcPH5bL5QroXAKhLa+/La9datvrb8trl9r2+tvy2qUrX78xRidOnFBSUlKDYwMSKJ07d1ZYWJgqKyv99ldWVsrtdl8wPioqSlFRUX774uLiWnKKTeZyudrkf6znteX1t+W1S217/W157VLbXn9bXrt0Zetv6M7JeQF5kmxkZKQGDx6sdevWOfvq6+u1bt06eTyeQEwJAABYJGC/4snPz9ekSZM0ZMgQ/fznP9cLL7ygU6dO6d577w3UlAAAgCUCFih33nmnvv32W82ePVter1c33XSTioqKLnjirO2ioqI0Z86cC34F1Va05fW35bVLbXv9bXntUttef1teu3R11x9iGvNaHwAAgKuIv8UDAACsQ6AAAADrECgAAMA6BAoAALAOgXIJJSUlGj16tJKSkhQSEqIVK1b4HZ88ebJCQkL8tszMTL8xx48f18SJE+VyuRQXF6cpU6bo5MmTV3EVl6ehtUvS3r17dfvttys2NlYdOnTQ0KFDdejQIef4mTNnlJubq06dOqljx47Kycm54I35bNXQ+n983c9vf/nLX5wxwXrtT548qby8PHXr1k3t27d3/tDn/wrma19ZWanJkycrKSlJ0dHRyszM1L59+/zGtNb1FxYWaujQoYqJiVFCQoLGjh2rsrIyvzGNWduhQ4eUnZ2t6OhoJSQk6KGHHlJdXd3VXEqTNWbtL7/8skaMGCGXy6WQkBBVVVVdcJ7W+nXf0PqPHz+u+++/X7169VL79u3VvXt3/fGPf3T+Lt55zX3tCZRLOHXqlAYOHKgFCxZcckxmZqYqKiqc7a233vI7PnHiRO3Zs0dr167V6tWrVVJSomnTprX01K9YQ2s/cOCAhg0bpt69e2vjxo3atWuXHn/8cbVr184ZM3PmTK1atUrLli1TcXGxysvLNW7cuKu1hCvS0Pr/95pXVFTotddeU0hIiHJycpwxwXrt8/PzVVRUpH/84x/au3evZsyYoby8PK1cudIZE6zX3hijsWPH6quvvtJ7772nHTt2KCUlRWlpaTp16pQzrrWuv7i4WLm5udqyZYvWrl2r2tpapaenN2lt586dU3Z2ts6ePavNmzfr9ddf15IlSzR79uxALKnRGrP206dPKzMzU4899tglz9Nav+4bWn95ebnKy8s1b9487d69W0uWLFFRUZGmTJninKNFrr1BgySZ5cuX++2bNGmSGTNmzCU/5vPPPzeSzKeffurs++CDD0xISIj55ptvWmimze9ia7/zzjvNXXfddcmPqaqqMhEREWbZsmXOvr179xpJprS0tKWm2iIutv4fGzNmjLntttucx8F87W+88Ubz5JNP+u27+eabzZ/+9CdjTHBf+7KyMiPJ7N6929l37tw506VLF/PKK68YY4Jr/UePHjWSTHFxsTGmcWt7//33TWhoqPF6vc6YhQsXGpfLZWpqaq7uAq7Aj9f+vzZs2GAkme+//95vf7B83Rvz0+s/75133jGRkZGmtrbWGNMy1547KFdg48aNSkhIUK9evTR9+nQdO3bMOVZaWqq4uDgNGTLE2ZeWlqbQ0FBt3bo1ENNtFvX19VqzZo1uuOEGZWRkKCEhQampqX63wrdv367a2lqlpaU5+3r37q3u3burtLQ0ALNuOZWVlVqzZo3fTxLBeu0l6Re/+IVWrlypb775RsYYbdiwQV9++aXS09MlBfe1r6mpkSS/O4WhoaGKiorSpk2bJAXX+s/fvo+Pj5fUuLWVlpaqf//+fm+4mZGRIZ/Ppz179lzF2V+ZH6+9MYLp674x66+urpbL5VJ4+H/f77Ulrj2BcpkyMzP1xhtvaN26dXrmmWdUXFysrKwsnTt3TpLk9XqVkJDg9zHh4eGKj4+X1+sNxJSbxdGjR3Xy5Ek9/fTTyszM1Icffqg77rhD48aNU3FxsaT/rj0yMvKCP+iYmJjYqtd+Ma+//rpiYmL8bnMH67WXpPnz56tv377q1q2bIiMjlZmZqQULFmj48OGSgvvan/9mXFBQoO+//15nz57VM888oyNHjqiiokJS8Ky/vr5eM2bM0K233qp+/fpJatzavF7vBe8Gfv5xa1n/xdbeGMHydd+Y9X/33Xd66qmn/H591RLXPmBvdd/ajR8/3vl3//79NWDAAF177bXauHGjRo4cGcCZtaz6+npJ0pgxYzRz5kxJ0k033aTNmzdr0aJF+tWvfhXI6V11r732miZOnOj3U3Uwmz9/vrZs2aKVK1cqJSVFJSUlys3NVVJSkt9P1sEoIiJC7777rqZMmaL4+HiFhYUpLS1NWVlZMkH2hty5ubnavXu3c2eoLWnLa5caXr/P51N2drb69u2rJ554okXnwh2UZtKzZ0917txZ+/fvlyS53W4dPXrUb0xdXZ2OHz8ut9sdiCk2i86dOys8PFx9+/b129+nTx/nVTxut1tnz5694FnulZWVrXrtP/bxxx+rrKxMv//97/32B+u1/+GHH/TYY4/pueee0+jRozVgwADl5eXpzjvv1Lx58yQF/7UfPHiwdu7cqaqqKlVUVKioqEjHjh1Tz549JQXH+vPy8rR69Wpt2LBB3bp1c/Y3Zm1ut/uCV/Wcf9wa1n+ptTdGMHzdN7T+EydOKDMzUzExMVq+fLkiIiKcYy1x7QmUZnLkyBEdO3ZMXbt2lSR5PB5VVVVp+/btzpj169ervr5eqampgZrmFYuMjNTQoUMveAnel19+qZSUFEn//Z94RESE1q1b5xwvKyvToUOH5PF4rup8W9Krr76qwYMHa+DAgX77g/Xa19bWqra2VqGh/v/bCAsLc+6stZVrHxsbqy5dumjfvn3atm2bxowZI6l1r98Yo7y8PC1fvlzr169Xjx49/I43Zm0ej0efffaZ3zfqtWvXyuVyXfBDjU0aWntjtOav+8as3+fzKT09XZGRkVq5cuUFd41b5Npf1lNr24ATJ06YHTt2mB07dhhJ5rnnnjM7duww//nPf8yJEyfMgw8+aEpLS83BgwfNRx99ZG6++WZz/fXXmzNnzjjnyMzMNIMGDTJbt241mzZtMtdff72ZMGFCAFfVOD+1dmOMeffdd01ERIR5+eWXzb59+8z8+fNNWFiY+fjjj51z3HfffaZ79+5m/fr1Ztu2bcbj8RiPxxOoJTVJQ+s3xpjq6moTHR1tFi5ceNFzBOu1/9WvfmVuvPFGs2HDBvPVV1+ZxYsXm3bt2pmXXnrJOUcwX/t33nnHbNiwwRw4cMCsWLHCpKSkmHHjxvmdo7Wuf/r06SY2NtZs3LjRVFRUONvp06edMQ2tra6uzvTr18+kp6ebnTt3mqKiItOlSxdTUFAQiCU1WmPWXlFRYXbs2GFeeeUVI8mUlJSYHTt2mGPHjjljWuvXfUPrr66uNqmpqaZ///5m//79fmPq6uqMMS1z7QmUSzj/UrIfb5MmTTKnT5826enppkuXLiYiIsKkpKSYqVOn+r28yhhjjh07ZiZMmGA6duxoXC6Xuffee82JEycCtKLG+6m1n/fqq6+a6667zrRr184MHDjQrFixwu8cP/zwg/nDH/5gfvazn5no6Ghzxx13mIqKiqu8ksvTmPX//e9/N+3btzdVVVUXPUewXvuKigozefJkk5SUZNq1a2d69epl/vrXv5r6+nrnHMF87f/2t7+Zbt26mYiICNO9e3cza9asC15C2VrXf7F1SzKLFy92xjRmbV9//bXJysoy7du3N507dzYPPPCA81JUWzVm7XPmzGlwTGv9um9o/Zf6upBkDh486Jynua99yP+fHAAAgDV4DgoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6/w8i5bjdJBsKNgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# первый параметр можно также сделать именованным (данные обозначаются через x)\n", + "# и тогда порядок параметров можно менять\n", + "plt.hist(bins=10, x=height)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 430 + }, + "id": "5rM2p7inZ4gh", + "outputId": "da7b573e-eaea-437a-8400-6d9cf7c44da5" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIm5JREFUeJzt3XtwVOXh//FP7hDIJg2QLBlCBC9c5CICjVsppZLJhRRB0hmhqOBQGGliB+I1FkG0841aqlYGoToKOhWrzAhy0VTkksgQUCgMghgBsYDJBgWT5SIhIc/vj/440xUwCSTsk837NXNm2HOenDzPHGPeOdndhBhjjAAAACwSGugJAAAA/BiBAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA64YGewOWor69XeXm5YmJiFBISEujpAACARjDG6MSJE0pKSlJo6E/fI2mVgVJeXq7k5ORATwMAAFyGw4cPq1u3bj85plUGSkxMjKT/LtDlcgV4NgAAoDF8Pp+Sk5Od7+M/pVUGyvlf67hcLgIFAIBWpjFPz+BJsgAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsE54oCcAoGVd8+iaQE+hyb5+OjvQUwAQYNxBAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHUIFAAAYB0CBQAAWIdAAQAA1iFQAACAdQgUAABgHQIFAABYh0ABAADWIVAAAIB1CBQAAGAdAgUAAFiHQAEAANYhUAAAgHWaFCiFhYUaOnSoYmJilJCQoLFjx6qsrMxvzIgRIxQSEuK33XfffX5jDh06pOzsbEVHRyshIUEPPfSQ6urqrnw1AAAgKIQ3ZXBxcbFyc3M1dOhQ1dXV6bHHHlN6ero+//xzdejQwRk3depUPfnkk87j6Oho59/nzp1Tdna23G63Nm/erIqKCt1zzz2KiIjQ//3f/zXDkgAAQGvXpEApKirye7xkyRIlJCRo+/btGj58uLM/Ojpabrf7ouf48MMP9fnnn+ujjz5SYmKibrrpJj311FN65JFH9MQTTygyMvIylgEAAILJFT0Hpbq6WpIUHx/vt//NN99U586d1a9fPxUUFOj06dPOsdLSUvXv31+JiYnOvoyMDPl8Pu3Zs+ein6empkY+n89vAwAAwatJd1D+V319vWbMmKFbb71V/fr1c/b/7ne/U0pKipKSkrRr1y498sgjKisr07vvvitJ8nq9fnEiyXns9Xov+rkKCws1d+7cy50qAABoZS47UHJzc7V7925t2rTJb/+0adOcf/fv319du3bVyJEjdeDAAV177bWX9bkKCgqUn5/vPPb5fEpOTr68iQMAAOtd1q948vLytHr1am3YsEHdunX7ybGpqamSpP3790uS3G63Kisr/cacf3yp561ERUXJ5XL5bQAAIHg1KVCMMcrLy9Py5cu1fv169ejRo8GP2blzpySpa9eukiSPx6PPPvtMR48edcasXbtWLpdLffv2bcp0AABAkGrSr3hyc3O1dOlSvffee4qJiXGeMxIbG6v27dvrwIEDWrp0qUaNGqVOnTpp165dmjlzpoYPH64BAwZIktLT09W3b1/dfffdevbZZ+X1ejVr1izl5uYqKiqq+VcIAABanSbdQVm4cKGqq6s1YsQIde3a1dnefvttSVJkZKQ++ugjpaenq3fv3nrggQeUk5OjVatWOecICwvT6tWrFRYWJo/Ho7vuukv33HOP3/umAACAtq1Jd1CMMT95PDk5WcXFxQ2eJyUlRe+//35TPjUAAGhD+Fs8AADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKwTHugJAK3JNY+uCfQUAKBN4A4KAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA6xAoAADAOgQKAACwDoECAACsQ6AAAADrECgAAMA6BAoAALAOgQIAAKxDoAAAAOsQKAAAwDoECgAAsA6BAgAArEOgAAAA64QHegIA8GPXPLom0FNosq+fzg70FICgwh0UAABgHQIFAABYh0ABAADWIVAAAIB1CBQA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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов),\n", + "# а значит, этот параметр можно не указывать\n", + "plt.hist(height)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "t6UAoUmwag2c", + "outputId": "ef4a2fe2-25e4-4cb4-de2d-e55464101797" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Первая строка\n", + "\n", + "Третья строка\n" + ] + } + ], + "source": [ + "# функция может не принимать параметров\n", + "print(\"Первая строка\")\n", + "print()\n", + "print(\"Третья строка\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-QM-A2zzJhIn" + }, + "source": [ + "#### Функции и методы" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "o_fZPD7XJjFy", + "outputId": "80d76b59-0838-44a2-a700-873b19e9e02f" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Machine Learning'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# дана строка\n", + "some_string = \"machine learning\"\n", + "\n", + "# применим метод .title()\n", + "some_string.title()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 176 + }, + "id": "uVPQU3PoKwpR", + "outputId": "72d66e92-3184-4c44-a758-651f7f13b9a7" + }, + "outputs": [], + "source": [ + "# к списку\n", + "some_list: list[str] = [\"machine\", \"learning\"]\n", + "\n", + "# этот метод не применить\n", + "# some_list.title()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lIM5gFo4TbUg" + }, + "source": [ + "### Собственные функции в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CgGpEK0AwM2k" + }, + "source": [ + "#### Объявление и вызов функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IjjLVSbWqDpE" + }, + "outputs": [], + "source": [ + "# создадим функцию, которая удваивает любое передаваемое ей значение\n", + "\n", + "\n", + "def double(x_arg: int) -> int:\n", + " \"\"\"Умножает переданное число на 2.\"\"\"\n", + " res = x_arg * 2\n", + " return res" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EW6-h1F8ufBG", + "outputId": "115c5e57-2e1c-4102-ff92-ddade6454602" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и вызовем ее, передав число 2\n", + "double(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mS-C-sJePhVf" + }, + "source": [ + "#### Пустое тело функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "40pnqSnoTR0Z" + }, + "outputs": [], + "source": [ + "# тело функции не может быть пустым\n", + "\n", + "\n", + "def only_return() -> None:\n", + " \"\"\"Функция с одним return.\"\"\"\n", + " # нужно либо указать ключевое слово return\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HuYLHkX7TZnt" + }, + "outputs": [], + "source": [ + "only_return()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tihYzRyawYD8" + }, + "outputs": [], + "source": [ + "# либо оператор pass\n", + "\n", + "\n", + "# def only_pass() -> None:\n", + "# \"\"\"Функция ничего не делает.\"\"\"\n", + "# pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dP_fIG04TLad" + }, + "outputs": [], + "source": [ + "# only_pass()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u06oHGCNK6Rm", + "outputId": "48a7322c-cc8c-455f-f8a3-445c9619b9c7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "# такая функция вернет тип данных None (отсутствие значения)\n", + "# print(only_return()) -> None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "08Pbq-5VREZk" + }, + "source": [ + "#### Функция print() вместо return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Jaj1wdaAYFF-" + }, + "outputs": [], + "source": [ + "# можно использовать print(), но есть нюансы (см. на странице урока)\n", + "\n", + "\n", + "def double_print(x_arg: int) -> None:\n", + " \"\"\"Функция умножает переданное число на 2 и выводит результат в консоль.\"\"\"\n", + " res = x_arg * 2\n", + " print(res)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XgtVgq_HYKyh", + "outputId": "53140a9c-9829-4d6c-dcd9-863b9811b037" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n" + ] + } + ], + "source": [ + "double_print(5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zmJv5y8dLUJK" + }, + "source": [ + "#### Параметры собственных функций" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uhOnVbWhuis_" + }, + "outputs": [], + "source": [ + "# объявим функцию с параметрами x и y\n", + "\n", + "\n", + "def calc_sum(x_arg: int, y_arg: int) -> int:\n", + " \"\"\"Функция складывает два целых числа.\"\"\"\n", + " # и выведем их сумму\n", + " return x_arg + y_arg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KTEhuHaYwWLQ", + "outputId": "2e3085e6-fe3c-456b-d4a0-e7e9a3ad46ac" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вызовем эту функцию с одним позиционным и одним именованным параметром\n", + "calc_sum(1, y_arg=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "baCYb7BbT46W", + "outputId": "a46fc104-cdc3-49cc-ec5c-1ccd7894905f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# параметрам функции можно задать аргументы по умолчанию\n", + "\n", + "\n", + "def calc_sum_default(x_arg: int = 1, y_arg: int = 2) -> int:\n", + " \"\"\"Функция складывает два целых числа.\"\"\"\n", + " return x_arg + y_arg\n", + "\n", + "\n", + "# и при вызове тогда их указывать не обязательно\n", + "calc_sum_default()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2DQF4G9w_JUW", + "outputId": "96859e9d-1785-4d78-81f2-9f2b19ccb072" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some string\n" + ] + } + ], + "source": [ + "# функция может не иметь параметров\n", + "\n", + "\n", + "def print_string() -> None:\n", + " \"\"\"Функция выводит строку 'Some string'.\"\"\"\n", + " print(\"Some string\")\n", + "\n", + "\n", + "print_string()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jZABsfhPx9wd" + }, + "source": [ + "#### Аннотация функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TAoI_kYPyB44" + }, + "outputs": [], + "source": [ + "# укажем, что на входе функция принимает тип float, а возвращает int\n", + "# значение 3,5 - это значение параметра x по умолчанию\n", + "\n", + "\n", + "def f(x_arg: float = 3.5) -> int:\n", + " \"\"\"Функция приводит переданный float-аргумент к целочисленному типу.\"\"\"\n", + " return int(x_arg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QaNrg6kZyW8Y", + "outputId": "4afa6d5c-eaa7-4651-e9c1-aa704dd19f3e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'x': float, 'return': int}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# желаемый тип данных можно посмотреть через атрибут __annotations__\n", + "f.__annotations__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-iKjBO501oYJ", + "outputId": "d6e6c8d3-89c3-4d12-9979-f323f45b82c4" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вызовем функцию без параметров\n", + "f()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ziFM30EY2Auj" + }, + "outputs": [], + "source": [ + "# сохраним аннотации, но изменим суть функции\n", + "# def f(x: float) -> int:\n", + "# # теперь вместо int она будет возвращать float\n", + "# return float(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AdpR-rb92Qmn", + "outputId": "e5f3fb8c-5172-46e2-af70-ccfedce19921" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вновь вызовем функцию, передав ей на входе int, и ожидая на выходе получить float\n", + "f(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vlOHQZGZ_ESX" + }, + "source": [ + "#### Дополнительные возможности" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QittQ3qiK8S4", + "outputId": "8b2c99f0-f61d-4fe5-e015-22c1762ddea6" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вызов функции можно совмещать с арифметическими\n", + "result = calc_sum(1, 2) * 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "83RysL9MMQCw", + "outputId": "45598627-36e6-47f3-d456-9e650ba74450" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и логическими операциями\n", + "result = calc_sum(1, 2) > 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "YL1UrXNFb84A", + "outputId": "395474c0-41a3-4146-a978-2e450c2ab8b7" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'P'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# можно и так\n", + "\n", + "\n", + "def first_letter() -> str:\n", + " \"\"\"Функция возвращает строку 'Python'.\"\"\"\n", + " return \"Python\"\n", + "\n", + "\n", + "print(first_letter()[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WC59uMaoWy8u", + "outputId": "6773f697-f6b8-4cf9-edaa-3ddcff1ac057" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Введите число: 5\n" + ] + }, + { + "data": { + "text/plain": [ + "25" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# функция может не принимать параметров, но использовать input()\n", + "\n", + "\n", + "def use_input() -> int:\n", + " \"\"\"Функция возводит введенное число в квадрат.\"\"\"\n", + " # запросим у пользователя число и переведем его в тип данных int\n", + " user_inp = int(input(\"Введите число: \"))\n", + "\n", + " # возведем число в квадрат\n", + " result_pow = user_inp**2\n", + "\n", + " # вернем результат\n", + " return result_pow\n", + "\n", + "\n", + "# вызовем функцию\n", + "use_input()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7L2W-41oM6vK" + }, + "source": [ + "#### Результат вызова функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zVLZODr-PazE", + "outputId": "af00da71-633a-4b23-c7ce-ed29d5ee7234" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 1, 2, 3, 4]" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# функция может возвращать также список, кортеж, словарь и др.\n", + "\n", + "\n", + "# объявим функцию, которая на входе получает число,\n", + "# а на выходе формирует список чисел от 0 и до числа, предшествующего заданному\n", + "def create_list(x_arg: int) -> list[int]:\n", + " \"\"\"Создает список,заполненный числами от 0 до x_arg.\"\"\"\n", + " # создадим пустой список\n", + " my_list: list[int] = []\n", + "\n", + " # в цикле for создадим последовательность\n", + " for index in range(x_arg):\n", + "\n", + " # и поместим ее в список\n", + " my_list.append(index)\n", + "\n", + " return my_list\n", + "\n", + "\n", + "# результатом вызова этой функции будет список\n", + "create_list(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RI4Xl_CHNm3B" + }, + "outputs": [], + "source": [ + "# функция может возвращать сразу два значения\n", + "\n", + "\n", + "def tuple_f() -> tuple[str, int]:\n", + " \"\"\"Возвращает кортеж ('python', 42).\"\"\"\n", + " string = \"Python\"\n", + " x_arg = 42\n", + " return string, x_arg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e4AXnLEdNzMJ", + "outputId": "c34bf598-3e76-4128-96ca-494dc144bea1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 42\n", + " \n" + ] + } + ], + "source": [ + "# если использовать две переменные\n", + "a_var, b_var = tuple_f()\n", + "\n", + "# на выходе мы получим строку и число\n", + "print(a_var, b_var)\n", + "print(type(a_var), type(b_var))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "n89-CN1pSlgP", + "outputId": "02b74687-471c-4a3e-e59c-c26440d25189" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('Python', 42)\n", + "\n" + ] + } + ], + "source": [ + "# если одну\n", + "c_var = tuple_f()\n", + "\n", + "# получится кортеж\n", + "print(c_var)\n", + "print(type(c_var))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bQzEDji5VFCY", + "outputId": "a9131375-4436-46a5-d27b-cc982a1ac1df" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# выводом может быть логическое значение (True или False)\n", + "\n", + "\n", + "def is_divisible(x_arg: int) -> bool:\n", + " \"\"\"Функция проверяет, является ли число четным.\"\"\"\n", + " # если остаток от деления на два равен нулю\n", + " if x_arg % 2 == 0:\n", + " # вернем True\n", + " return True\n", + "\n", + " # в противном случае False\n", + " return False\n", + "\n", + "\n", + "is_divisible(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uRNLyptXUoyj" + }, + "source": [ + "#### Использование библиотек" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gFTdRnLlUt4Y" + }, + "outputs": [], + "source": [ + "# применим функцию mean() библиотеки Numpy для расчета среднего арифметического\n", + "\n", + "\n", + "# на входе наша функция примет список или массив x,\n", + "def mean_f(x_arg: list[int]) -> float:\n", + " \"\"\"Функция вычисляет среднее и прибавляет 1 к результату.\"\"\"\n", + " # рассчитает среднее арифметическое и прибавит единицу\n", + " return float(np.mean(x_arg) + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OFeR_QpWU4i1", + "outputId": "a4f13201-904f-47c1-d97d-cc3d84598450" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и подготовить данные\n", + "x_var: list[int] = [1, 2, 3]\n", + "\n", + "mean_f(x_var)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kY91ANMfXeRG" + }, + "source": [ + "#### Глобальные и локальные переменные" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "a4HSsRlBVBT2" + }, + "outputs": [], + "source": [ + "# создадим глобальную переменную вне функции\n", + "global_name = \"Петр\"\n", + "\n", + "# а затем используем ее внутри новой функции\n", + "\n", + "\n", + "def show_name() -> None:\n", + " \"\"\"Функция выводит в консоль глобальную переменную global_name.\"\"\"\n", + " print(global_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_trqrh-zTjkk", + "outputId": "a436b20d-c6cf-42c2-c21a-f1ad43a70c91" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Петр\n" + ] + } + ], + "source": [ + "show_name()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jmStHPcqTo3F" + }, + "outputs": [], + "source": [ + "# а теперь вначале создадим функцию,\n", + "# внутри которой объявим локальную переменную\n", + "\n", + "\n", + "def show_local_name() -> None:\n", + " \"\"\"Функция выводит в консоль 'Алена'.\"\"\"\n", + " local_name_1 = \"Алена\"\n", + " print(local_name_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9KMmXhAIUJkT", + "outputId": "19f7a460-1ba2-4b13-e72a-b25a74d4543a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Алена\n" + ] + } + ], + "source": [ + "show_local_name()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 159 + }, + "id": "mcINdfP0UYt9", + "outputId": "352fad89-c4e3-49c7-a74f-64b0fb5192aa" + }, + "outputs": [], + "source": [ + "# при попытке обратиться к переменной вне функции мы получим ошибку\n", + "# local_name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GfAMDzGvUcZ8" + }, + "outputs": [], + "source": [ + "# превратить локальную переменную в глобальную можно\n", + "# через ключевое слово global\n", + "local_name = \"Татьяна\"\n", + "\n", + "\n", + "def make_global() -> None:\n", + " \"\"\"Создает глобальную переменную local_name и выводит ее в консоль.\"\"\"\n", + " global local_name # pylint: disable=W0603\n", + " local_name = \"Алена\"\n", + " print(local_name)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "glXQZMD9UpDf", + "outputId": "97d5d26f-7f28-4611-f6f7-006866a20068" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Алена\n" + ] + } + ], + "source": [ + "make_global()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "GSHkMhwXUxQK", + "outputId": "adda3989-248e-4c4e-8a41-0ed373aa4820" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Алена'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# теперь ошибки быть не должно\n", + "# local_name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rDAqC_PlZr35" + }, + "outputs": [], + "source": [ + "# объявим глобальную переменную\n", + "global_number = 5\n", + "\n", + "\n", + "def print_number() -> None:\n", + " \"\"\"Выводит в консоль значение локальной переменной local_number.\"\"\"\n", + " # затем объявим локальную переменную\n", + " local_number = 10\n", + " print(\"Local number:\", local_number)\n", + " print(\"Global number:\", global_number)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JvOjd91fGILS", + "outputId": "d7753061-9c05-49f6-c7d9-59fd4f299b30" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Local number: 10\n" + ] + } + ], + "source": [ + "# функция всегда \"предпочтет\" локальную переменную\n", + "print_number()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "306Y9HOWGIOf", + "outputId": "41ec6f6a-5d13-4412-e844-e63767eda086" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Global number: 5\n" + ] + } + ], + "source": [ + "# при этом значение глобальной переменной для остального кода не изменится\n", + "print(\"Global number:\", global_number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xLJn10TGXhlq" + }, + "source": [ + "### Lambda-функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CFPwBvbYXrYR", + "outputId": "4b879d40-03cc-4851-df11-29a92136f043" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим функцию, которая принимает два числа и перемножает их\n", + "lf: Callable[[int, int], int] = lambda a_arg, b_arg: a_arg * b_arg\n", + "\n", + "# вызовем функцию и передадим ей числа 2 и 3\n", + "lf(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "65xVovHHXks9", + "outputId": "d3bdc43a-fed4-4f65-bb86-4dc388694488" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# этот же функционал можно поместить в обычную функцию\n", + "\n", + "\n", + "def normal_f(a_arg: int, b_arg: int) -> int:\n", + " \"\"\"Возвращает произведение переданных аргументов.\"\"\"\n", + " return a_arg * b_arg\n", + "\n", + "\n", + "normal_f(2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "na0O4KAlE1Pk" + }, + "source": [ + "#### Lambda-функция внутри функции filter()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RMNawYCas6ER", + "outputId": "0ccd1139-a50f-491c-f44b-43d8602f3c26" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[15, 27, 18]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим список\n", + "nums_list: list[int] = [15, 27, 9, 18, 3, 1, 4]\n", + "\n", + "# напишем lambda-функцию, которая выведет True,\n", + "# если число больше 10, и False, если меньше\n", + "is_more_then_10: Callable[[int], bool] = lambda n_arg: n_arg > 10\n", + "\n", + "# поместим список и lambda-функцию в функцию filter()\n", + "# и преобразуем результат в список\n", + "list(filter(is_more_then_10, nums_list))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Y86jO6CewXnU", + "outputId": "c380fd97-b591-4f5b-b9ca-e1c504d5cc49" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[15, 27, 18]" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# все это можно записать в одну строчку\n", + "list(filter(lambda n_arg: n_arg > 10, nums_list))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VWAYPtGg0YQh", + "outputId": "9a81b80a-52c4-46c3-8bc1-07a445561738" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[15, 27, 18]" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# ту же задачу можно решить через обычную функцию,\n", + "# но придется написать больше кода\n", + "\n", + "\n", + "def is_more_then_10_2(n_arg: int) -> bool:\n", + " \"\"\"Функция проверяет, больше ли переданное число 10.\"\"\"\n", + " if n_arg > 10:\n", + " return True\n", + "\n", + " return False\n", + "\n", + "\n", + "list(filter(is_more_then_10_2, nums_list))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "k9aPF9yVE9eF" + }, + "source": [ + "#### Lambda-функция внутри функции sorted()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dMFSmXtJYb39", + "outputId": "20eb2b83-cad8-4fc5-fc11-fcbca0ea3725" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(901, 0.0), (1002, 0.22982440568634488), (442, 0.25401128310081567)]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# напомню, что мы создали список из кортежей,\n", + "# и в каждом кортеже был индекс фильма и расстояние до него\n", + "indices_distances = [\n", + " (901, 0.0),\n", + " (1002, 0.22982440568634488),\n", + " (442, 0.25401128310081567),\n", + "]\n", + "\n", + "# lambda-функция возьмет каждый кортеж и вернет второй [1] его элемент\n", + "# передав эту функцию через параметр key, мы отсортируем список по расстоянию\n", + "sorted(indices_distances, key=lambda x_arg: x_arg[1], reverse=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C8bwJf2oLI69" + }, + "source": [ + "#### Немедленно вызываемые функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XjbG4yHOK9in", + "outputId": "178e510d-4aac-4ef5-b663-8b527bfb333d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# lambda-функцию можно вызвать сразу в момент объявления\n", + "(lambda x_arg: x_arg * x_arg)(10) # pylint: disable=C3002" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pgeY1tf6U9PP" + }, + "source": [ + "### \\*args и \\**kwargs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uCX_-CWeYJeZ" + }, + "source": [ + "#### \\*args" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QxlLBRqLW0Mu" + }, + "outputs": [], + "source": [ + "# напишем функцию для расчета среднего арифметического двух чисел\n", + "\n", + "\n", + "def mean_simple(a_arg: int, b_arg: int) -> float:\n", + " \"\"\"Функция вычисляет среднее арифметическое.\"\"\"\n", + " return (a_arg + b_arg) / 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EhuFz8wpXBVr", + "outputId": "db2f6e0a-3c22-464a-f071-2f8817d3b936" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.5" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mean_simple(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cyQLlIZLVIM3" + }, + "outputs": [], + "source": [ + "# объявим функцию, которой нужно передать список\n", + "\n", + "\n", + "def mean_1(list_arg: list[int]) -> float:\n", + " \"\"\"Функция вычисляет среднее арифметическое переданного массива чисел.\"\"\"\n", + " # зададим переменную для суммы,\n", + " total = 0\n", + "\n", + " # в цикле сложим все числа из списка\n", + " for num in list_arg:\n", + " total += num\n", + "\n", + " # и разделим на количество элементов\n", + " return total / len(list_arg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nh7pebndVIQU", + "outputId": "1436f42d-4a15-4d13-d917-bad885b440df" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.5" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим список\n", + "list_: list[int] = [1, 2, 3, 4]\n", + "\n", + "# и передадим его в новую функцию\n", + "mean_1(list_)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 159 + }, + "id": "zy8lFJA9bkBc", + "outputId": "5311a807-f945-4c29-9422-adafa172e940" + }, + "outputs": [], + "source": [ + "# однако новая функция уже не может работать с отдельными числами\n", + "# mean(1, 2) -> Error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vwrIY7CqVIYO" + }, + "outputs": [], + "source": [ + "# объявим функцию с *args\n", + "\n", + "\n", + "def mean_2(*nums: int) -> float:\n", + " \"\"\"Функция вычисляет среднее арифметическое переданного массива чисел.\"\"\"\n", + " # в данном случае мы складываем элементы кортежа\n", + " total = 0\n", + " for num in nums:\n", + " total += num\n", + "\n", + " return total / len(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "P5Q1bhIVXz9p", + "outputId": "137523e1-03c8-49d5-ad5e-445879480dbf" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.5" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# теперь мы можем передать функции отдельные числа\n", + "mean_2(1, 2, 3, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bhHgfAZ-Ytmp", + "outputId": "a13075ac-6eef-4c70-b02b-fdd68cdf6be4" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.5" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# или список\n", + "mean_2(*list_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "L3NKa556r5O0", + "outputId": "7523137c-3f2c-419c-81c0-3573b02993b0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2, 3, 4) \n" + ] + } + ], + "source": [ + "# убедимся, что оператор распаковки * формирует кортеж\n", + "\n", + "\n", + "def test_type(*nums: int) -> None:\n", + " \"\"\"Функция проверят тип аргумента *nums.\"\"\"\n", + " print(nums, type(nums))\n", + "\n", + "\n", + "test_type(1, 2, 3, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XHDvzgXFxql2", + "outputId": "05e55975-5e49-4bff-b0ee-e1fa91dad322" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2, 3, 4) \n" + ] + } + ], + "source": [ + "# со списком происходит то же самое\n", + "test_type(*list_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JeLoABCMy_om", + "outputId": "3f00f8a2-8801-456a-9341-df12e3753e1d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5, 6]\n" + ] + } + ], + "source": [ + "# для наглядности приведем еще один пример\n", + "a_list: list[int] = [1, 2, 3]\n", + "b_list: list[int] = [*a_list, 4, 5, 6]\n", + "\n", + "print(b_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qh8xqKMwYLpH" + }, + "source": [ + "#### \\**kwargs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gzEpZF_13Vfq" + }, + "outputs": [], + "source": [ + "# **kwargs преобразует именованные параметры в словарь\n", + "\n", + "\n", + "def fn(**kwargs: int) -> ItemsView[str, int]:\n", + " \"\"\"Возвращает список пар ключ/значение именованных параметров.\"\"\"\n", + " return kwargs.items()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rl4dALTK3plJ", + "outputId": "c317ee78-c259-4cff-cd33-110d73749b74" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('a', 1), ('b', 2)])" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn(a=1, b=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Zd930nvoasfs" + }, + "outputs": [], + "source": [ + "# *nums превращается в кортеж, **params - в словарь\n", + "\n", + "\n", + "def simple_stats(*nums: int, **params: bool) -> None:\n", + " \"\"\"Функция вычисляет среднее арифметическое и СКО.\"\"\"\n", + " # если ключ 'mean' есть в словаре params и его значение == True\n", + " if \"mean\" in params and params[\"mean\"] is True:\n", + "\n", + " # рассчитаем среднее арифметическое и округлим\n", + " # \\t - это символ табуляции\n", + " print(f\"mean: \\t{np.round(np.mean(nums), 3)}\")\n", + "\n", + " # если ключ 'std' есть в словаре params и его значение == True\n", + " if \"std\" in params and params[\"std\"] is True:\n", + "\n", + " # рассчитаем СКО и округлим\n", + " print(f\"std: \\t{np.round(np.std(nums), 3)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YQqRLpXvfYJZ", + "outputId": "d5a72bf9-363d-4204-e659-5d61c414ea88" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mean: \t12.5\n", + "std: \t5.59\n" + ] + } + ], + "source": [ + "# вызовем функцию simple_stats() и передадим ей числа и именованные аргументы\n", + "simple_stats(5, 10, 15, 20, mean=True, std=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "h5VyRzsjn2ny", + "outputId": "a6762112-09d1-495c-f2e8-1b62fea3e20b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mean: \t12.5\n" + ] + } + ], + "source": [ + "# если для одного из параметров задать значение False,\n", + "# функция не выведет соответствующую метрику\n", + "simple_stats(5, 10, 15, 20, mean=True, std=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "J6TjmWJ3hbBv", + "outputId": "21d40d7d-9493-496c-86ac-2064f4b5b1a3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mean: \t12.5\n", + "std: \t5.59\n" + ] + } + ], + "source": [ + "# если мы хотим передать параметры списком и словарем,\n", + "list_nums: list[int] = [5, 10, 15, 20]\n", + "settings: dict[str, bool] = {\"mean\": True, \"std\": True}\n", + "\n", + "# то нам нужно использовать операторы распаковки * и ** соответственно\n", + "simple_stats(*list_nums, **settings)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "55FDFsesoAOP", + "outputId": "0c09c820-dc81-4d04-d4e0-ce9ab1b1a1ea" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mean: \t12.5\n", + "std: \t5.59\n" + ] + } + ], + "source": [ + "# ничто не мешает нам добавить еще один параметр\n", + "simple_stats(5, 10, 15, 20, mean=True, std=True, median=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "j4mcGPUgjKp4" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_06_functions.py b/Python/makarov/chapter_06_functions.py new file mode 100644 index 00000000..7e498356 --- /dev/null +++ b/Python/makarov/chapter_06_functions.py @@ -0,0 +1,634 @@ +"""Макаров. + +Функции в Питоне. +""" + +# ## Функции в Питоне + +# ### Встроенные функции + +# + +from collections.abc import ItemsView +from typing import Callable + +# импортируем библиотеки +import matplotlib.pyplot as plt + +# перед вызовом функции нужно не забыть импортировать +# соответствующую библиотеку +import numpy as np + +# установим точку отсчета +np.random.seed(42) +# и снова сгенерируем данные о росте +# (как мы делали на восьмом занятии вводного курса) +height = list(np.round(np.random.normal(180, 10, 1000))) +# - + +# #### Параметры и аргументы функции + +# теперь построим гистограмму передав ей два параметра, +# данные о росте и количество интервалов +# первый параметр у нас позиционный, второй - именованный +plt.hist(height, bins=10) +plt.show() + +# первый параметр можно также сделать именованным (данные обозначаются через x) +# и тогда порядок параметров можно менять +plt.hist(bins=10, x=height) +plt.show() + +# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов), +# а значит, этот параметр можно не указывать +plt.hist(height) +plt.show() + +# функция может не принимать параметров +print("Первая строка") +print() +print("Третья строка") + +# #### Функции и методы + +# + +# дана строка +some_string = "machine learning" + +# применим метод .title() +some_string.title() + +# + +# к списку +some_list: list[str] = ["machine", "learning"] + +# этот метод не применить +# some_list.title() +# - + +# ### Собственные функции в Питоне + +# #### Объявление и вызов функции + +# + +# создадим функцию, которая удваивает любое передаваемое ей значение + + +def double(x_arg: int) -> int: + """Умножает переданное число на 2.""" + res = x_arg * 2 + return res + + +# - + +# и вызовем ее, передав число 2 +double(2) + +# #### Пустое тело функции + +# + +# тело функции не может быть пустым + + +def only_return() -> None: + """Функция с одним return.""" + # нужно либо указать ключевое слово return + return + + +# - + +only_return() + +# + +# либо оператор pass + + +# def only_pass() -> None: +# """Функция ничего не делает.""" +# pass + +# + +# only_pass() + +# + +# такая функция вернет тип данных None (отсутствие значения) +# print(only_return()) -> None +# - + +# #### Функция print() вместо return + +# + +# можно использовать print(), но есть нюансы (см. на странице урока) + + +def double_print(x_arg: int) -> None: + """Функция умножает переданное число на 2 и выводит результат в консоль.""" + res = x_arg * 2 + print(res) + + +# - + +double_print(5) + +# #### Параметры собственных функций + +# + +# объявим функцию с параметрами x и y + + +def calc_sum(x_arg: int, y_arg: int) -> int: + """Функция складывает два целых числа.""" + # и выведем их сумму + return x_arg + y_arg + + +# - + +# вызовем эту функцию с одним позиционным и одним именованным параметром +calc_sum(1, y_arg=2) + +# + +# параметрам функции можно задать аргументы по умолчанию + + +def calc_sum_default(x_arg: int = 1, y_arg: int = 2) -> int: + """Функция складывает два целых числа.""" + return x_arg + y_arg + + +# и при вызове тогда их указывать не обязательно +calc_sum_default() + +# + +# функция может не иметь параметров + + +def print_string() -> None: + """Функция выводит строку 'Some string'.""" + print("Some string") + + +print_string() +# - + +# #### Аннотация функции + +# + +# укажем, что на входе функция принимает тип float, а возвращает int +# значение 3,5 - это значение параметра x по умолчанию + + +def f(x_arg: float = 3.5) -> int: + """Функция приводит переданный float-аргумент к целочисленному типу.""" + return int(x_arg) + + +# - + +# желаемый тип данных можно посмотреть через атрибут __annotations__ +f.__annotations__ + +# вызовем функцию без параметров +f() + +# + +# сохраним аннотации, но изменим суть функции +# def f(x: float) -> int: +# # теперь вместо int она будет возвращать float +# return float(x) +# - + +# вновь вызовем функцию, передав ей на входе int, и ожидая на выходе получить float +f(3) + +# #### Дополнительные возможности + +# вызов функции можно совмещать с арифметическими +result = calc_sum(1, 2) * 2 + +# и логическими операциями +result = calc_sum(1, 2) > 2 + +# + +# можно и так + + +def first_letter() -> str: + """Функция возвращает строку 'Python'.""" + return "Python" + + +print(first_letter()[0]) + +# + +# функция может не принимать параметров, но использовать input() + + +def use_input() -> int: + """Функция возводит введенное число в квадрат.""" + # запросим у пользователя число и переведем его в тип данных int + user_inp = int(input("Введите число: ")) + + # возведем число в квадрат + result_pow = user_inp**2 + + # вернем результат + return result_pow + + +# вызовем функцию +use_input() +# - + +# #### Результат вызова функции + +# + +# функция может возвращать также список, кортеж, словарь и др. + + +# объявим функцию, которая на входе получает число, +# а на выходе формирует список чисел от 0 и до числа, предшествующего заданному +def create_list(x_arg: int) -> list[int]: + """Создает список,заполненный числами от 0 до x_arg.""" + # создадим пустой список + my_list: list[int] = [] + + # в цикле for создадим последовательность + for index in range(x_arg): + + # и поместим ее в список + my_list.append(index) + + return my_list + + +# результатом вызова этой функции будет список +create_list(5) + +# + +# функция может возвращать сразу два значения + + +def tuple_f() -> tuple[str, int]: + """Возвращает кортеж ('python', 42).""" + string = "Python" + x_arg = 42 + return string, x_arg + + +# + +# если использовать две переменные +a_var, b_var = tuple_f() + +# на выходе мы получим строку и число +print(a_var, b_var) +print(type(a_var), type(b_var)) + +# + +# если одну +c_var = tuple_f() + +# получится кортеж +print(c_var) +print(type(c_var)) + +# + +# выводом может быть логическое значение (True или False) + + +def is_divisible(x_arg: int) -> bool: + """Функция проверяет, является ли число четным.""" + # если остаток от деления на два равен нулю + if x_arg % 2 == 0: + # вернем True + return True + + # в противном случае False + return False + + +is_divisible(10) +# - + +# #### Использование библиотек + +# + +# применим функцию mean() библиотеки Numpy для расчета среднего арифметического + + +# на входе наша функция примет список или массив x, +def mean_f(x_arg: list[int]) -> float: + """Функция вычисляет среднее и прибавляет 1 к результату.""" + # рассчитает среднее арифметическое и прибавит единицу + return float(np.mean(x_arg) + 1) + + +# + +# и подготовить данные +x_var: list[int] = [1, 2, 3] + +mean_f(x_var) +# - + +# #### Глобальные и локальные переменные + +# + +# создадим глобальную переменную вне функции +global_name = "Петр" + +# а затем используем ее внутри новой функции + + +def show_name() -> None: + """Функция выводит в консоль глобальную переменную global_name.""" + print(global_name) + + +# - + +show_name() + +# + +# а теперь вначале создадим функцию, +# внутри которой объявим локальную переменную + + +def show_local_name() -> None: + """Функция выводит в консоль 'Алена'.""" + local_name_1 = "Алена" + print(local_name_1) + + +# - + +show_local_name() + +# + +# при попытке обратиться к переменной вне функции мы получим ошибку +# local_name + +# + +# превратить локальную переменную в глобальную можно +# через ключевое слово global +local_name = "Татьяна" + + +def make_global() -> None: + """Создает глобальную переменную local_name и выводит ее в консоль.""" + global local_name # pylint: disable=W0603 + local_name = "Алена" + print(local_name) + + +# - + +make_global() + +# + +# теперь ошибки быть не должно +# local_name + +# + +# объявим глобальную переменную +global_number = 5 + + +def print_number() -> None: + """Выводит в консоль значение локальной переменной local_number.""" + # затем объявим локальную переменную + local_number = 10 + print("Local number:", local_number) + print("Global number:", global_number) + + +# - + +# функция всегда "предпочтет" локальную переменную +print_number() + +# при этом значение глобальной переменной для остального кода не изменится +print("Global number:", global_number) + +# ### Lambda-функции + +# + +# создадим функцию, которая принимает два числа и перемножает их +lf: Callable[[int, int], int] = lambda a_arg, b_arg: a_arg * b_arg + +# вызовем функцию и передадим ей числа 2 и 3 +lf(2, 3) + +# + +# этот же функционал можно поместить в обычную функцию + + +def normal_f(a_arg: int, b_arg: int) -> int: + """Возвращает произведение переданных аргументов.""" + return a_arg * b_arg + + +normal_f(2, 3) +# - + +# #### Lambda-функция внутри функции filter() + +# + +# создадим список +nums_list: list[int] = [15, 27, 9, 18, 3, 1, 4] + +# напишем lambda-функцию, которая выведет True, +# если число больше 10, и False, если меньше +is_more_then_10: Callable[[int], bool] = lambda n_arg: n_arg > 10 + +# поместим список и lambda-функцию в функцию filter() +# и преобразуем результат в список +list(filter(is_more_then_10, nums_list)) +# - + +# все это можно записать в одну строчку +list(filter(lambda n_arg: n_arg > 10, nums_list)) + +# + +# ту же задачу можно решить через обычную функцию, +# но придется написать больше кода + + +def is_more_then_10_2(n_arg: int) -> bool: + """Функция проверяет, больше ли переданное число 10.""" + if n_arg > 10: + return True + + return False + + +list(filter(is_more_then_10_2, nums_list)) +# - + +# #### Lambda-функция внутри функции sorted() + +# + +# напомню, что мы создали список из кортежей, +# и в каждом кортеже был индекс фильма и расстояние до него +indices_distances = [ + (901, 0.0), + (1002, 0.22982440568634488), + (442, 0.25401128310081567), +] + +# lambda-функция возьмет каждый кортеж и вернет второй [1] его элемент +# передав эту функцию через параметр key, мы отсортируем список по расстоянию +sorted(indices_distances, key=lambda x_arg: x_arg[1], reverse=False) +# - + +# #### Немедленно вызываемые функции + +# lambda-функцию можно вызвать сразу в момент объявления +(lambda x_arg: x_arg * x_arg)(10) # pylint: disable=C3002 + +# ### \*args и \**kwargs + +# #### \*args + +# + +# напишем функцию для расчета среднего арифметического двух чисел + + +def mean_simple(a_arg: int, b_arg: int) -> float: + """Функция вычисляет среднее арифметическое.""" + return (a_arg + b_arg) / 2 + + +# - + +mean_simple(1, 2) + +# + +# объявим функцию, которой нужно передать список + + +def mean_1(list_arg: list[int]) -> float: + """Функция вычисляет среднее арифметическое переданного массива чисел.""" + # зададим переменную для суммы, + total = 0 + + # в цикле сложим все числа из списка + for num in list_arg: + total += num + + # и разделим на количество элементов + return total / len(list_arg) + + +# + +# создадим список +list_: list[int] = [1, 2, 3, 4] + +# и передадим его в новую функцию +mean_1(list_) + +# + +# однако новая функция уже не может работать с отдельными числами +# mean(1, 2) -> Error + +# + +# объявим функцию с *args + + +def mean_2(*nums: int) -> float: + """Функция вычисляет среднее арифметическое переданного массива чисел.""" + # в данном случае мы складываем элементы кортежа + total = 0 + for num in nums: + total += num + + return total / len(nums) + + +# - + +# теперь мы можем передать функции отдельные числа +mean_2(1, 2, 3, 4) + +# или список +mean_2(*list_) + +# + +# убедимся, что оператор распаковки * формирует кортеж + + +def test_type(*nums: int) -> None: + """Функция проверят тип аргумента *nums.""" + print(nums, type(nums)) + + +test_type(1, 2, 3, 4) +# - + +# со списком происходит то же самое +test_type(*list_) + +# + +# для наглядности приведем еще один пример +a_list: list[int] = [1, 2, 3] +b_list: list[int] = [*a_list, 4, 5, 6] + +print(b_list) +# - + +# #### \**kwargs + +# + +# **kwargs преобразует именованные параметры в словарь + + +def fn(**kwargs: int) -> ItemsView[str, int]: + """Возвращает список пар ключ/значение именованных параметров.""" + return kwargs.items() + + +# - + +fn(a=1, b=2) + +# + +# *nums превращается в кортеж, **params - в словарь + + +def simple_stats(*nums: int, **params: bool) -> None: + """Функция вычисляет среднее арифметическое и СКО.""" + # если ключ 'mean' есть в словаре params и его значение == True + if "mean" in params and params["mean"] is True: + + # рассчитаем среднее арифметическое и округлим + # \t - это символ табуляции + print(f"mean: \t{np.round(np.mean(nums), 3)}") + + # если ключ 'std' есть в словаре params и его значение == True + if "std" in params and params["std"] is True: + + # рассчитаем СКО и округлим + print(f"std: \t{np.round(np.std(nums), 3)}") + + +# - + +# вызовем функцию simple_stats() и передадим ей числа и именованные аргументы +simple_stats(5, 10, 15, 20, mean=True, std=True) + +# если для одного из параметров задать значение False, +# функция не выведет соответствующую метрику +simple_stats(5, 10, 15, 20, mean=True, std=False) + +# + +# если мы хотим передать параметры списком и словарем, +list_nums: list[int] = [5, 10, 15, 20] +settings: dict[str, bool] = {"mean": True, "std": True} + +# то нам нужно использовать операторы распаковки * и ** соответственно +simple_stats(*list_nums, **settings) +# - + +# ничто не мешает нам добавить еще один параметр +simple_stats(5, 10, 15, 20, mean=True, std=True, median=True) diff --git a/Python/makarov/chapter_07_classes.ipynb b/Python/makarov/chapter_07_classes.ipynb new file mode 100644 index 00000000..8f61ad01 --- /dev/null +++ b/Python/makarov/chapter_07_classes.ipynb @@ -0,0 +1,1967 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Классы и объекты в Питоне.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0AoPSXyUTTLc" + }, + "source": [ + "## Классы и объекты в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lIM5gFo4TbUg" + }, + "source": [ + "### Создание класса" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RROZUPzwm4rK" + }, + "source": [ + "#### Создание класса и метод `.__init__()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QGswhMFMno7-" + }, + "outputs": [], + "source": [ + "# создадим класс CatClass\n", + "# нашему классу понадобится Numpy\n", + "# массивом Numpy и другими объектами\n", + "import numpy as np\n", + "\n", + "# из набора линейных моделей библиотеки sklearn импортируем линейную регрессию\n", + "from sklearn.linear_model import LinearRegression\n", + "\n", + "\n", + "class CatClass:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " # и пропишем метод .__init__()\n", + " def __init__(self) -> None:\n", + " \"\"\"Инициализация экземпляра класса.\"\"\"\n", + " print(\"Cat initializing\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qIIzZD65n1Mh" + }, + "source": [ + "#### Создание объекта" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 104 + }, + "id": "pERZ4ZQonsJB", + "outputId": "ec41568f-3897-4c9f-e23f-2a65ecb4d894" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "
CatClass
def __init__()
<no docstring>
" + ], + "text/plain": [ + "__main__.CatClass" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект Matroskin класса CatClass\n", + "Matroskin = CatClass()\n", + "\n", + "# проверим тип данных созданной переменной\n", + "type(Matroskin)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1DUEgzmJn6_h" + }, + "source": [ + "#### Атрибуты класса" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0mQE6dKcwIjD" + }, + "outputs": [], + "source": [ + "# вновь создадим класс CatClass\n", + "\n", + "\n", + "class CatClass1:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " # метод .__init__() на этот раз принимает еще и параметр color\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация Кота с цветом и типом.\"\"\"\n", + " # этот параметр будет записан в переменную атрибута self.color\n", + " self.color = color\n", + "\n", + " # значение атрибута type_ задается внутри класса\n", + " self.type_ = \"cat\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CXFmjySeEJ59", + "outputId": "63e1955b-c1e7-43e1-d5d0-f077bfead122" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('gray', 'cat')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# повторно создадим объект класса CatClass, передав ему параметр цвета шерсти\n", + "Matroskin1 = CatClass1(\"gray\")\n", + "\n", + "# и выведем атрибуты класса\n", + "Matroskin1.color, Matroskin1.type_" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lm6DQjYEWR-M" + }, + "source": [ + "#### Методы класса" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KYAGXaEaWXGt" + }, + "outputs": [], + "source": [ + "# перепишем класс CatClass\n", + "\n", + "\n", + "class CatClass2:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " # метод .__init__() и атрибуты оставим без изменений\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация Кота с цветом и типом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"\n", + "\n", + " # однако добавим метод, который позволит коту мяукать\n", + " def meow(self) -> None:\n", + " \"\"\"Метод осуществляет мяуканье.\"\"\"\n", + " for _ in range(3):\n", + " print(\"Мяу\")\n", + "\n", + " # и метод .info() для вывода информации об объекте\n", + " def info(self) -> None:\n", + " \"\"\"Метод выводит информацию об экземпляре Кот.\"\"\"\n", + " print(self.color, self.type_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pwOtF86Na8Tm" + }, + "outputs": [], + "source": [ + "# создадим объект\n", + "Matroskin2 = CatClass2(\"gray\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5wKGe_KEcq9C", + "outputId": "7faf0fa9-83e6-4879-e9ee-451f67b99dc3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Мяу\n", + "Мяу\n", + "Мяу\n" + ] + } + ], + "source": [ + "# применим метод .meow()\n", + "Matroskin2.meow()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "01l4yJA17k9d", + "outputId": "f8831b06-b408-41dd-bdf1-cedd0ef17d84" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gray cat\n" + ] + } + ], + "source": [ + "# и метод .info()\n", + "Matroskin2.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7o5fPMtonH0V" + }, + "source": [ + "### Принципы ООП" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LG0BiM5PgxYR" + }, + "source": [ + "#### Инкапсуляция" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "X0HiYZq0Rmnd", + "outputId": "21e52d6a-c48c-475e-f9d8-52ab960961da" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'dog'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# изменим атрибут type_ объекта Matroskin на dog\n", + "Matroskin1.type_ = \"dog\"\n", + "\n", + "# выведем этот атрибут\n", + "Matroskin1.type_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_Un9fShyWILH" + }, + "outputs": [], + "source": [ + "class CatClass3:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация Кота с цветом и типом.\"\"\"\n", + " self.color = color\n", + " # символ подчеркивания ПЕРЕД названием атрибута указывает,\n", + " # что это частный атрибут и изменять его не стоит\n", + " self._type_ = \"cat\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "YmpfrLukW4sm", + "outputId": "d1f43282-6ddf-4054-aac7-82b75041450e" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'dog'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вновь создадим объект класса CatClass\n", + "Matroskin3 = CatClass3(\"gray\")\n", + "\n", + "# и изменим значение атрибута _type_\n", + "Matroskin3._type_ = \"dog\" # pylint: disable=W0212\n", + "Matroskin3._type_ # pylint: disable=W0212" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GqU88qogZ8_B" + }, + "outputs": [], + "source": [ + "class CatClass4:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация Кота с цветом и типом.\"\"\"\n", + " self.color = color\n", + " # символ двойного подчеркивания предотвратит доступ извне\n", + " self.__type_ = \"cat\"\n", + " print(self.__type_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2O1GUso6aG4C" + }, + "outputs": [], + "source": [ + "# при попытке вызова такого атрибута Питон выдаст ошибку\n", + "Matroskin4 = CatClass4(\"gray\")\n", + "print(Matroskin4.__type_) # pylint: disable=W0212" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "gqsNNuRiaHBr", + "outputId": "4a011366-bbe1-4668-ba86-9a3774713d71" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'dog'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# поставим _CatClass перед __type_\n", + "# Matroskin4._CatClass4__type_ = \"dog\" C0103\n", + "\n", + "# к сожалению, значение атрибута изменится\n", + "# print(Matroskin4._CatClass4__type_)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "33PlUWogG2HN" + }, + "source": [ + "#### Наследование классов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e-NaY_Yd4l5h" + }, + "source": [ + "Создание родительского класса и класса-потомка" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HS-07E-6Vc6Q" + }, + "outputs": [], + "source": [ + "# создадим класс Animal\n", + "\n", + "\n", + "class Animal:\n", + " \"\"\"Базовый класс животное.\"\"\"\n", + "\n", + " # пропишем метод .__init__() с двумя параметрами: вес (кг) и длина (см)\n", + " def __init__(self, weight: float, length: float) -> None:\n", + " \"\"\"Инициализация Животного с весом и длинной.\"\"\"\n", + " # поместим аргументы этих параметров в соответствующие переменные\n", + " self.weight = weight\n", + " self.length = length\n", + "\n", + " # объявим методы .eat()\n", + " def eat(self) -> None:\n", + " \"\"\"Метод осуществляет употребление пищи.\"\"\"\n", + " print(\"Eating\")\n", + "\n", + " # и .sleep()\n", + " def sleep(self) -> None:\n", + " \"\"\"Метод для сна.\"\"\"\n", + " print(\"Sleeping\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lABQGZSoCF0Y" + }, + "outputs": [], + "source": [ + "# создадим класс Bird\n", + "# родительский класс Animal пропишем в скобках\n", + "\n", + "\n", + "class Bird(Animal):\n", + " \"\"\"Класс Птица, наследник класса Animal.\"\"\"\n", + "\n", + " def __init__(self, weight: float, length: float) -> None:\n", + " \"\"\"Инициализация Птицы с весом, длинной.\"\"\"\n", + " # с помощью функции super() вызовем метод .__init__() родительского класса Animal\n", + " Animal.__init__(self, weight, length)\n", + "\n", + " # внутри класса Bird объявим новый метод .move()\n", + " def move(self) -> None:\n", + " \"\"\"Осуществляет полет птицы.\"\"\"\n", + " # для птиц .move() будет означать \"летать\"\n", + " print(\"Flying\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RzdouKGtCQMP" + }, + "outputs": [], + "source": [ + "# создадим объект pigeon и передадим ему значения веса и длины\n", + "pigeon = Bird(0.3, 30)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YGwcXdjWCcDd", + "outputId": "ed5ba623-7a25-4989-8575-b0b828fd32b1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.3, 30)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на унаследованные у класса Animal атрибуты\n", + "print(pigeon.weight, pigeon.length)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8wX-Bo_fCcP7", + "outputId": "f81e094f-0fc7-495f-d361-6ae2089af75f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eating\n" + ] + } + ], + "source": [ + "# и методы\n", + "pigeon.eat()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d6HH0FwrCw90", + "outputId": "392ed579-8f16-485b-cb46-1fc8a1622756" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Flying\n" + ] + } + ], + "source": [ + "# теперь вызовем метод, свойственный только классу Bird\n", + "pigeon.move()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9yL5WN2C_Qi" + }, + "source": [ + "Функция `super()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JYzIVcZrVc9W" + }, + "outputs": [], + "source": [ + "# снова создадим класс Bird\n", + "\n", + "\n", + "class Bird1(Animal):\n", + " \"\"\"Класс Птица, наследник класса Animal.\"\"\"\n", + "\n", + " # в метод .__init__() добавим параметр скорости полета (км/ч)\n", + " def __init__(\n", + " self, weight: float, length: float, flying_speed: float\n", + " ) -> None:\n", + " \"\"\"Инициализация Птицы с весом, длинной и скоростью полета.\"\"\"\n", + " # с помощью функции super() вызовем метод .__init__() родительского класса Animal\n", + " Animal.__init__(self, weight, length)\n", + " self.flying_speed = flying_speed\n", + "\n", + " # вновь пропишем метод .move()\n", + " def move(self) -> None:\n", + " \"\"\"Осуществляет полет птицы.\"\"\"\n", + " print(\"Flying\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cwu9N5Q0VdAO" + }, + "outputs": [], + "source": [ + "# вновь создадим объект pigeon класса Bird, но уже с тремя параметрами\n", + "pigeon1 = Bird1(0.3, 30, 100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WfcbSzlf6Ao_", + "outputId": "e1c67eff-0beb-4cd5-d23a-e16d77b74288" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.3, 30, 100)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вызовем как унаследованные, так и собственные атрибуты класса Bird\n", + "print(pigeon1.weight, pigeon1.length, pigeon1.flying_speed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "230dmrRP4ZZE", + "outputId": "08c58782-4a4a-487c-afef-caebda3f5518" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sleeping\n" + ] + } + ], + "source": [ + "# вызовем унаследованный метод .sleep()\n", + "pigeon1.sleep()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "spaGz6VV4g_m", + "outputId": "005310bf-a88a-484e-a816-feac166bf35a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Flying\n" + ] + } + ], + "source": [ + "# и собственный метод .move()\n", + "pigeon1.move()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qmOoUK4R5D-f" + }, + "source": [ + "Переопределение класса" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7EFREoZb4kE6" + }, + "outputs": [], + "source": [ + "# создадим подкласс Flightless класса Bird\n", + "\n", + "\n", + "class Flightless(Bird):\n", + " \"\"\"Класс Нелетающая Птица.\"\"\"\n", + "\n", + " # метод .__init__() этого подкласса \"стирает\" .__init__() родительского класса\n", + " def __init__(\n", + " self, weight: float, length: float, running_speed: float\n", + " ) -> None:\n", + " \"\"\"Инициализация Нелетающей Птицы со скоростью бега.\"\"\"\n", + " # таким образом, у нас остается только один атрибут\n", + " Bird.__init__(self, weight, length)\n", + "\n", + " self.running_speed = running_speed\n", + "\n", + " # кроме того, результатом метода .move() будет 'Running'\n", + " def move(self) -> None:\n", + " \"\"\"Осуществляет бег птицы.\"\"\"\n", + " print(\"Running\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lb8QJKEN4kKh" + }, + "outputs": [], + "source": [ + "# создадим объект ostrich класса Flightless\n", + "ostrich = Flightless(13, 33, 60)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AoTdJFjw4kNn", + "outputId": "19cba34f-4d88-4b14-8996-84ab61c669d7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "60" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на значение атрибута скорости\n", + "ostrich.running_speed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PledBnHF4kQ3", + "outputId": "f8dc2bdd-d8cb-4d1d-f389-2909bf9b5e16" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running\n" + ] + } + ], + "source": [ + "# и проверим метод .move()\n", + "ostrich.move()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7oBZyEcG797e", + "outputId": "445235a6-2e6a-4ff7-d66d-99ba8a359145" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eating\n" + ] + } + ], + "source": [ + "# подкласс Flightless сохранил методы всех родительских классов\n", + "ostrich.eat()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BW2xaOeB9PDk" + }, + "source": [ + "Множественное наследование" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jA-Aj_z49RYk" + }, + "outputs": [], + "source": [ + "# создадим родительский класс Fish\n", + "\n", + "\n", + "class Fish:\n", + " \"\"\"Класс Рыба.\"\"\"\n", + "\n", + " # и метод .swim()\n", + " def swim(self) -> None:\n", + " \"\"\"Осуществляет плавание.\"\"\"\n", + " print(\"Swimming\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CN-A0F2D9RSC" + }, + "outputs": [], + "source": [ + "# и еще один родительский класс Bird\n", + "\n", + "\n", + "class Bird2:\n", + " \"\"\"Класс Птица.\"\"\"\n", + "\n", + " # и метод .fly()\n", + " def fly(self) -> None:\n", + " \"\"\"Осуществляет полет.\"\"\"\n", + " print(\"Flying\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "udTUUUr0HKkB" + }, + "outputs": [], + "source": [ + "# теперь создадим класс-потомок этих двух классов\n", + "\n", + "\n", + "class SwimmingBird(Bird2, Fish):\n", + " \"\"\"Класс Плавающая Птица.\"\"\"\n", + "\n", + " pass # pylint: disable=unnecessary-pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7SfYHQItHe9I" + }, + "outputs": [], + "source": [ + "# создадим объект duck класса SwimmingBird\n", + "duck = SwimmingBird()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xogM-VUMHrd0", + "outputId": "99eac1e7-b433-4d7f-808d-fee1d0eee5b2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Flying\n" + ] + } + ], + "source": [ + "# как мы видим утка умеет как летать,\n", + "duck.fly()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lHZ6WVWQJmVi", + "outputId": "9e410617-daaa-4f91-d25a-4e21be5efc26" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Swimming\n" + ] + } + ], + "source": [ + "# так и плавать\n", + "duck.swim()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ruBf2MpEiN-9" + }, + "source": [ + "#### Полиморфизм" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "57k1y3vHzS2Q", + "outputId": "580b15e7-667d-426f-9e1b-545feb183354" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для чисел '+' является оператором сложения\n", + "print(2 + 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "XFkxRUt2zTFb", + "outputId": "d413afd8-f001-4220-c0f0-ab14f10900dc" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'классы и объекты'" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для строк - оператором объединения\n", + "print(\"классы\" + \" и \" + \"объекты\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "syQy4yvkiRUN" + }, + "source": [ + "1. Полиморфизм функций" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "T23-llHTiTY2", + "outputId": "53e10046-f89b-4e08-855f-15872c223af2" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "26" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# функцию len() можно применить к строке\n", + "print(len(\"Программирование на Питоне\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CbWOFs8wqLKZ", + "outputId": "1bdb6886-59d4-4f7a-ea07-d269a971e358" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# кроме того, она способна работать со списком\n", + "print(len([\"Программирование\", \"на\", \"Питоне\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "l03mggVpqLPx", + "outputId": "235f05b6-d75f-4a3c-b65b-5673ae460020" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# словарем\n", + "print(len({0: \"Программирование\", 1: \"на\", 2: \"Питоне\"}))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EAvsld8Zr6eK", + "outputId": "9d3f93a2-08ef-49e9-d83a-89d85f53ce3e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(len(np.array([1, 2, 3])))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "03fAyPjYiUD-" + }, + "source": [ + "2. Полиморфизм классов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Q2JI2sNo_rJ4" + }, + "source": [ + "Создадим объекты с одинаковыми атрибутами и методами" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JexRqp8IiTig" + }, + "outputs": [], + "source": [ + "# создадим класс котов\n", + "\n", + "\n", + "class CatClass5:\n", + " \"\"\"Класс Кот.\"\"\"\n", + "\n", + " # определим атрибуты клички, типа и цвета шерсти\n", + " def __init__(self, name: str, color: str) -> None:\n", + " \"\"\"Инициализация Кота с именем, цветом и типом.\"\"\"\n", + " self.name = name\n", + " self._type_ = \"кот\"\n", + " self.color = color\n", + "\n", + " # создадим метод .info() для вывода этих атрибутов\n", + " def info(self) -> None:\n", + " \"\"\"Выводит информацию о коте.\"\"\"\n", + " print(\n", + " f\"Меня зовут {self.name}, я {self._type_}, цвет моей шерсти {self.color}\"\n", + " )\n", + "\n", + " # и метод .sound(), показывающий, что коты умеют мяукать\n", + " def sound(self) -> None:\n", + " \"\"\"Метод сообщает, что коты умеют мяукать.\"\"\"\n", + " print(\"Я умею мяукать\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PEyzqo1qBxhZ" + }, + "outputs": [], + "source": [ + "# создадим класс собак\n", + "\n", + "\n", + "class DogClass5:\n", + " \"\"\"Класс Пес.\"\"\"\n", + "\n", + " # с такими же атрибутами\n", + " def __init__(self, name: str, color: str) -> None:\n", + " \"\"\"Инициализация Пса с именем, цветом и типом.\"\"\"\n", + " self.name = name\n", + " self._type_ = \"пес\"\n", + " self.color = color\n", + "\n", + " # и методами\n", + " def info(self) -> None:\n", + " \"\"\"Выводит информацию о коте.\"\"\"\n", + " print(\n", + " f\"Меня зовут {self.name}, я {self._type_}, цвет моей шерсти {self.color}\"\n", + " )\n", + "\n", + " # хотя, обратите внимание, действия внутри методов отличаются\n", + " def sound(self) -> None:\n", + " \"\"\"Метод сообщает, что псы умеют лаять.\"\"\"\n", + " print(\"Я умею лаять\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yxdVCLs7_vGV" + }, + "source": [ + "Создадим объекты этих классов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "j5TO6c1aAHRV" + }, + "outputs": [], + "source": [ + "cat5 = CatClass5(\"Бегемот\", \"черный\")\n", + "dog5 = DogClass5(\"Барбос\", \"серый\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "In29ldkK_9aa" + }, + "source": [ + "В цикле `for` вызовем атрибуты и методы каждого из классов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5xvyicz0AGyU", + "outputId": "ab94a6c3-16e7-424d-9763-f38dfee541bb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Меня зовут Бегемот, я кот, цвет моей шерсти черный\n", + "Я умею мяукать\n", + "\n", + "Меня зовут Барбос, я пес, цвет моей шерсти серый\n", + "Я умею лаять\n", + "\n" + ] + } + ], + "source": [ + "for animal in (cat5, dog5):\n", + " animal.info()\n", + " animal.sound()\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UhIuTZgpjK-n" + }, + "source": [ + "### Парадигмы программирования" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "D329DnitlFFM" + }, + "outputs": [], + "source": [ + "patients: list[dict[str, str | int]] = [\n", + " {\"name\": \"Николай\", \"height\": 178},\n", + " {\"name\": \"Иван\", \"height\": 182},\n", + " {\"name\": \"Алексей\", \"height\": 190},\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SakiQ8b0jQb5" + }, + "source": [ + "#### Процедурное программирование" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fMwt8bYhjVG0", + "outputId": "6e503541-62ca-4f17-c384-4799190734be" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "183.33333333333334" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим переменные для общего роста и количества пациентов\n", + "total, count = 0, 0\n", + "\n", + "# в цикле for пройдемся по пациентам (отдельным словарям)\n", + "for patient in patients:\n", + " # достанем значение роста и прибавим к текущему значению переменной total\n", + " total += int(patient[\"height\"])\n", + " # на каждой итерации будем увеличивать счетчик пациентов на один\n", + " count += 1\n", + "\n", + "# разделим общий рост на количество пациентов,\n", + "# чтобы получить среднее значение\n", + "print(total / count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pdLaSjb-jV1y" + }, + "source": [ + "#### Объектно-ориентированное программирование" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7OKHNVZHjiTu" + }, + "outputs": [], + "source": [ + "# создадим класс для работы с данными DataClass\n", + "\n", + "\n", + "class DataClass:\n", + " \"\"\"Класс для работы с данными.\"\"\"\n", + "\n", + " # при создании объекта будем передавать ему данные для анализа\n", + " def __init__(self, data: list[dict[str, str | int]]) -> None:\n", + " \"\"\"Инициализация экземпляра класса.\"\"\"\n", + " self.data = data\n", + " self.metric: str | None = None\n", + " self.__total: int | None = None\n", + " self.__count: int | None = None\n", + "\n", + " # кроме того, создадим метод для расчета среднего значения\n", + " def count_average(self, metric: str) -> float:\n", + " \"\"\"Расчет среднего значения по переданной метрике.\"\"\"\n", + " # параметр metric определит, по какому столбцу считать среднее\n", + " self.metric = metric\n", + "\n", + " # объявим два частных атрибута\n", + " self.__total = 0\n", + " self.__count = 0\n", + "\n", + " # в цикле for пройдемся по списку словарей\n", + " for item in self.data:\n", + "\n", + " # рассчитаем общую сумму по указанному в metric\n", + " # значению каждого словаря\n", + " self.__total += int(item[self.metric])\n", + "\n", + " # и количество таких записей\n", + " self.__count += 1\n", + "\n", + " # разделим общую сумму показателя на количество записей\n", + " return self.__total / self.__count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CgiUFzkLjibJ", + "outputId": "9c019f6e-adee-45f2-d837-ff4a876c12e5" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "183.33333333333334" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект класса DataClass и передадим ему данные о пациентах\n", + "data_object = DataClass(patients)\n", + "\n", + "# вызовем метод .count_average() с метрикой 'height'\n", + "data_object.count_average(\"height\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oIzYzt3hji9O" + }, + "source": [ + "#### Функциональное программирование" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "T7rvVdD9rU4a" + }, + "source": [ + "Функция map()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5PT_4ZLfqqG0", + "outputId": "e659e7fa-e06c-4e21-ef9e-26df60c55556" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[178, 182, 190]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# lambda-функция достанет значение по ключу height\n", + "# функция map() применит lambda-функцию к каждому вложенному в patients словарю\n", + "# функция list() преобразует результат в список\n", + "heights: list[int] = list(\n", + " map(lambda patient: int(patient[\"height\"]), patients)\n", + ")\n", + "heights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Zr0R6ibHor5X", + "outputId": "6838b211-8132-497d-efe4-7a5893d04db6" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "183.33333333333334" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# воспользуемся функциями sum() и len() для нахождения среднего значения\n", + "print(sum(heights) / len(heights))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5WBvRwVxrXRg" + }, + "source": [ + "Функция einsum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "co4xTm4krdDn" + }, + "outputs": [], + "source": [ + "# возьмем два двумерных массива\n", + "a_np_array = np.array([[0, 1, 2], [3, 4, 5]])\n", + "\n", + "b_np_array = np.array([[5, 4], [3, 2], [1, 0]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D1U0EgomMX-6", + "outputId": "489d668d-1800-4032-d25b-af055a01fb12" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 5, 2],\n", + " [32, 20]])" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# перемножим a и b по индексу j через функцию np.einsum()\n", + "einsum_result = np.einsum(\"ij, jk -> ik\", a_np_array, b_np_array)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K3j3MzT6h70G" + }, + "source": [ + "### Классы и объекты в машинном обучении" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WFqBszMyiAdy" + }, + "source": [ + "#### Готовые классы в библиотеке sklearn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VtVNV9EMiFcF" + }, + "outputs": [], + "source": [ + "# возьмем данные роста и обхвата шеи\n", + "X_np_array = np.array(\n", + " [\n", + " 1.48,\n", + " 1.49,\n", + " 1.49,\n", + " 1.50,\n", + " 1.51,\n", + " 1.52,\n", + " 1.52,\n", + " 1.53,\n", + " 1.53,\n", + " 1.54,\n", + " 1.55,\n", + " 1.56,\n", + " 1.57,\n", + " 1.57,\n", + " 1.58,\n", + " 1.58,\n", + " 1.59,\n", + " 1.60,\n", + " 1.61,\n", + " 1.62,\n", + " 1.63,\n", + " 1.64,\n", + " 1.65,\n", + " 1.65,\n", + " 1.66,\n", + " 1.67,\n", + " 1.67,\n", + " 1.68,\n", + " 1.68,\n", + " 1.69,\n", + " 1.70,\n", + " 1.70,\n", + " 1.71,\n", + " 1.71,\n", + " 1.71,\n", + " 1.74,\n", + " 1.75,\n", + " 1.76,\n", + " 1.77,\n", + " 1.77,\n", + " 1.78,\n", + " ]\n", + ")\n", + "y_np_array = np.array(\n", + " [\n", + " 29.1,\n", + " 30.0,\n", + " 30.1,\n", + " 30.2,\n", + " 30.4,\n", + " 30.6,\n", + " 30.8,\n", + " 30.9,\n", + " 31.0,\n", + " 30.6,\n", + " 30.7,\n", + " 30.9,\n", + " 31.0,\n", + " 31.2,\n", + " 31.3,\n", + " 32.0,\n", + " 31.4,\n", + " 31.9,\n", + " 32.4,\n", + " 32.8,\n", + " 32.8,\n", + " 33.3,\n", + " 33.6,\n", + " 33.0,\n", + " 33.9,\n", + " 33.8,\n", + " 35.0,\n", + " 34.5,\n", + " 34.7,\n", + " 34.6,\n", + " 34.2,\n", + " 34.8,\n", + " 35.5,\n", + " 36.0,\n", + " 36.2,\n", + " 36.3,\n", + " 36.6,\n", + " 36.8,\n", + " 36.8,\n", + " 37.0,\n", + " 38.5,\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "urkDTmJiGexS" + }, + "outputs": [], + "source": [ + "# преобразуем данные роста в двумерный массив\n", + "X_2D = X_np_array.reshape(-1, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Y_7E3d9EE2_e", + "outputId": "9c5a5c1e-49f3-4287-d7ad-a0ec3904c777" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([26.86181201]), -10.570936299787334)" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект этого класса и запишем в переменную model\n", + "model = LinearRegression()\n", + "\n", + "# обучим модель с помощью метода .fit(), которому передадим наши данные\n", + "model.fit(X_2D, y_np_array)\n", + "\n", + "# на выходе получим коэффициенты линейной регрессии\n", + "model.coef_, model.intercept_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DAkn0YJd_xZM", + "outputId": "4cf1ef45-8f54-4fc9-a534-511d6d835f8d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([29.18454547, 29.45316359, 29.45316359, 29.72178171, 29.99039983])" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# +\n", + "# построим прогноз и выведем первые пять значений\n", + "y_pred = model.predict(X_2D)\n", + "print(y_pred[:5])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "deNVn0YIJ9Lx" + }, + "source": [ + "#### Пример ООП: собственный класс линейной регрессии" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Fy55136VEDy9" + }, + "outputs": [], + "source": [ + "# создадим класс SimpleLinearRegression\n", + "\n", + "\n", + "class SimpleLinearRegression:\n", + " \"\"\"Класс SimpleLinearRegression.\"\"\"\n", + "\n", + " # в методе .__init__() объявим переменные наклона и сдвига\n", + " def __init__(self) -> None:\n", + " \"\"\"Инициализация экземпляра класса.\"\"\"\n", + " self.slope_: float | None = None\n", + " self.intercept_: float | None = None\n", + "\n", + " # создадим метод .fit()\n", + " def fit(self, x_arg: list[float], y_arg: list[float]) -> None:\n", + " \"\"\"Метод для вычисления наклона и сдвига.\"\"\"\n", + " # найдем среднее значение X и y\n", + " x_mean = self.find_mean(x_arg)\n", + " y_mean = self.find_mean(y_arg)\n", + "\n", + " # объявим переменные для числителя и знаменателя\n", + " numerator: float = 0\n", + " denominator: float = 0\n", + "\n", + " # в цикле пройдемся по данным\n", + " for index, value_x in enumerate(x_arg):\n", + "\n", + " # вычислим значения числителя и знаменателя по формуле выше\n", + " numerator += (value_x - x_mean) * (y_arg[index] - y_mean)\n", + " denominator += (value_x - x_mean) ** 2\n", + "\n", + " # найдем наклон и сдвиг\n", + " slope_ = numerator / denominator\n", + " intercept_ = y_mean - slope_ * x_mean\n", + "\n", + " # сохраним получившиеся коэффициенты в виде атрибутов\n", + " self.slope_ = slope_\n", + " self.intercept_ = intercept_\n", + "\n", + " # метод .predict() просто умножит через скалярное произведение\n", + " # вектор данных на наклон и прибавит сдвиг\n", + " def predict(self, x_arg: list[float]): # type: ignore\n", + " \"\"\"Вычисляет склярное произведение.\"\"\"\n", + " # на выходе мы получим вектор прогнозных значений\n", + " return np.dot(self.slope_, x_arg) + self.intercept_\n", + "\n", + " # служебный метод: расчет среднего\n", + " def find_mean(self, nums: list[float]) -> float:\n", + " \"\"\"Расчет среднего.\"\"\"\n", + " return sum(nums) / len(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SO7OMSo0E3K7" + }, + "outputs": [], + "source": [ + "# создадим объект класса SimpleLinearRegression\n", + "model = SimpleLinearRegression()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "g27k4R8dE8mo", + "outputId": "03691d70-a0c4-49e8-f6b3-5c7b879b529c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(26.861812005569757, -10.570936299787334)" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# применим метод .fit()\n", + "model.fit(X_np_array, y_np_array) # type: ignore\n", + "\n", + "# посмотрим на коэффициенты\n", + "print(model.slope_, model.intercept_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FgvjnZG9F_rA", + "outputId": "0560d5a5-dbf8-4bb3-efd7-926f3f8a29da" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([29.18454547, 29.45316359, 29.45316359, 29.72178171, 29.99039983])" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# сделаем прогноз через .predict()\n", + "y_pred = model.predict(X_np_array) # type: ignore\n", + "\n", + "# и выведем первые пять коэффициентов\n", + "print(y_pred[:5])" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_07_classes.py b/Python/makarov/chapter_07_classes.py new file mode 100644 index 00000000..4dd65ab4 --- /dev/null +++ b/Python/makarov/chapter_07_classes.py @@ -0,0 +1,739 @@ +"""Макаров. + +Классы и объекты в Питоне. +""" + +# ## Классы и объекты в Питоне + +# ### Создание класса + +# #### Создание класса и метод `.__init__()` + +# + +# создадим класс CatClass +# нашему классу понадобится Numpy +# массивом Numpy и другими объектами +import numpy as np + +# из набора линейных моделей библиотеки sklearn импортируем линейную регрессию +from sklearn.linear_model import LinearRegression + + +class CatClass: + """Класс Кот.""" + + # и пропишем метод .__init__() + def __init__(self) -> None: + """Инициализация экземпляра класса.""" + print("Cat initializing") + + +# - + +# #### Создание объекта + +# + +# создадим объект Matroskin класса CatClass +Matroskin = CatClass() + +# проверим тип данных созданной переменной +type(Matroskin) +# - + +# #### Атрибуты класса + +# + +# вновь создадим класс CatClass + + +class CatClass1: + """Класс Кот.""" + + # метод .__init__() на этот раз принимает еще и параметр color + def __init__(self, color: str) -> None: + """Инициализация Кота с цветом и типом.""" + # этот параметр будет записан в переменную атрибута self.color + self.color = color + + # значение атрибута type_ задается внутри класса + self.type_ = "cat" + + +# + +# повторно создадим объект класса CatClass, передав ему параметр цвета шерсти +Matroskin1 = CatClass1("gray") + +# и выведем атрибуты класса +Matroskin1.color, Matroskin1.type_ +# - + +# #### Методы класса + +# + +# перепишем класс CatClass + + +class CatClass2: + """Класс Кот.""" + + # метод .__init__() и атрибуты оставим без изменений + def __init__(self, color: str) -> None: + """Инициализация Кота с цветом и типом.""" + self.color = color + self.type_ = "cat" + + # однако добавим метод, который позволит коту мяукать + def meow(self) -> None: + """Метод осуществляет мяуканье.""" + for _ in range(3): + print("Мяу") + + # и метод .info() для вывода информации об объекте + def info(self) -> None: + """Метод выводит информацию об экземпляре Кот.""" + print(self.color, self.type_) + + +# - + +# создадим объект +Matroskin2 = CatClass2("gray") + +# применим метод .meow() +Matroskin2.meow() + +# и метод .info() +Matroskin2.info() + +# ### Принципы ООП + +# #### Инкапсуляция + +# + +# изменим атрибут type_ объекта Matroskin на dog +Matroskin1.type_ = "dog" + +# выведем этот атрибут +Matroskin1.type_ + + +# - + + +class CatClass3: + """Класс Кот.""" + + def __init__(self, color: str) -> None: + """Инициализация Кота с цветом и типом.""" + self.color = color + # символ подчеркивания ПЕРЕД названием атрибута указывает, + # что это частный атрибут и изменять его не стоит + self._type_ = "cat" + + +# + +# вновь создадим объект класса CatClass +Matroskin3 = CatClass3("gray") + +# и изменим значение атрибута _type_ +Matroskin3._type_ = "dog" # pylint: disable=W0212 +Matroskin3._type_ # pylint: disable=W0212 + + +# - + + +class CatClass4: + """Класс Кот.""" + + def __init__(self, color: str) -> None: + """Инициализация Кота с цветом и типом.""" + self.color = color + # символ двойного подчеркивания предотвратит доступ извне + self.__type_ = "cat" + print(self.__type_) + + +# при попытке вызова такого атрибута Питон выдаст ошибку +Matroskin4 = CatClass4("gray") +print(Matroskin4.__type_) # pylint: disable=W0212 + +# + +# поставим _CatClass перед __type_ +# Matroskin4._CatClass4__type_ = "dog" C0103 + +# к сожалению, значение атрибута изменится +# print(Matroskin4._CatClass4__type_) +# - + +# #### Наследование классов + +# Создание родительского класса и класса-потомка + +# + +# создадим класс Animal + + +class Animal: + """Базовый класс животное.""" + + # пропишем метод .__init__() с двумя параметрами: вес (кг) и длина (см) + def __init__(self, weight: float, length: float) -> None: + """Инициализация Животного с весом и длинной.""" + # поместим аргументы этих параметров в соответствующие переменные + self.weight = weight + self.length = length + + # объявим методы .eat() + def eat(self) -> None: + """Метод осуществляет употребление пищи.""" + print("Eating") + + # и .sleep() + def sleep(self) -> None: + """Метод для сна.""" + print("Sleeping") + + +# + +# создадим класс Bird +# родительский класс Animal пропишем в скобках + + +class Bird(Animal): + """Класс Птица, наследник класса Animal.""" + + def __init__(self, weight: float, length: float) -> None: + """Инициализация Птицы с весом, длинной.""" + # с помощью функции super() вызовем метод .__init__() родительского класса Animal + Animal.__init__(self, weight, length) + + # внутри класса Bird объявим новый метод .move() + def move(self) -> None: + """Осуществляет полет птицы.""" + # для птиц .move() будет означать "летать" + print("Flying") + + +# - + +# создадим объект pigeon и передадим ему значения веса и длины +pigeon = Bird(0.3, 30) + +# посмотрим на унаследованные у класса Animal атрибуты +print(pigeon.weight, pigeon.length) + +# и методы +pigeon.eat() + +# теперь вызовем метод, свойственный только классу Bird +pigeon.move() + +# Функция `super()` + +# + +# снова создадим класс Bird + + +class Bird1(Animal): + """Класс Птица, наследник класса Animal.""" + + # в метод .__init__() добавим параметр скорости полета (км/ч) + def __init__( + self, weight: float, length: float, flying_speed: float + ) -> None: + """Инициализация Птицы с весом, длинной и скоростью полета.""" + # с помощью функции super() вызовем метод .__init__() родительского класса Animal + Animal.__init__(self, weight, length) + self.flying_speed = flying_speed + + # вновь пропишем метод .move() + def move(self) -> None: + """Осуществляет полет птицы.""" + print("Flying") + + +# - + +# вновь создадим объект pigeon класса Bird, но уже с тремя параметрами +pigeon1 = Bird1(0.3, 30, 100) + +# вызовем как унаследованные, так и собственные атрибуты класса Bird +print(pigeon1.weight, pigeon1.length, pigeon1.flying_speed) + +# вызовем унаследованный метод .sleep() +pigeon1.sleep() + +# и собственный метод .move() +pigeon1.move() + +# Переопределение класса + +# + +# создадим подкласс Flightless класса Bird + + +class Flightless(Bird): + """Класс Нелетающая Птица.""" + + # метод .__init__() этого подкласса "стирает" .__init__() родительского класса + def __init__( + self, weight: float, length: float, running_speed: float + ) -> None: + """Инициализация Нелетающей Птицы со скоростью бега.""" + # таким образом, у нас остается только один атрибут + Bird.__init__(self, weight, length) + + self.running_speed = running_speed + + # кроме того, результатом метода .move() будет 'Running' + def move(self) -> None: + """Осуществляет бег птицы.""" + print("Running") + + +# - + +# создадим объект ostrich класса Flightless +ostrich = Flightless(13, 33, 60) + +# посмотрим на значение атрибута скорости +ostrich.running_speed + +# и проверим метод .move() +ostrich.move() + +# подкласс Flightless сохранил методы всех родительских классов +ostrich.eat() + +# Множественное наследование + +# + +# создадим родительский класс Fish + + +class Fish: + """Класс Рыба.""" + + # и метод .swim() + def swim(self) -> None: + """Осуществляет плавание.""" + print("Swimming") + + +# + +# и еще один родительский класс Bird + + +class Bird2: + """Класс Птица.""" + + # и метод .fly() + def fly(self) -> None: + """Осуществляет полет.""" + print("Flying") + + +# + +# теперь создадим класс-потомок этих двух классов + + +class SwimmingBird(Bird2, Fish): + """Класс Плавающая Птица.""" + + pass # pylint: disable=unnecessary-pass + + +# - + +# создадим объект duck класса SwimmingBird +duck = SwimmingBird() + +# как мы видим утка умеет как летать, +duck.fly() + +# так и плавать +duck.swim() + +# #### Полиморфизм + +# для чисел '+' является оператором сложения +print(2 + 2) + +# для строк - оператором объединения +print("классы" + " и " + "объекты") + +# 1. Полиморфизм функций + +# функцию len() можно применить к строке +print(len("Программирование на Питоне")) + +# кроме того, она способна работать со списком +print(len(["Программирование", "на", "Питоне"])) + +# словарем +print(len({0: "Программирование", 1: "на", 2: "Питоне"})) + +print(len(np.array([1, 2, 3]))) + +# 2. Полиморфизм классов + +# Создадим объекты с одинаковыми атрибутами и методами + +# + +# создадим класс котов + + +class CatClass5: + """Класс Кот.""" + + # определим атрибуты клички, типа и цвета шерсти + def __init__(self, name: str, color: str) -> None: + """Инициализация Кота с именем, цветом и типом.""" + self.name = name + self._type_ = "кот" + self.color = color + + # создадим метод .info() для вывода этих атрибутов + def info(self) -> None: + """Выводит информацию о коте.""" + print( + f"Меня зовут {self.name}, я {self._type_}, цвет моей шерсти {self.color}" + ) + + # и метод .sound(), показывающий, что коты умеют мяукать + def sound(self) -> None: + """Метод сообщает, что коты умеют мяукать.""" + print("Я умею мяукать") + + +# + +# создадим класс собак + + +class DogClass5: + """Класс Пес.""" + + # с такими же атрибутами + def __init__(self, name: str, color: str) -> None: + """Инициализация Пса с именем, цветом и типом.""" + self.name = name + self._type_ = "пес" + self.color = color + + # и методами + def info(self) -> None: + """Выводит информацию о коте.""" + print( + f"Меня зовут {self.name}, я {self._type_}, цвет моей шерсти {self.color}" + ) + + # хотя, обратите внимание, действия внутри методов отличаются + def sound(self) -> None: + """Метод сообщает, что псы умеют лаять.""" + print("Я умею лаять") + + +# - + +# Создадим объекты этих классов + +cat5 = CatClass5("Бегемот", "черный") +dog5 = DogClass5("Барбос", "серый") + +# В цикле `for` вызовем атрибуты и методы каждого из классов + +for animal in (cat5, dog5): + animal.info() + animal.sound() + print() + +# ### Парадигмы программирования + +patients: list[dict[str, str | int]] = [ + {"name": "Николай", "height": 178}, + {"name": "Иван", "height": 182}, + {"name": "Алексей", "height": 190}, +] + +# #### Процедурное программирование + +# + +# создадим переменные для общего роста и количества пациентов +total, count = 0, 0 + +# в цикле for пройдемся по пациентам (отдельным словарям) +for patient in patients: + # достанем значение роста и прибавим к текущему значению переменной total + total += int(patient["height"]) + # на каждой итерации будем увеличивать счетчик пациентов на один + count += 1 + +# разделим общий рост на количество пациентов, +# чтобы получить среднее значение +print(total / count) +# - + +# #### Объектно-ориентированное программирование + +# + +# создадим класс для работы с данными DataClass + + +class DataClass: + """Класс для работы с данными.""" + + # при создании объекта будем передавать ему данные для анализа + def __init__(self, data: list[dict[str, str | int]]) -> None: + """Инициализация экземпляра класса.""" + self.data = data + self.metric: str | None = None + self.__total: int | None = None + self.__count: int | None = None + + # кроме того, создадим метод для расчета среднего значения + def count_average(self, metric: str) -> float: + """Расчет среднего значения по переданной метрике.""" + # параметр metric определит, по какому столбцу считать среднее + self.metric = metric + + # объявим два частных атрибута + self.__total = 0 + self.__count = 0 + + # в цикле for пройдемся по списку словарей + for item in self.data: + + # рассчитаем общую сумму по указанному в metric + # значению каждого словаря + self.__total += int(item[self.metric]) + + # и количество таких записей + self.__count += 1 + + # разделим общую сумму показателя на количество записей + return self.__total / self.__count + + +# + +# создадим объект класса DataClass и передадим ему данные о пациентах +data_object = DataClass(patients) + +# вызовем метод .count_average() с метрикой 'height' +data_object.count_average("height") +# - + +# #### Функциональное программирование + +# Функция map() + +# lambda-функция достанет значение по ключу height +# функция map() применит lambda-функцию к каждому вложенному в patients словарю +# функция list() преобразует результат в список +heights: list[int] = list( + map(lambda patient: int(patient["height"]), patients) +) +heights + +# воспользуемся функциями sum() и len() для нахождения среднего значения +print(sum(heights) / len(heights)) + +# Функция einsum() + +# + +# возьмем два двумерных массива +a_np_array = np.array([[0, 1, 2], [3, 4, 5]]) + +b_np_array = np.array([[5, 4], [3, 2], [1, 0]]) +# - + +# перемножим a и b по индексу j через функцию np.einsum() +einsum_result = np.einsum("ij, jk -> ik", a_np_array, b_np_array) + +# ### Классы и объекты в машинном обучении + +# #### Готовые классы в библиотеке sklearn + +# возьмем данные роста и обхвата шеи +X_np_array = np.array( + [ + 1.48, + 1.49, + 1.49, + 1.50, + 1.51, + 1.52, + 1.52, + 1.53, + 1.53, + 1.54, + 1.55, + 1.56, + 1.57, + 1.57, + 1.58, + 1.58, + 1.59, + 1.60, + 1.61, + 1.62, + 1.63, + 1.64, + 1.65, + 1.65, + 1.66, + 1.67, + 1.67, + 1.68, + 1.68, + 1.69, + 1.70, + 1.70, + 1.71, + 1.71, + 1.71, + 1.74, + 1.75, + 1.76, + 1.77, + 1.77, + 1.78, + ] +) +y_np_array = np.array( + [ + 29.1, + 30.0, + 30.1, + 30.2, + 30.4, + 30.6, + 30.8, + 30.9, + 31.0, + 30.6, + 30.7, + 30.9, + 31.0, + 31.2, + 31.3, + 32.0, + 31.4, + 31.9, + 32.4, + 32.8, + 32.8, + 33.3, + 33.6, + 33.0, + 33.9, + 33.8, + 35.0, + 34.5, + 34.7, + 34.6, + 34.2, + 34.8, + 35.5, + 36.0, + 36.2, + 36.3, + 36.6, + 36.8, + 36.8, + 37.0, + 38.5, + ] +) + +# преобразуем данные роста в двумерный массив +X_2D = X_np_array.reshape(-1, 1) + +# + +# создадим объект этого класса и запишем в переменную model +model = LinearRegression() + +# обучим модель с помощью метода .fit(), которому передадим наши данные +model.fit(X_2D, y_np_array) + +# на выходе получим коэффициенты линейной регрессии +model.coef_, model.intercept_ +# - + +# # + +# построим прогноз и выведем первые пять значений +y_pred = model.predict(X_2D) +print(y_pred[:5]) + +# #### Пример ООП: собственный класс линейной регрессии + +# + +# создадим класс SimpleLinearRegression + + +class SimpleLinearRegression: + """Класс SimpleLinearRegression.""" + + # в методе .__init__() объявим переменные наклона и сдвига + def __init__(self) -> None: + """Инициализация экземпляра класса.""" + self.slope_: float | None = None + self.intercept_: float | None = None + + # создадим метод .fit() + def fit(self, x_arg: list[float], y_arg: list[float]) -> None: + """Метод для вычисления наклона и сдвига.""" + # найдем среднее значение X и y + x_mean = self.find_mean(x_arg) + y_mean = self.find_mean(y_arg) + + # объявим переменные для числителя и знаменателя + numerator: float = 0 + denominator: float = 0 + + # в цикле пройдемся по данным + for index, value_x in enumerate(x_arg): + + # вычислим значения числителя и знаменателя по формуле выше + numerator += (value_x - x_mean) * (y_arg[index] - y_mean) + denominator += (value_x - x_mean) ** 2 + + # найдем наклон и сдвиг + slope_ = numerator / denominator + intercept_ = y_mean - slope_ * x_mean + + # сохраним получившиеся коэффициенты в виде атрибутов + self.slope_ = slope_ + self.intercept_ = intercept_ + + # метод .predict() просто умножит через скалярное произведение + # вектор данных на наклон и прибавит сдвиг + def predict(self, x_arg: list[float]): # type: ignore + """Вычисляет склярное произведение.""" + # на выходе мы получим вектор прогнозных значений + return np.dot(self.slope_, x_arg) + self.intercept_ # type: ignore + + # служебный метод: расчет среднего + def find_mean(self, nums: list[float]) -> float: + """Расчет среднего.""" + return sum(nums) / len(nums) + + +# - + +# создадим объект класса SimpleLinearRegression +model = SimpleLinearRegression() + +# + +# применим метод .fit() +model.fit(X_np_array, y_np_array) + +# посмотрим на коэффициенты +print(model.slope_, model.intercept_) + +# + +# сделаем прогноз через .predict() +y_pred = model.predict(X_np_array) + +# и выведем первые пять коэффициентов +print(y_pred[:5]) diff --git a/Python/makarov/chapter_08_lists_tuples_sets.ipynb b/Python/makarov/chapter_08_lists_tuples_sets.ipynb new file mode 100644 index 00000000..8e33372e --- /dev/null +++ b/Python/makarov/chapter_08_lists_tuples_sets.ipynb @@ -0,0 +1,2544 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Списки, кортежи и множества.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0AoPSXyUTTLc" + }, + "source": [ + "## Списки, кортежи и множества" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lIM5gFo4TbUg" + }, + "source": [ + "### Списки" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PPEbDV8hCAGO" + }, + "source": [ + "Основы работы со списками" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "x3wxNu5jthst", + "outputId": "9ea10800-7ade-4833-d849-424f4252cab8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] []\n" + ] + } + ], + "source": [ + "# пустой список можно создать через [] или функцию list()\n", + "# импортируем класс стеммера и создаем объект\n", + "from nltk.stem import PorterStemmer\n", + "\n", + "some_list_1: list[str] = []\n", + "some_list_2: list[str] = list() # pylint: disable=R1734\n", + "\n", + "print(some_list_1, some_list_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tLWO1t--thwP", + "outputId": "bdba1e22-059a-4ae5-b5b7-f8cf32544eec" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[3, 'число три', ['число', 'три'], {'число': 3}]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# элементами списка могут, в частности, быть числа, строки,\n", + "# другие списки и словари\n", + "number_three: list[int | str | list[str] | dict[str, int]] = [\n", + " 3,\n", + " \"число три\",\n", + " [\"число\", \"три\"],\n", + " {\"число\": 3},\n", + "]\n", + "number_three" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_jEdnwKSkbSE", + "outputId": "c50a7419-d925-4a2c-ec7d-f705e2c988c0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# длина списка рассчитывается через функцию len()\n", + "len(number_three)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9KkaG7NJDOMW" + }, + "source": [ + "Индекс и срез списка" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b3GS578dDRSH", + "outputId": "76a19d40-7499-4467-efd0-8d07b1b5d968" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a e\n" + ] + } + ], + "source": [ + "# у списка есть положительный и отрицательный индексы\n", + "abc_list: list[str] = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", + "\n", + "# воспользуемся ими для вывода первого и последнего элементов\n", + "print(abc_list[0], abc_list[-1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "LsfIU3Z3F6Qo", + "outputId": "ad2627b5-bc55-438f-ab49-be1c0ed9814e" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Игорь'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# при работе с вложенным списком\n", + "salary_list: list[list[str | int]] = [\n", + " [\"Анна\", 90000],\n", + " [\"Игорь\", 85000],\n", + " [\"Алексей\", 95000],\n", + "]\n", + "\n", + "# мы вначале указываем индекс вложенного списка, а затем индекс элемента в нем\n", + "salary_list[1][0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hzcsnn7LISyc", + "outputId": "8e597d24-f23d-41a7-d055-70220a1f033f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# индекс можно узнать с помощью метода .index()\n", + "abc_list.index(\"c\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ARwsxkVfrFOQ", + "outputId": "3137c4c5-16bd-4ccb-e448-81cb54de5270" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .index() можно применить и ко вложенному списку\n", + "salary_list[0].index(90000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VvcCb_ZaItpp", + "outputId": "25c98c5c-e90b-4adc-b28d-94d4802ed786" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Вт', 'Ср', 'Чт', 'Пт']" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим список с днями недели\n", + "days_list: list[str] = [\"Пн\", \"Вт\", \"Ср\", \"Чт\", \"Пт\", \"Сб\", \"Вс\"]\n", + "\n", + "# и выведем со второго по пятый элемент включительно\n", + "days_list[1:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vFJFJGigJ-kQ", + "outputId": "a0b65aff-7ef8-428c-ea4f-9bd14651f442" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Пн', 'Ср', 'Пт']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# выведем каждый второй элемент в срезе с первого по пятый\n", + "days_list[:5:2]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "n6Q6not7t9GH", + "outputId": "57d9edce-4af6-4e33-b058-147d844289e7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# проверим есть ли \"Пн\" в списке\n", + "\"Пн\" in days_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HpVMXHWDuXy7", + "outputId": "5fd5743a-2da3-41a3-f875-ae4276e1cd63" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Такое слово есть\n" + ] + } + ], + "source": [ + "# если \"Вт\" есть в списке\n", + "if \"Вт\" in days_list:\n", + "\n", + " # выведем сообщение\n", + " print(\"Такое слово есть\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "msuaGJ1OLXrB" + }, + "source": [ + "Добавление, замена и удаление элементов списка" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fEyyqpUDLcsY" + }, + "outputs": [], + "source": [ + "# создадим список\n", + "weekdays: list[str] = [\"Понедельник\", \"Вторник\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2YK5UIHOnZwq", + "outputId": "5fa9a3c3-f203-458c-a4fa-befa0b0f36a9" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник', 'Четверг']" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим один элемент в конец списка с помощью метода .append()\n", + "weekdays.append(\"Четверг\")\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Yvrd-wOMnZ9d", + "outputId": "8e85dec1-5b22-47c3-be53-25b8c2f3aa42" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник', 'Среда', 'Четверг']" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим элемент в определенное место в списке\n", + "# через желаемый индекс этого элемента\n", + "weekdays.insert(2, \"Среда\")\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zPEqoCsrzKEB", + "outputId": "c7214ad0-de20-4d05-e178-0ae0b29081fe" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник', 'Среда', 'Пятница']" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# изменим четвертый элемент в списке\n", + "weekdays[3] = \"Пятница\"\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gsWduacznqg4", + "outputId": "0ffc359c-e8dc-473a-d2dd-6bb573746afc" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник', 'Среда']" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# удалим элемент по его значению\n", + "weekdays.remove(\"Пятница\")\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D2bnKXNAxmq2", + "outputId": "94426985-ebca-423b-e80a-dc3f5bc0a8b1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник']" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# удалим элемент по его индексу через ключевое слово del\n", + "del weekdays[2]\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "CooE-D9ouFeP", + "outputId": "149a53e2-baf6-4e8e-85a4-630ce28819a5" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Вторник'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# сделаем то же самое с помощью метода .pop()\n", + "# этот метод выводит удаляемый элемент\n", + "weekdays.pop(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kM8rmPNXuyN6", + "outputId": "f1a810fc-ccb3-4ccf-8f25-480dbb83f25a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник']" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим, что осталось в нашем списке\n", + "weekdays" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MWP0v0fz0IQ4" + }, + "source": [ + "Сложение списков" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cwu9M49e0HlB", + "outputId": "66ef1e04-7468-47c7-9997-fd250cedf9f3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# соединить два списка можно с помощью метода .extend()\n", + "more_weekdays: list[str] = [\"Вторник\", \"Среда\", \"Четверг\", \"Пятница\"]\n", + "\n", + "weekdays.extend(more_weekdays)\n", + "weekdays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CE07IDeZx0cl", + "outputId": "e4674475-aa75-4468-a95b-f5a0c142300f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье']\n" + ] + } + ], + "source": [ + "weekend: list[str] = [\"Суббота\", \"Воскресенье\"]\n", + "\n", + "# или просто сложив два списка\n", + "print(weekdays + weekend)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJcSZ0jiz840", + "outputId": "7edd4ed0-2c01-4beb-ca00-f2e135746e97" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Понедельник']" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# иногда бывает полезно \"размножить\" элементы списка\n", + "print([\"Понедельник\"] * 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3MRsPPs30N1V", + "outputId": "5d0b022c-1487-43dc-b8dd-c4a5751187e0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Понедельник', 'Понедельник', 'Вторник', 'Вторник']" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# такие \"произведения\" также можно складывать\n", + "print([\"Понедельник\"] * 2 + [\"Вторник\"] * 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5zWQeyD4lsk9" + }, + "source": [ + "Распаковка списков" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n6kQjWdPlv7P" + }, + "outputs": [], + "source": [ + "# дан список\n", + "# \"_\" добавлен для того, чтобы избежать дублирования кода\n", + "# и как следствие ошибок pylint\n", + "week: list[str] = [\n", + " \"Понедельник_\",\n", + " \"Вторник_\",\n", + " \"Среда_\",\n", + " \"Четверг_\",\n", + " \"Пятница_\",\n", + " \"Суббота_\",\n", + " \"Воскресенье_\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "ZquIS9Lhl2z_", + "outputId": "4c532ff3-8e31-45e0-ac63-515e8b9b621d" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Понедельник'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# указав индекс элемента, его можно записать в переменную\n", + "monday = week[0]\n", + "monday" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1HAhG91Dl22z", + "outputId": "d0fd205e-f2c4-4888-fb93-7d0fa82464ba" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('Понедельник', 'Вторник', 'Среда')" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# срез можно поместить в несколько переменных\n", + "monday, tuesday, wednesday = week[:3]\n", + "\n", + "# важно, чтобы количество элементов среза было равно количеству переменных\n", + "monday, tuesday, wednesday" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "hfnnO_eUl25o", + "outputId": "cbf4587c-f1f0-445b-b973-64dd677f43f6" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Понедельник'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# можно выделить первый элемент, а остальные поместить\n", + "# в переменную со звездочкой\n", + "monday, *rest = week\n", + "monday" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7oVKz24-m9lY", + "outputId": "de4351b4-5e71-49b6-cb0b-beedca62c8c8" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('Понедельник', 'Воскресенье')" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# также можно поступить, например, с первым и последним элементом\n", + "monday, *days, sunday = week\n", + "monday, sunday" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xgSI_44Mqlwv", + "outputId": "53e11e9b-8830-48e0-97c5-986219ff5dc0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота']" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим, какие элементы остались в переменной со звездочкой\n", + "days" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CZZ2Tq0yq4l5" + }, + "source": [ + "Сортировка списков" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zTBWIYWZr80H" + }, + "outputs": [], + "source": [ + "# возьмем список чисел\n", + "nums: list[int] = [25, 10, 30, 20, 5, 15]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Zt9iwWx3q7IL", + "outputId": "b991cb38-26b3-4c26-a6d9-668056437217" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 10, 15, 20, 25, 30]" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и отсортируем с помощью функции sorted(), результат выводится сразу\n", + "sorted(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vS7G44sVs0jv", + "outputId": "14debf29-01e2-48ba-9d67-120ac5d35748" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[25, 10, 30, 20, 5, 15]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# исходный список при этом не изменился\n", + "nums" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qbi91ZlWv116", + "outputId": "09bd4d25-2a75-4517-a261-df619a283929" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 10, 15, 20, 25, 30]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если поместить результат в переменную, изменения сохранятся\n", + "sorted_nums = sorted(nums)\n", + "sorted_nums" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "J92cKG_ZsGJd" + }, + "outputs": [], + "source": [ + "# метод .sort() сохраняет результат, но не выводит его сразу\n", + "# reverse = True задает сортировку по убыванию\n", + "nums.sort(reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tMLloVoxsdcW", + "outputId": "fa528c25-7eea-4131-dc2a-5ac2758bcc1f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[30, 25, 20, 15, 10, 5]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# выведем результат\n", + "nums" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kU0tmatFulXn", + "outputId": "0ea3f3cf-fdd9-48dd-b7c5-b3aea0221c28" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 10, 15, 20, 25, 30]" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .reverse() задает обратный порядок, сохраняет, но не выводит результат\n", + "nums.reverse()\n", + "\n", + "# его также нужно вывести отдельно\n", + "nums" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BZfKDeRM3fbf", + "outputId": "163b97aa-7255-4e8d-fa57-4e932be32b20" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# функция reversed() возвращает итератор\n", + "reversed(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7piJhHSn8aUW", + "outputId": "e2da4c5b-f524-4926-ed00-b59dab91cab3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[30, 25, 20, 15, 10, 5]" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вывести результат можно с помощью функции list()\n", + "list(reversed(nums))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ldVNvlbg8v3_", + "outputId": "bd43b64e-2679-4060-bd33-cc634c6189f1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 10, 15, 20, 25, 30]" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# результат при этом не сохраняется\n", + "nums" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_Xyi6IdeuA-4" + }, + "source": [ + "Преобразование списка в строку" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iUoGSZMq0JFD" + }, + "outputs": [], + "source": [ + "# дан список из строковых элементов\n", + "str_list: list[str] = [\"P\", \"y\", \"t\", \"h\", \"o\", \"n\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "KuKfbsZLuER1", + "outputId": "70e66ff3-2188-42dc-e5cb-5a2f3d446c98" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Python'" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# с помощью метода .join() можно соединить все элементы\n", + "joined_str = \"\".join(str_list)\n", + "joined_str" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "WfMTmZcEzgky", + "outputId": "5e00ef97-a330-431f-c3c2-770330aaeaf3" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'P_y_t_h_o_n'" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если в кавычках ничего не указывать, элементы просто соединятся,\n", + "# но можно указать любой другой элемент\n", + "joined_str_ = \"_\".join(str_list)\n", + "joined_str_" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uEFM3lgzzr_k" + }, + "source": [ + "Арифметика в списках" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tGBnQ-mFzwJw" + }, + "outputs": [], + "source": [ + "# дан список чисел\n", + "nums_: list[int] = [3, 2, 1, 4, 5, 12, 3, 3, 7, 9, 11, 15]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VjwNZw2JzwUG", + "outputId": "e8586a86-a592-444e-9e3c-23ef494a040d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# с помощью метода .count() мы можем посчитать частоту вхождения\n", + "# элемента в список\n", + "nums_.count(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rrRSZEb13P0e", + "outputId": "d64be983-954e-44e2-cf3a-d801a61a848e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 15 75\n" + ] + } + ], + "source": [ + "# кроме того мы можем найти минимальное и максимальное значения\n", + "# и сумму элементов\n", + "print(min(nums_), max(nums_), sum(nums_))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zZKefwcpZ_rd" + }, + "source": [ + "List comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gKYUJua2aCdY" + }, + "outputs": [], + "source": [ + "# дан список имен\n", + "# оставим имена, начинающиеся с буквы \"А\"\n", + "names: list[str] = [\n", + " \"Артем\",\n", + " \"Антон\",\n", + " \"Александр\",\n", + " \"Борис\",\n", + " \"Виктор\",\n", + " \"Геннадий\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iZhNYZ7raCw_", + "outputId": "c8806615-90af-4669-da6e-ff3083e1dcba" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Артем', 'Антон', 'Александр']" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вначале решим эту задачу с помощью цикла for\n", + "\n", + "# создадим пустой список\n", + "a_names: list[str] = []\n", + "\n", + "# пройдемся по исходному списку в цикле for\n", + "for name in names:\n", + "\n", + " # с помощью метода .startswith() проверим, начинается ли слово с \"А\"\n", + " if name.startswith(\"А\"):\n", + "\n", + " # если да, добавим в новый список\n", + " a_names.append(name)\n", + "\n", + "# выведем результат\n", + "a_names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RfU5aFoia66k", + "outputId": "fb93053e-0d50-4df5-8941-2280ad774ed7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Артем', 'Антон', 'Александр']" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# эту же задачу можно решить через list comprehension\n", + "# по сути мы пишем: \"что сделать, пока есть элемент в списке, при каком условии\"\n", + "a_names = [name for name in names if name.startswith(\"А\")]\n", + "a_names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PtAJqkZRsQGM", + "outputId": "57ffe7da-07b4-4106-9226-e26ccf433fb0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['артем', 'антон', 'александр', 'борис', 'виктор', 'геннадий']" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# переведем все буквы в строчные, условие здесь не нужно\n", + "lower_names = [name.lower() for name in names]\n", + "lower_names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aj8uLZu9clcD", + "outputId": "558587b2-02a1-4339-dc8b-18e38a05d74a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Артем', 'Антон', 'Александр', 'Борис', 'Вадим', 'Геннадий']" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# схема условия if-else немного отличается\n", + "# оставляем имя, если это не Виктор, если Виктор - заменяем на Вадим\n", + "replace_name = [name if name != \"Виктор\" else \"Вадим\" for name in names]\n", + "replace_name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EiKriSA89dJk", + "outputId": "a6964157-1f0e-4cf7-f53f-0cadad414ce5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['pari', 'visit', 'lot', 'museum', 'first', 'went', 'louvr', 'largest', 'art', 'museum', 'world', 'alway', 'interest', 'art', 'spent', 'mani', 'hour', 'museum', 'enourm', 'week', 'would', 'enough']\n" + ] + } + ], + "source": [ + "# на занятии по обработке естественного языка с помощью list comprehension\n", + "# мы применили стеммер Портера к списку слов\n", + "lemmatized: list[str] = [\n", + " \"paris\",\n", + " \"visited\",\n", + " \"lot\",\n", + " \"museum\",\n", + " \"first\",\n", + " \"went\",\n", + " \"louvre\",\n", + " \"largest\",\n", + " \"art\",\n", + " \"museum\",\n", + " \"world\",\n", + " \"always\",\n", + " \"interested\",\n", + " \"art\",\n", + " \"spent\",\n", + " \"many\",\n", + " \"hour\",\n", + " \"museum\",\n", + " \"enormous\",\n", + " \"week\",\n", + " \"would\",\n", + " \"enough\",\n", + "]\n", + "\n", + "\n", + "porter = PorterStemmer()\n", + "\n", + "# применяем стеммер к элементу s, пока есть элементы s в списке lemmatized\n", + "stemmed_p = [porter.stem(s) for s in lemmatized]\n", + "print(stemmed_p)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kY91ANMfXeRG" + }, + "source": [ + "### Кортежи" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aF48XEbsCsV4" + }, + "source": [ + "Основы работы с кортежами" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qVBaa3SrtlYB", + "outputId": "c561c5ad-c089-450a-9300-7b1d5e2858e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "() ()\n" + ] + } + ], + "source": [ + "# пустой кортеж можно создать с помощью пустых круглых скобок ()\n", + "# или функции tuple()\n", + "tuple_1: tuple[()] = ()\n", + "tuple_2: tuple[str, ...] = tuple()\n", + "print(tuple_1, tuple_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "Vv-okCt8tlau", + "outputId": "09cb5284-ae26-4057-a206-7b414437d4f3" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'a'" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# в кортеже элементы упорядочены, а значит есть индекс\n", + "letters: tuple[str, ...] = (\"a\", \"b\", \"c\")\n", + "letters[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 159 + }, + "id": "3MERPn2I_o9D", + "outputId": "353f1a99-0e22-4353-9f24-537d0077cec4" + }, + "outputs": [], + "source": [ + "# но изменить элемент, как мы это делали в списке, нельзя\n", + "# letters[0] = \"d\" --> Error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3AKPc8KEBKDl", + "outputId": "87e678c8-043c-4f31-cef1-75655c3ec8db" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['d', 'b', 'c']" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для изменения элемента кортеж вначале нужно преобразовать в список\n", + "letters_list = list(letters)\n", + "letters_list[0] = \"d\"\n", + "letters_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NvRmYNY2C36p", + "outputId": "2c0a674d-e22c-4df9-b7e5-fc6faa7d995f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# кортеж из одного элемента можно создать с помощью запятой\n", + "letters_a: tuple[str] = (\"a\",)\n", + "type(letters_a)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D2vQPPY8C_2G", + "outputId": "a10456d8-ae19-4ed5-d5b3-8b44d8b3e29b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если запятую не указывать, получится строка\n", + "a_let = \"a\"\n", + "type(a_let)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4VBqLLmkCyGB" + }, + "source": [ + "Функция enumerate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2Al5QGWAC0sc", + "outputId": "827cc64c-71e4-48f1-cae6-cf6124d5505c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 'Microsoft') \n", + "(1, 'Apple') \n", + "(2, 'Tesla') \n" + ] + } + ], + "source": [ + "companies: list[str] = [\"Microsoft\", \"Apple\", \"Tesla\"]\n", + "\n", + "# если записать результат функции enumerate() в одну переменную,\n", + "for company in enumerate(companies):\n", + "\n", + " # то мы получим кортежи\n", + " print(company, type(company))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OHyjLQYRL-lz" + }, + "source": [ + "Просмотр элементов словаря" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LSkNG2x1MEWX" + }, + "outputs": [], + "source": [ + "shopping_dict: dict[str, int] = {\n", + " \"огурцы\": 2,\n", + " \"помидоры\": 3,\n", + " \"лук\": 1,\n", + " \"картофель\": 2,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d_s5V8U6NFAL", + "outputId": "9b6ec1e0-25b0-4dce-b3d0-6bd0af6ea425" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('огурцы', 2)\n", + "('помидоры', 3)\n", + "('лук', 1)\n", + "('картофель', 2)\n" + ] + } + ], + "source": [ + "# то же самое со словарем и методом .items()\n", + "for item in shopping_dict.items():\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GlSqhwQfJbCN" + }, + "source": [ + "Распаковка кортежей" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "h1hVafb_JaUn", + "outputId": "5692e7d9-0089-42d0-987a-b884b8b62c86" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a\n" + ] + } + ], + "source": [ + "# как и список, кортеж можно распаковать в несколько переменных\n", + "a_let, b_let, c_let = (\"a\", \"b\", \"c\")\n", + "print(a_let)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BFtyYKARKQPT", + "outputId": "bb7ed4cf-d02a-4219-e998-f386324a03f4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 Microsoft\n", + "1 Apple\n", + "2 Tesla\n" + ] + } + ], + "source": [ + "companies = [\"Microsoft\", \"Apple\", \"Tesla\"]\n", + "\n", + "# распаковку в две переменные с функцией enumerate() мы делать уже умеем\n", + "for index, company_name in enumerate(companies):\n", + " print(index, company_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pO8swT03UAeV", + "outputId": "0b68f3ab-bd57-4526-b439-9c913772ea6d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "огурцы 2\n", + "помидоры 3\n", + "лук 1\n", + "картофель 2\n" + ] + } + ], + "source": [ + "shopping_dict = {\n", + " \"огурцы\": 2,\n", + " \"помидоры\": 3,\n", + " \"лук\": 1,\n", + " \"картофель\": 2,\n", + "}\n", + "\n", + "# то же самое с ключами и значениями словаря\n", + "for key, value in shopping_dict.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BZRw7ZcPCwGh" + }, + "source": [ + "Функция zip()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ti6oDrQjCvhb", + "outputId": "bbb46ac5-a1fa-4925-cced-7e3f2cd4e52e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если есть два и более списка\n", + "names = [\n", + " \"Артем\",\n", + " \"Антон\",\n", + " \"Александр\",\n", + " \"Борис\",\n", + " \"Виктор\",\n", + " \"Геннадий\",\n", + "]\n", + "income: list[int] = [97000, 110000, 95000, 84000, 140000, 120000]\n", + "\n", + "# функция zip() соединит первые элементы списков,\n", + "# вторые элементы списков и т.д.\n", + "zip(names, income)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "S84QKkqDHztA", + "outputId": "886a7956-4213-4349-fa15-8063782c15b3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Артем', 97000),\n", + " ('Антон', 110000),\n", + " ('Александр', 95000),\n", + " ('Борис', 84000),\n", + " ('Виктор', 140000),\n", + " ('Геннадий', 120000)]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для вывода результата нужно передать zip-объект в функцию list()\n", + "# на выходе мы получим список из кортежей\n", + "print(list(zip(names, income)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xLJn10TGXhlq" + }, + "source": [ + "### Множества" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "69RH3VDxWu_5" + }, + "source": [ + "Создание множества" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7T9QjMN_tn5Q", + "outputId": "67e65595-422c-408e-ec10-a86901243173" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "set() {'a', 'e', 'c', 'b', 'd'} {'a', 'b', 'c'}\n" + ] + } + ], + "source": [ + "# пустое множество задается через функцию set()\n", + "set_1: set[str] = set()\n", + "\n", + "# непустое множество задается через функцию set() и итератор\n", + "# например, список элементов или строку\n", + "set_2: set[str] = set(\"abcde\") # pylint: disable=W0130\n", + "\n", + "# или путем перечисления элементов в фигурных скобках {}\n", + "set_3: set[str] = {\"a\", \"b\", \"c\", \"c\"} # pylint: disable=W0130\n", + "\n", + "# множество содержит только уникальные элементы, поэтому дубликаты удаляются\n", + "print(set_1, set_2, set_3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k4sVIWhxtn8N", + "outputId": "0d134a34-16ac-4421-c7fd-13c6492f55bc" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создать множество через пустые фигурные скобки нельзя\n", + "not_a_set: dict[str, str] = {}\n", + "\n", + "# так создается словарь\n", + "type(not_a_set)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tHsQ7_dCXQkJ" + }, + "source": [ + "Добавление и удаление элементов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VMdDL588YmOm" + }, + "outputs": [], + "source": [ + "# предположим, что мы хотим создать множество гласных букв в русском языке\n", + "vowels: set[str] = {\"а\", \"о\", \"э\", \"е\", \"у\", \"ё\", \"ю\"}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "A3b7LnrTuxu9", + "outputId": "5a6319ac-77ea-435c-884d-b16b4e9f3649" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'а', 'е', 'о', 'у', 'э', 'ю', 'я', 'ё'}" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим одну букву \"я\" методом .add()\n", + "vowels.add(\"я\")\n", + "vowels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RuCG-RnTk9SZ", + "outputId": "d18e632c-25d8-42a9-e441-084807cbcd20" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'а', 'е', 'и', 'о', 'у', 'ы', 'э', 'ю', 'я', 'ё'}" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим две буквы \"и\" и \"ы\" методом .update()\n", + "vowels.update([\"и\", \"ы\"])\n", + "vowels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6OJBJ5cYvwrl", + "outputId": "0693321a-76a4-4a8a-deda-d3c8b5abd101" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'а', 'е', 'и', 'о', 'у', 'щ', 'ы', 'э', 'ю', 'я', 'ё'}" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если по ошибке мы добавим согласную букву,\n", + "vowels.add(\"щ\")\n", + "vowels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VTCYCiXZv4Wc", + "outputId": "5698ddec-d2a8-4c89-e9d9-d8fc91fb6b2e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'а', 'е', 'и', 'о', 'у', 'ы', 'э', 'ю', 'я', 'ё'}" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# ее можно удалить методом .remove()\n", + "vowels.remove(\"щ\")\n", + "vowels" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uB1je436aY19" + }, + "source": [ + "Теория множеств в Питоне" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ScRveXFjagly", + "outputId": "2767f29b-0f54-492d-f9dd-3a0cccbe6048" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# два множества равны, если содержат одинаковые элементы,\n", + "# при этом порядок элементов не важен\n", + "print({\"a\", \"b\", \"c\"} == {\"c\", \"b\", \"a\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "O5_E1AS9JOL2", + "outputId": "807df047-e851-4a72-9035-c72316d33b59" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# выведем мощность множества с помощью функции len()\n", + "print(len({\"a\", \"b\", \"c\"}))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tktbnMu4JOOY", + "outputId": "2f201ee0-3b16-4ff6-9121-de1f0155834f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# проверим, содержится ли элемент во множестве\n", + "print(\"a\" in {\"a\", \"b\", \"c\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7LjJsYc2JORQ", + "outputId": "3d5cd120-68d9-450c-ba02-c95fb0c3f051" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# возможна и обратная операция\n", + "print(\"a\" not in {\"a\", \"b\", \"c\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MF1nGQvDJOUI", + "outputId": "95c006ba-48be-4766-fa53-293ecdb1ec27" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# проверим является ли А подмножеством В\n", + "set_a: set[str] = {\"a\", \"b\", \"c\"}\n", + "set_b: set[str] = {\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}\n", + "\n", + "set_a.issubset(set_b)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ne2zLJy3DMT7", + "outputId": "96bcbed1-0112-40f7-f012-149a229f67af" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# проверим является ли B надмножеством А\n", + "set_b.issuperset(set_a)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0MxJj85iDc_y" + }, + "outputs": [], + "source": [ + "# даны участники команд по обработке естественного языка (nlp)\n", + "# и компьютерному зрению (cv)\n", + "nlp: set[str] = {\"Анна\", \"Николай\", \"Павел\", \"Оксана\"}\n", + "cv: set[str] = {\"Николай\", \"Евгений\", \"Ольга\", \"Оксана\"}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ipXl-eO5EFKS", + "outputId": "bc4c9d32-c49f-473e-ff79-5631038a2e61" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Павел', 'Евгений', 'Оксана', 'Николай', 'Анна', 'Ольга'}\n", + "{'Павел', 'Евгений', 'Оксана', 'Николай', 'Анна', 'Ольга'}\n" + ] + } + ], + "source": [ + "# найдем тех, кто работает или в nlp, или в cv, или в обеих командах\n", + "\n", + "# можно использовать метод .union()\n", + "print(nlp.union(cv))\n", + "\n", + "# или символ |\n", + "print(nlp | cv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vkUEl3eeEnPn", + "outputId": "3606efdb-d621-4268-b072-7c05e676054b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Оксана', 'Николай'}\n", + "{'Оксана', 'Николай'}\n" + ] + } + ], + "source": [ + "# найдем пересечение множеств, то есть тех, кто работает и в nlp, и в cv\n", + "print(nlp.intersection(cv))\n", + "print(nlp & cv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mNV8c0hQExzL", + "outputId": "0a71a036-d2a9-4728-9f4a-adff5df74b4b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Анна', 'Павел'}\n", + "{'Анна', 'Павел'}\n" + ] + } + ], + "source": [ + "# выведем тех, кто работает только в nlp, но не в cv или cv и nlp одновременно\n", + "print(nlp.difference(cv))\n", + "print(nlp - cv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RnawDdtME8T4", + "outputId": "9691b8dd-bc78-428c-ec7f-322a316f33ba" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Евгений', 'Ольга'}\n", + "{'Евгений', 'Ольга'}\n" + ] + } + ], + "source": [ + "# выведем тех, кто работает только в cv,\n", + "# но не в nlp или nlp и cv одновременно\n", + "print(cv.difference(nlp))\n", + "print(cv - nlp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sHIWgu4ZFEqv", + "outputId": "8f8c106c-3186-4b23-f95c-5d508a708b65" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Павел', 'Евгений', 'Анна', 'Ольга'}\n", + "{'Павел', 'Евгений', 'Анна', 'Ольга'}\n" + ] + } + ], + "source": [ + "# найдем тех, кто работает или в cv, или в nlp,\n", + "# но не в обеих областях одновременно\n", + "print(nlp.symmetric_difference(cv))\n", + "print(nlp ^ cv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8uLziVR-Z5mU" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_08_lists_tuples_sets.py b/Python/makarov/chapter_08_lists_tuples_sets.py new file mode 100644 index 00000000..e83b2b40 --- /dev/null +++ b/Python/makarov/chapter_08_lists_tuples_sets.py @@ -0,0 +1,583 @@ +# %% +"""Макаров. + +Списки, кортежи и множества. +""" + +# ## Списки, кортежи и множества + +# ### Списки + +# Основы работы со списками + +# %% +# пустой список можно создать через [] или функцию list() +# импортируем класс стеммера и создаем объект +from nltk.stem import PorterStemmer + +some_list_1: list[str] = [] +some_list_2: list[str] = list() # pylint: disable=R1734 + +print(some_list_1, some_list_2) + +# %% +# элементами списка могут, в частности, быть числа, строки, +# другие списки и словари +number_three: list[int | str | list[str] | dict[str, int]] = [ + 3, + "число три", + ["число", "три"], + {"число": 3}, +] +number_three + +# %% +# длина списка рассчитывается через функцию len() +len(number_three) + +# Индекс и срез списка + +# %% +# у списка есть положительный и отрицательный индексы +abc_list: list[str] = ["a", "b", "c", "d", "e"] + +# воспользуемся ими для вывода первого и последнего элементов +print(abc_list[0], abc_list[-1]) + +# %% +# при работе с вложенным списком +salary_list: list[list[str | int]] = [ + ["Анна", 90000], + ["Игорь", 85000], + ["Алексей", 95000], +] + +# мы вначале указываем индекс вложенного списка, а затем индекс элемента в нем +salary_list[1][0] + +# %% +# индекс можно узнать с помощью метода .index() +abc_list.index("c") + +# %% +# метод .index() можно применить и ко вложенному списку +salary_list[0].index(90000) + +# %% +# создадим список с днями недели +days_list: list[str] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] + +# и выведем со второго по пятый элемент включительно +days_list[1:5] + +# %% +# выведем каждый второй элемент в срезе с первого по пятый +days_list[:5:2] + +# %% +# проверим есть ли "Пн" в списке +"Пн" in days_list + +# %% +# если "Вт" есть в списке +if "Вт" in days_list: + + # выведем сообщение + print("Такое слово есть") + +# Добавление, замена и удаление элементов списка + +# %% +# создадим список +weekdays: list[str] = ["Понедельник", "Вторник"] + +# %% +# добавим один элемент в конец списка с помощью метода .append() +weekdays.append("Четверг") +weekdays + +# %% +# добавим элемент в определенное место в списке +# через желаемый индекс этого элемента +weekdays.insert(2, "Среда") +weekdays + +# %% +# изменим четвертый элемент в списке +weekdays[3] = "Пятница" +weekdays + +# %% +# удалим элемент по его значению +weekdays.remove("Пятница") +weekdays + +# %% +# удалим элемент по его индексу через ключевое слово del +del weekdays[2] +weekdays + +# %% +# сделаем то же самое с помощью метода .pop() +# этот метод выводит удаляемый элемент +weekdays.pop(1) + +# %% +# посмотрим, что осталось в нашем списке +weekdays + +# Сложение списков + +# %% +# соединить два списка можно с помощью метода .extend() +more_weekdays: list[str] = ["Вторник", "Среда", "Четверг", "Пятница"] + +weekdays.extend(more_weekdays) +weekdays + +# %% +weekend: list[str] = ["Суббота", "Воскресенье"] + +# или просто сложив два списка +print(weekdays + weekend) + +# %% +# иногда бывает полезно "размножить" элементы списка +print(["Понедельник"] * 2) + +# %% +# такие "произведения" также можно складывать +print(["Понедельник"] * 2 + ["Вторник"] * 2) + +# Распаковка списков + +# %% +# дан список +# "_" добавлен для того, чтобы избежать дублирования кода +# и как следствие ошибок pylint +week: list[str] = [ + "Понедельник_", + "Вторник_", + "Среда_", + "Четверг_", + "Пятница_", + "Суббота_", + "Воскресенье_", +] + +# %% +# указав индекс элемента, его можно записать в переменную +monday = week[0] +monday + +# %% +# срез можно поместить в несколько переменных +monday, tuesday, wednesday = week[:3] + +# важно, чтобы количество элементов среза было равно количеству переменных +monday, tuesday, wednesday + +# %% +# можно выделить первый элемент, а остальные поместить +# в переменную со звездочкой +monday, *rest = week +monday + +# %% +# также можно поступить, например, с первым и последним элементом +monday, *days, sunday = week +monday, sunday + +# %% +# посмотрим, какие элементы остались в переменной со звездочкой +days + +# Сортировка списков + +# %% +# возьмем список чисел +nums: list[int] = [25, 10, 30, 20, 5, 15] + +# %% +# и отсортируем с помощью функции sorted(), результат выводится сразу +sorted(nums) + +# %% +# исходный список при этом не изменился +nums + +# %% +# если поместить результат в переменную, изменения сохранятся +sorted_nums = sorted(nums) +sorted_nums + +# %% +# метод .sort() сохраняет результат, но не выводит его сразу +# reverse = True задает сортировку по убыванию +nums.sort(reverse=True) + +# %% +# выведем результат +nums + +# %% +# метод .reverse() задает обратный порядок, сохраняет, но не выводит результат +nums.reverse() + +# его также нужно вывести отдельно +nums + +# %% +# функция reversed() возвращает итератор +reversed(nums) + +# %% +# вывести результат можно с помощью функции list() +list(reversed(nums)) + +# %% +# результат при этом не сохраняется +nums + +# Преобразование списка в строку + +# %% +# дан список из строковых элементов +str_list: list[str] = ["P", "y", "t", "h", "o", "n"] + +# %% +# с помощью метода .join() можно соединить все элементы +joined_str = "".join(str_list) +joined_str + +# %% +# если в кавычках ничего не указывать, элементы просто соединятся, +# но можно указать любой другой элемент +joined_str_ = "_".join(str_list) +joined_str_ + +# Арифметика в списках + +# %% +# дан список чисел +nums_: list[int] = [3, 2, 1, 4, 5, 12, 3, 3, 7, 9, 11, 15] + +# %% +# с помощью метода .count() мы можем посчитать частоту вхождения +# элемента в список +nums_.count(3) + +# %% +# кроме того мы можем найти минимальное и максимальное значения +# и сумму элементов +print(min(nums_), max(nums_), sum(nums_)) + +# List comprehension + +# %% +# дан список имен +# оставим имена, начинающиеся с буквы "А" +names: list[str] = [ + "Артем", + "Антон", + "Александр", + "Борис", + "Виктор", + "Геннадий", +] + +# вначале решим эту задачу с помощью цикла for + +# создадим пустой список +a_names: list[str] = [] + +# пройдемся по исходному списку в цикле for +for name in names: + + # с помощью метода .startswith() проверим, начинается ли слово с "А" + if name.startswith("А"): + + # если да, добавим в новый список + a_names.append(name) + +# выведем результат +a_names + +# %% +# эту же задачу можно решить через list comprehension +# по сути мы пишем: "что сделать, пока есть элемент в списке, при каком условии" +a_names = [name for name in names if name.startswith("А")] +a_names + +# %% +# переведем все буквы в строчные, условие здесь не нужно +lower_names = [name.lower() for name in names] +lower_names + +# %% +# схема условия if-else немного отличается +# оставляем имя, если это не Виктор, если Виктор - заменяем на Вадим +replace_name = [name if name != "Виктор" else "Вадим" for name in names] +replace_name + +# %% +# на занятии по обработке естественного языка с помощью list comprehension +# мы применили стеммер Портера к списку слов +lemmatized: list[str] = [ + "paris", + "visited", + "lot", + "museum", + "first", + "went", + "louvre", + "largest", + "art", + "museum", + "world", + "always", + "interested", + "art", + "spent", + "many", + "hour", + "museum", + "enormous", + "week", + "would", + "enough", +] + + +porter = PorterStemmer() + +# применяем стеммер к элементу s, пока есть элементы s в списке lemmatized +stemmed_p = [porter.stem(s) for s in lemmatized] +print(stemmed_p) + +# ### Кортежи + +# Основы работы с кортежами + +# %% +# пустой кортеж можно создать с помощью пустых круглых скобок () +# или функции tuple() +tuple_1: tuple[()] = () +tuple_2: tuple[str, ...] = tuple() +print(tuple_1, tuple_2) + +# %% +# в кортеже элементы упорядочены, а значит есть индекс +letters: tuple[str, ...] = ("a", "b", "c") +letters[0] + +# но изменить элемент, как мы это делали в списке, нельзя +# letters[0] = "d" --> Error + +# %% +# для изменения элемента кортеж вначале нужно преобразовать в список +letters_list = list(letters) +letters_list[0] = "d" +letters_list + +# %% +# кортеж из одного элемента можно создать с помощью запятой +letters_a: tuple[str] = ("a",) +type(letters_a) + +# %% +# если запятую не указывать, получится строка +a_let = "a" +type(a_let) + +# Функция enumerate() + +# %% +companies: list[str] = ["Microsoft", "Apple", "Tesla"] + +# если записать результат функции enumerate() в одну переменную, +for company in enumerate(companies): + + # то мы получим кортежи + print(company, type(company)) + +# Просмотр элементов словаря + +# %% +shopping_dict: dict[str, int] = { + "огурцы": 2, + "помидоры": 3, + "лук": 1, + "картофель": 2, +} + +# %% +# то же самое со словарем и методом .items() +for item in shopping_dict.items(): + print(item) + +# Распаковка кортежей + +# %% +# как и список, кортеж можно распаковать в несколько переменных +a_let, b_let, c_let = ("a", "b", "c") +print(a_let) + +# %% +companies = ["Microsoft", "Apple", "Tesla"] + +# распаковку в две переменные с функцией enumerate() мы делать уже умеем +for index, company_name in enumerate(companies): + print(index, company_name) + +# %% +shopping_dict = { + "огурцы": 2, + "помидоры": 3, + "лук": 1, + "картофель": 2, +} + +# то же самое с ключами и значениями словаря +for key, value in shopping_dict.items(): + print(key, value) + +# Функция zip() + +# %% +# если есть два и более списка +names = [ + "Артем", + "Антон", + "Александр", + "Борис", + "Виктор", + "Геннадий", +] +income: list[int] = [97000, 110000, 95000, 84000, 140000, 120000] + +# функция zip() соединит первые элементы списков, +# вторые элементы списков и т.д. +zip(names, income) + +# %% +# для вывода результата нужно передать zip-объект в функцию list() +# на выходе мы получим список из кортежей +print(list(zip(names, income))) + +# ### Множества + +# Создание множества + +# %% +# пустое множество задается через функцию set() +set_1: set[str] = set() + +# непустое множество задается через функцию set() и итератор +# например, список элементов или строку +set_2: set[str] = set("abcde") # pylint: disable=W0130 + +# или путем перечисления элементов в фигурных скобках {} +set_3: set[str] = {"a", "b", "c", "c"} # pylint: disable=W0130 + +# множество содержит только уникальные элементы, поэтому дубликаты удаляются +print(set_1, set_2, set_3) + +# %% +# создать множество через пустые фигурные скобки нельзя +not_a_set: dict[str, str] = {} + +# так создается словарь +type(not_a_set) + +# Добавление и удаление элементов + +# %% +# предположим, что мы хотим создать множество гласных букв в русском языке +vowels: set[str] = {"а", "о", "э", "е", "у", "ё", "ю"} + +# %% +# добавим одну букву "я" методом .add() +vowels.add("я") +vowels + +# %% +# добавим две буквы "и" и "ы" методом .update() +vowels.update(["и", "ы"]) +vowels + +# %% +# если по ошибке мы добавим согласную букву, +vowels.add("щ") +vowels + +# %% +# ее можно удалить методом .remove() +vowels.remove("щ") +vowels + +# Теория множеств в Питоне + +# %% +# два множества равны, если содержат одинаковые элементы, +# при этом порядок элементов не важен +print({"a", "b", "c"} == {"c", "b", "a"}) + +# %% +# выведем мощность множества с помощью функции len() +print(len({"a", "b", "c"})) + +# %% +# проверим, содержится ли элемент во множестве +print("a" in {"a", "b", "c"}) + +# %% +# возможна и обратная операция +print("a" not in {"a", "b", "c"}) + +# %% +# проверим является ли А подмножеством В +set_a: set[str] = {"a", "b", "c"} +set_b: set[str] = {"a", "b", "c", "d", "e", "f"} + +set_a.issubset(set_b) + +# %% +# проверим является ли B надмножеством А +set_b.issuperset(set_a) + +# %% +# даны участники команд по обработке естественного языка (nlp) +# и компьютерному зрению (cv) +nlp: set[str] = {"Анна", "Николай", "Павел", "Оксана"} +cv: set[str] = {"Николай", "Евгений", "Ольга", "Оксана"} + +# найдем тех, кто работает или в nlp, или в cv, или в обеих командах + +# можно использовать метод .union() +print(nlp.union(cv)) + +# или символ | +print(nlp | cv) + +# %% +# найдем пересечение множеств, то есть тех, кто работает и в nlp, и в cv +print(nlp.intersection(cv)) +print(nlp & cv) + +# %% +# выведем тех, кто работает только в nlp, но не в cv или cv и nlp одновременно +print(nlp.difference(cv)) +print(nlp - cv) + +# %% +# выведем тех, кто работает только в cv, +# но не в nlp или nlp и cv одновременно +print(cv.difference(nlp)) +print(cv - nlp) + +# %% +# найдем тех, кто работает или в cv, или в nlp, +# но не в обеих областях одновременно +print(nlp.symmetric_difference(cv)) +print(nlp ^ cv) diff --git a/Python/makarov/chapter_09_dictionaries.ipynb b/Python/makarov/chapter_09_dictionaries.ipynb new file mode 100644 index 00000000..2055f74d --- /dev/null +++ b/Python/makarov/chapter_09_dictionaries.ipynb @@ -0,0 +1,2758 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Словарь в Питоне\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0AoPSXyUTTLc" + }, + "source": [ + "## Словарь в Питоне" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lIM5gFo4TbUg" + }, + "source": [ + "### Понятие словаря" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AEHY4SW_3mIj" + }, + "source": [ + "#### Создание словаря" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "x3wxNu5jthst", + "outputId": "eedbd51a-e6ce-4a0f-ac35-7d57c4cc59ef" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{} {}\n" + ] + } + ], + "source": [ + "# пустой словарь можно создать с помощью {} или функции dict()\n", + "# импортируем класс Counter\n", + "from collections import Counter\n", + "\n", + "# импортируем функцию pprint() из модуля pprint\n", + "# некоторые структуры данных она выводит лучше, чем обычная print()\n", + "from pprint import pprint\n", + "\n", + "import numpy as np\n", + "\n", + "dict_1: dict[str, str] = {}\n", + "dict_2: dict[str, str] = dict() # pylint: disable=R1735\n", + "print(dict_1, dict_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tLWO1t--thwP", + "outputId": "c802fcb0-3757-40ab-94c2-44fae324b0db" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Toyota', 'founded': 1937, 'founder': 'Kiichiro Toyoda'}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# словарь можно сразу заполнить ключами и значениями\n", + "company: dict[str, str | int] = {\n", + " \"name\": \"Toyota\",\n", + " \"founded\": 1937,\n", + " \"founder\": \"Kiichiro Toyoda\",\n", + "}\n", + "company" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pBONkfBYZRkZ", + "outputId": "f364cd9b-bbac-4011-e7f4-069889ac14df" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'TYO': 'Toyota', 'TSLA': 'Tesla', 'F': 'Ford'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# словарь можно создать из вложенных списков\n", + "tickers: dict[str, str] = dict(\n", + " [[\"TYO\", \"Toyota\"], [\"TSLA\", \"Tesla\"], [\"F\", \"Ford\"]]\n", + ")\n", + "tickers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "y5zC4qKWbUn3", + "outputId": "becdc0ee-1018-4dee-d6e9-9a861791c5c7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k1': 0, 'k2': 0, 'k3': 0}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если поместить ключи в кортеж\n", + "keys: tuple[str, ...] = (\"k1\", \"k2\", \"k3\")\n", + "# и задать значение\n", + "value_global = 0\n", + "\n", + "# то с помощью метода .fromkeys() можно создать словарь\n", + "# с этими ключами и заданным значением для каждого из них\n", + "empty_values = dict.fromkeys(keys, value_global)\n", + "empty_values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SdW5ePii6BGZ" + }, + "source": [ + "#### Ключи и значения словаря" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W9vaAXb2zKVb" + }, + "source": [ + "Виды значений словаря" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d0bh3eRu6EAy", + "outputId": "ef5c819c-8445-4b53-d843-19154d4f3e47" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k1': 123,\n", + " 'k2': 'string',\n", + " 'k3': nan,\n", + " 'k4': True,\n", + " 'k5': None,\n", + " 'k6': [1, 2, 3],\n", + " 'k7': array([1, 2, 3]),\n", + " 'k8': {1: 'v1', 2: 'v2', 3: 'v3'}}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# приведем пример того, какими могут быть значения словаря\n", + "value_types: dict[\n", + " str, int | str | bool | None | list[int] | object | float\n", + "] = {\n", + " \"k1\": 123,\n", + " \"k2\": \"string\",\n", + " \"k3\": np.nan, # тип \"Пропущенное значение\"\n", + " \"k4\": True, # логическое значение\n", + " \"k5\": None,\n", + " \"k6\": [1, 2, 3],\n", + " \"k7\": np.array([1, 2, 3]),\n", + " \"k8\": {1: \"v1\", 2: \"v2\", 3: \"v3\"},\n", + "}\n", + "\n", + "value_types" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AktD-qd772bs" + }, + "source": [ + "Методы .keys(), .values() и .items()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FoLHc64C716M" + }, + "outputs": [], + "source": [ + "# создадим несложный словарь с информацией о сотруднике\n", + "person: dict[str, str | int | list[str]] = {\n", + " \"first name\": \"Иван\",\n", + " \"last name\": \"Иванов\",\n", + " \"born\": 1980,\n", + " \"dept\": \"IT\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8Gl7ZMqt8wuZ", + "outputId": "708807b9-c8e5-478c-ac25-baee23c1a0ad" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['first name', 'last name', 'born', 'dept'])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на ключи и\n", + "print(person.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "j3mruPRR8xgX", + "outputId": "06bbf715-5091-45c5-d84a-8bfcdefdcf76" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_values(['Иван', 'Иванов', 1980, 'IT'])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# значения\n", + "print(person.values())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wfmjr1bu81Ay", + "outputId": "53c39b8e-dc1f-4be5-800b-97f534efc66c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('first name', 'Иван'), ('last name', 'Иванов'), ('born', 1980), ('dept', 'IT')])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# а также на пары ключ-значение в виде списка из кортежей\n", + "print(person.items())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YxrdHML389v4" + }, + "source": [ + "Использование цикла for" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ssN6LrjO83h8", + "outputId": "1d079d55-69b3-4f88-8f58-d34896f7945e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first name Иван\n", + "last name Иванов\n", + "born 1980\n", + "dept IT\n" + ] + } + ], + "source": [ + "# ключи и значения можно вывести в цикле for\n", + "for key, value_p in person.items():\n", + " print(key, value_p)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zU-JtgYJ-vyO" + }, + "source": [ + "Доступ по ключу и метод .get()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "Au-MDTwZ-tGI", + "outputId": "3ba22481-65b9-43b9-8be3-e68bc38d87a1" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Иванов'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# значение можно посмотреть по ключу\n", + "person[\"last name\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 159 + }, + "id": "XwWDSYzH-O0l", + "outputId": "dbc6b773-bbcd-45f9-c0ac-bab705e05338" + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'education'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# если такого ключа нет, Питон выдаст ошибку\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mperson\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'education'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 'education'" + ] + } + ], + "source": [ + "# если такого ключа нет, Питон выдаст ошибку\n", + "# person['education'] --> Error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dx2nFVH7eils", + "outputId": "b9ff14d3-4bea-41c7-b838-4560ddd3d228" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "# чтобы этого не произошло, можно использовать метод .get()\n", + "# по умолчанию при отсутствии ключа он выводит значение None\n", + "print(person.get(\"education\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qTovRCnIb1ei", + "outputId": "e0bbb24f-00fb-4c43-f611-fc4ad8866598" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1980" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если ключ все-таки есть, .get() выведет соответствующее значение\n", + "print(person.get(\"born\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IBgf5Al-aCOK" + }, + "source": [ + "Проверка вхождения ключа и значения в словарь" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XbRxj_nPaHJU", + "outputId": "faa46646-4e20-432b-dbe8-c68e06ff9699" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# проверим есть ли такой ключ\n", + "print(\"born\" in person)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sAKfv79KbC1p", + "outputId": "3f9562bd-fab8-4a29-abee-ec82d9d85fae" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и такое значение\n", + "print(1980 in person.values())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UelKiVrybH30", + "outputId": "0f5eacb2-eb21-4ef2-b818-9e66500e1227" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# можно также проверить наличие и ключа, и значения одновременно\n", + "print((\"born\", 1980) in person.items())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kY91ANMfXeRG" + }, + "source": [ + "### Операции со словарями" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ebptqYBRcrJp" + }, + "source": [ + "#### Добавление и изменение элементов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qVBaa3SrtlYB", + "outputId": "83fbde38-1fff-4e8c-c2c5-5de94465eca9" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'dept': 'IT',\n", + " 'languages': ['Python', 'C++']}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавить элемент можно, передав новому ключу новое значение\n", + "# обратите внимание, в данном случае новое значение - это список\n", + "person[\"languages\"] = [\"Python\", \"C++\"]\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vv-okCt8tlau", + "outputId": "74f5f097-72cc-4539-ba8c-f4baf7223a83" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'dept': 'IT',\n", + " 'languages': ['Python']}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# изменить элемент можно, передав существующему ключу новое значение,\n", + "# значение - это по-прежнему список, но из одного элемента\n", + "person[\"languages\"] = [\"Python\"]\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FBO1GOoDHXjc", + "outputId": "87efcd08-cdd8-47cb-ac39-52807319d17c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'dept': 'IT',\n", + " 'languages': ['Python'],\n", + " 'job': 'программист',\n", + " 'experience': 7}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# возьмем еще один словарь\n", + "new_elements: dict[str, str | int] = {\"job\": \"программист\", \"experience\": 7}\n", + "\n", + "# и присоединим его к существующему словарю с помощью метода .update()\n", + "person.update(new_elements)\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8ABSReiDS86F", + "outputId": "f7c2c895-86fc-451c-a619-56514d1b4ad2" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'dept': 'IT',\n", + " 'languages': ['Python'],\n", + " 'job': 'программист',\n", + " 'experience': 7}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .setdefault() проверит есть ли ключ в словаре,\n", + "# если \"да\", значение не изменится\n", + "person.setdefault(\"last name\", \"Петров\")\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uq1eR4FEYIFx", + "outputId": "b9ca0002-c8bc-458c-b978-c86413159dfb" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'dept': 'IT',\n", + " 'languages': ['Python'],\n", + " 'job': 'программист',\n", + " 'experience': 7,\n", + " 'f_languages': ['русский', 'английский']}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# если нет, будет добавлен новый ключ и соответствующее значение\n", + "person.setdefault(\"f_languages\", [\"русский\", \"английский\"])\n", + "person" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wUMTWNK-fFsd" + }, + "source": [ + "#### Удаление элементов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "AKViA8ItfIye", + "outputId": "037a5a8d-0b08-4c4a-aec2-29e4eea7e49e" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'IT'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .pop() удаляет элемент по ключу и выводит удаляемое значение\n", + "print(person.pop(\"dept\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "anOK_5RVh8eG", + "outputId": "9a696c51-117c-42cc-aa12-3049452b3111" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first name': 'Иван',\n", + " 'last name': 'Иванов',\n", + " 'born': 1980,\n", + " 'languages': ['Python'],\n", + " 'job': 'программист',\n", + " 'experience': 7,\n", + " 'f_languages': ['русский', 'английский']}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# мы видим, что пары 'dept' : 'IT' больше нет\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "t6flk9z_fluM" + }, + "outputs": [], + "source": [ + "# ключевое слово del также удаляет элемент по ключу\n", + "# удаляемое значение не выводится\n", + "del person[\"born\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6aruZx9MKBUK", + "outputId": "0334ba54-fb22-460f-a83a-99ce528947be" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('f_languages', ['русский', 'английский'])" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .popitem() удаляет последний добавленный элемент и выводит его\n", + "person.popitem()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tH_TmTAgf1Qc", + "outputId": "77b794f7-1026-4973-b30b-835773e2aa9d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# метод .clear() удаляет все элементы словаря\n", + "person.clear()\n", + "person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KQzvF_SmHFYa" + }, + "outputs": [], + "source": [ + "# ключевое слово del также позволяет удалить словарь целиком\n", + "del person" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 159 + }, + "id": "41jpFZaGA3U3", + "outputId": "659769e5-c524-40b6-cc40-e0253b2ab52f" + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'person' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# убедимся, что такого словаря больше нет\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mperson\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'person' is not defined" + ] + } + ], + "source": [ + "# убедимся, что такого словаря больше нет\n", + "person" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VS8k8X8kwAhj" + }, + "source": [ + "#### Сортировка словарей" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u3rg89UKwE1e" + }, + "outputs": [], + "source": [ + "# возьмем несложный словарь\n", + "dict_to_sort: dict[str, int] = {\"k2\": 30, \"k1\": 20, \"k3\": 10}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bTwIep0qwFW8", + "outputId": "117cde36-cd88-49b3-ba59-7b52cb3e70d1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['k1', 'k2', 'k3']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# отсортируем ключи\n", + "sorted(dict_to_sort)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tnJvCeBxna2q", + "outputId": "544cdf21-7661-4fb1-cdcb-c1d39fec5eab" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20, 30]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# и значения\n", + "sorted(dict_to_sort.values())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jYQNHiJrMhSA", + "outputId": "cbfcc983-24f9-4c9f-bea1-f2c5dcfd806d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('k2', 30), ('k1', 20), ('k3', 10)])" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# посмотрим на пары ключ : значение\n", + "dict_to_sort.items()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2Lbe96f0nh85", + "outputId": "3a86244b-ab49-4aad-b350-58d7abd88883" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('k1', 20), ('k2', 30), ('k3', 10)]" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для их сортировки по ключу (индекс [0])\n", + "# воспользуемся методом .items() и lambda-функцией\n", + "sorted(dict_to_sort.items(), key=lambda item: item[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vl9gs9fVoE-A", + "outputId": "bd90cb3f-4e6a-4c6f-91e8-77b84df8b85f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('k3', 10), ('k1', 20), ('k2', 30)]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# сортировка по значению выполняется так же, однако\n", + "# lambda-функции мы передаем индекс [1]\n", + "sorted(dict_to_sort.items(), key=lambda item: item[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v9aK8-q2CdXO" + }, + "source": [ + "#### Копирование словарей" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ysVmXSyVFZMU" + }, + "outputs": [], + "source": [ + "# создадим исходный словарь с количеством студентов на первом и втором курсах университета\n", + "original: dict[str, int] = {\"Первый курс\": 174, \"Второй курс\": 131}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DYkMoaN3GL1X" + }, + "source": [ + "Копирование с помощью метода .copy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-t7mXlsUCc2p", + "outputId": "877c2f4b-629f-48d4-cca6-3a0fa71151f1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Первый курс': 174, 'Второй курс': 131}\n", + "{'Первый курс': 174, 'Второй курс': 131, 'Третий курс': 117}\n" + ] + } + ], + "source": [ + "# создадим копию этого словаря с помощью метода .copy()\n", + "new_1 = original.copy()\n", + "\n", + "# добавим информацию о третьем курсе в новый словарь\n", + "new_1[\"Третий курс\"] = 117\n", + "\n", + "# исходный словарь не изменился\n", + "print(original)\n", + "print(new_1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h9IslECkGSsr" + }, + "source": [ + "Копирование через оператор присваивания `=` (так делать не стоит!)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cQZ-uDDpEvkO", + "outputId": "99f751ae-396c-4a2c-ffd8-f06a75b6bb57" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n", + "{}\n" + ] + } + ], + "source": [ + "# передадим исходный словарь в новую переменную\n", + "new_2 = original\n", + "\n", + "# удалим элементы нового словаря\n", + "new_2.clear()\n", + "\n", + "# из исходного словаря данные также удалились\n", + "print(original)\n", + "print(new_2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3iDksQfFNiux" + }, + "source": [ + "### Функция `dir()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nv3sToYdNmTd", + "outputId": "10c4048b-b1b3-471f-838d-9d211546fee3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__class__',\n", + " '__class_getitem__',\n", + " '__contains__',\n", + " '__delattr__',\n", + " '__delitem__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattribute__']" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# функция dir() возвращает все методы передаваемого ей объекта\n", + "some_dict = {\"k\": 1}\n", + "\n", + "# вначале идут специальные методы,\n", + "# они начинаются и заканчиваются символом '__'\n", + "# выведем первые 11 элементов\n", + "print(dir(some_dict)[:11])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UkqCOYF0su5e", + "outputId": "7074b3c3-e901-44d7-bd5a-9b8d3d865a3a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'k': 1}\n" + ] + } + ], + "source": [ + "# когда мы передаем наш словарь функции print(),\n", + "print(some_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "BGLlQ1PhqEKF", + "outputId": "cf166c59-ba9f-4f1c-d1ac-6b7ec6ce0a57" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "\"{'k': 1}\"" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# на самом деле мы применяем к объекту метод .__str__()\n", + "some_dict.__str__() # pylint: disable=C2801" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "s2PqsanmtAE4", + "outputId": "eb3b4328-96cf-4339-8c4b-365948c4834c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['clear',\n", + " 'copy',\n", + " 'fromkeys',\n", + " 'get',\n", + " 'items',\n", + " 'keys',\n", + " 'pop',\n", + " 'popitem',\n", + " 'setdefault',\n", + " 'update',\n", + " 'values']" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# в большинстве случаев нас будут интересовать методы без '__'\n", + "methods = dir(some_dict)[-11:]\n", + "\n", + "print(methods)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7fi2zJoGvh5v" + }, + "source": [ + "### Dict comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Qr-HfxbHv_if" + }, + "outputs": [], + "source": [ + "# создадим еще один несложный словарь\n", + "source_dict: dict[str, int] = {\"k1\": 2, \"k2\": 4, \"k3\": 6}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wqP1P4EcwAC4", + "outputId": "af999e25-fed2-4303-e69a-182fdab468f5" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k1': 4, 'k2': 8, 'k3': 12}" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# с помощью dict comprehension умножим каждое значение на два\n", + "new_source_dict = {key: value * 2 for (key, value) in source_dict.items()}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "l1w42uT0Kf1b", + "outputId": "906ee62e-c5d0-498c-d6ff-ba99c4352f14" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'K1': 2, 'K2': 4, 'K3': 6}" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# сделаем символы всех ключей заглавными\n", + "new_source_dict = {key.upper(): value for (key, value) in source_dict.items()}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XKoh7SUJ5S8z", + "outputId": "785a0735-56eb-4599-8a1c-ac788c8f7dce" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k2': 4}" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим условие, что значение должно быть больше двух И меньше шести\n", + "new_dict_1 = {\n", + " key: value\n", + " for (key, value) in source_dict.items()\n", + " if value > 2\n", + " if value < 6\n", + "}\n", + "\n", + "print(new_dict_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I1nyzDSi6V3Y", + "outputId": "72f66774-7065-4815-d211-ef06ebd318ee" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k2': 4}" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_dict: dict[str, int] = {}\n", + "\n", + "# при решении этой же задачи в цикле for\n", + "for key, value in source_dict.items():\n", + "\n", + " # мы бы использовали логическое И (and)\n", + " if 2 < value < 6:\n", + "\n", + " # если условия верны, записываем ключ и значение в новый словарь\n", + " new_dict[key] = value\n", + "\n", + "new_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-rJUlsr563N7", + "outputId": "00c0b343-8ccb-4cfb-9ef3-f5568de87730" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k1': 'even', 'k2': 'even', 'k3': 'even'}" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# условие с if-else ставится в самом начале схемы dict comprehension\n", + "# заменим значение на слово even, если оно четное, и odd, если нечетное\n", + "even_odd_dict: dict[str, str] = {\n", + " key: (\"even\" if value % 2 == 0 else \"odd\")\n", + " for (key, value) in source_dict.items()\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3sCsWyonkkGM", + "outputId": "3dacd13e-10d3-44a7-8f23-3898e5840d13" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'k1': 0, 'k2': 0, 'k3': 0}" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# dict comprehension можно использовать вместо метода .fromkeys()\n", + "keys = (\"k1\", \"k2\", \"k3\")\n", + "\n", + "# передадим словарю ключи из кортежа keys и зададим значение 0 каждому из них\n", + "zeros_dict = {key: 0 for key in keys}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HplemEzXwNw0" + }, + "source": [ + "### Дополнительные примеры" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9I3D7qej8JFO" + }, + "source": [ + "#### lambda-функции, функции `map()` и `zip()`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NQxOQz-_jilk" + }, + "source": [ + "Пример со списком" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZXoJ1TMsh13z" + }, + "outputs": [], + "source": [ + "# возьмем список слов\n", + "words: list[str] = [\"apple\", \"banana\", \"fig\", \"blackberry\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BSyuvd278UPf", + "outputId": "4e410273-cca8-475f-e132-b2809c9ee789" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 6, 3, 10]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим lambda-функцию, которая посчитает длину передаваемого ей слова\n", + "# с помощью функции map() применим lambda-функцию\n", + "# к каждому элементу списка words\n", + "# и поместим длины слов в новый список length с помощью функции list()\n", + "length = list(map(len, words))\n", + "length" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b0JRdAJHh2df", + "outputId": "5eda6227-fd0c-4170-c487-81c10956a8db" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'apple': 5, 'banana': 6, 'fig': 3, 'blackberry': 10}" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# с помощью функции zip() поэлементно соединим оба списка и\n", + "# преобразуем в словарь\n", + "from_zip_dict = dict(zip(words, length))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kYNYaOQ-9-V6", + "outputId": "f3887c5f-3134-4c9f-cba7-9e55f340e20d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'apple': 5, 'banana': 6, 'fig': 3, 'blackberry': 10}" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# то же самое можно сделать с помощью функции zip() и list comprehension\n", + "zip_length_dict = dict(zip(words, [len(word) for word in words]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PUs12XYyjmBh" + }, + "source": [ + "Пример со словарем" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0ZVWDzRMjo-6" + }, + "outputs": [], + "source": [ + "# возьмем словарь с ростом людей в футах\n", + "height_feet: dict[str, float] = {\"Alex\": 6.1, \"Jerry\": 5.4, \"Ben\": 5.8}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "irwOj3Rt_M1e", + "outputId": "3f5be590-39ab-4dbf-b475-4ab6afc82d7a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.85928, 1.6459200000000003, 1.76784]" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для преобразования футов в метры создадим lambda-функцию lambda m: m * 0.3048\n", + "# применим эту функцию к значениям словаря с помощью функции map()\n", + "# преобразуем в список\n", + "metres = list(map(lambda m: m * 0.3048, height_feet.values()))\n", + "metres" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RcqkuR4WjsnB", + "outputId": "74026d78-3508-4898-9502-70f84f556a5d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Alex': 1.86, 'Jerry': 1.65, 'Ben': 1.77}" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# с помощью функции zip() соединим ключи исходного словаря с элементами списка metres\n", + "dict(zip(height_feet.keys(), np.round(metres, 2)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pxqmUJG7CEtO", + "outputId": "b3ee3331-c47a-42ac-ac3c-17f82987d4d3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Alex': 1.86, 'Jerry': 1.65, 'Ben': 1.77}" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# то же самое можно выполнить с помощью dict comprehensions\n", + "# всего в одну строчку\n", + "# мы просто преобразуем значения словаря в метры\n", + "students_heights_dict = {\n", + " key: np.round(value * 0.3048, 2) for (key, value) in height_feet.items()\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I-UUEhQNvezN" + }, + "source": [ + "#### Вложенные словари" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OA69rLjwwTAc" + }, + "outputs": [], + "source": [ + "# возьмем словарь, ключами которого будут id сотрудников\n", + "employees: dict[str, dict[str, str | int | float]] = {\n", + " \"id1\": {\n", + " \"first name\": \"Александр\",\n", + " \"last name\": \"Иванов\",\n", + " \"age\": 30,\n", + " \"job\": \"программист\",\n", + " },\n", + " \"id2\": {\n", + " \"first name\": \"Ольга\",\n", + " \"last name\": \"Петрова\",\n", + " \"age\": 35,\n", + " \"job\": \"ML-engineer\",\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8UNwQKCANq0J", + "outputId": "a9831ab9-878a-4201-dc20-1d0a741eca26" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'first name': 'Александр', 'last name': 'Иванов', 'age': 30, 'job': 'программист'}\n", + "{'first name': 'Ольга', 'last name': 'Петрова', 'age': 35, 'job': 'ML-engineer'}\n" + ] + } + ], + "source": [ + "# а значениями - вложенные словари с информацией о них\n", + "for value_e in employees.values():\n", + " print(value_e)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xBRzL0hdkF95" + }, + "source": [ + "##### Базовый операции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0Hx_jDxnOBF3", + "outputId": "c7767b24-5831-436b-cd71-d04d2917a9aa" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "30" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# для того чтобы вывести значение элемента вложенного словаря,\n", + "# воспользуемся двойным ключом\n", + "print(employees[\"id1\"][\"age\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iUNy0ji7ORNC", + "outputId": "8e0c2822-2aac-4c93-9998-3c9547edf06e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id1': {'age': 30,\n", + " 'first name': 'Александр',\n", + " 'job': 'программист',\n", + " 'last name': 'Иванов'},\n", + " 'id2': {'age': 35,\n", + " 'first name': 'Ольга',\n", + " 'job': 'ML-engineer',\n", + " 'last name': 'Петрова'},\n", + " 'id3': {'age': 27,\n", + " 'first name': 'Дарья',\n", + " 'job': 'веб-дизайнер',\n", + " 'last name': 'Некрасова'}}\n" + ] + } + ], + "source": [ + "# добавим информацию о новом сотруднике\n", + "employees[\"id3\"] = {\n", + " \"first name\": \"Дарья\",\n", + " \"last name\": \"Некрасова\",\n", + " \"age\": 27,\n", + " \"job\": \"веб-дизайнер\",\n", + "}\n", + "\n", + "# и выведем обновленный словарь с помощью функции pprint()\n", + "pprint(employees)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mHmC4hPGwTC_", + "outputId": "897b5449-4fed-4df6-a538-be532c3c6fcf" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id1': {'age': 30,\n", + " 'first name': 'Александр',\n", + " 'job': 'программист',\n", + " 'last name': 'Иванов'},\n", + " 'id2': {'age': 35,\n", + " 'first name': 'Ольга',\n", + " 'job': 'ML-engineer',\n", + " 'last name': 'Петрова'},\n", + " 'id3': {'age': 26,\n", + " 'first name': 'Дарья',\n", + " 'job': 'веб-дизайнер',\n", + " 'last name': 'Некрасова'}}\n" + ] + } + ], + "source": [ + "# изменить значение вложенного словаря можно также с помощью двойного ключа\n", + "employees[\"id3\"][\"age\"] = 26\n", + "pprint(employees)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kP8KzxtckMf0" + }, + "source": [ + "##### Циклы `for`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rfKMVzEGlUf1", + "outputId": "7567b97d-4963-4dc8-eb09-73d2c84d9775" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id1': {'age': 30.0,\n", + " 'first name': 'Александр',\n", + " 'job': 'программист',\n", + " 'last name': 'Иванов'},\n", + " 'id2': {'age': 35.0,\n", + " 'first name': 'Ольга',\n", + " 'job': 'ML-engineer',\n", + " 'last name': 'Петрова'},\n", + " 'id3': {'age': 26.0,\n", + " 'first name': 'Дарья',\n", + " 'job': 'веб-дизайнер',\n", + " 'last name': 'Некрасова'}}\n" + ] + } + ], + "source": [ + "# заменим тип данных в информации о возрасте с int на float\n", + "\n", + "# для этого вначале пройдемся по вложенным словарям,\n", + "# т.е. по значениям info внешнего словаря employees\n", + "for info in employees.values():\n", + "\n", + " # затем по ключам и значениям вложенного словаря info\n", + " for key_info, value_info in info.items():\n", + "\n", + " # если ключ совпадет со словом 'age'\n", + " if key_info == \"age\":\n", + "\n", + " # преобразуем значение в тип float\n", + " info[key_info] = float(value_info)\n", + "\n", + "pprint(employees)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HO97JxkUkZyR" + }, + "source": [ + "##### Вложенные словари и dict comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-lBSDGLtm62e", + "outputId": "11b6677f-5514-4a7f-c868-7959c26b0e3a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id1': {'age': 30.0,\n", + " 'first name': 'Александр',\n", + " 'job': 'программист',\n", + " 'last name': 'Иванов'},\n", + " 'id2': {'age': 35.0,\n", + " 'first name': 'Ольга',\n", + " 'job': 'ML-engineer',\n", + " 'last name': 'Петрова'},\n", + " 'id3': {'age': 26.0,\n", + " 'first name': 'Дарья',\n", + " 'job': 'веб-дизайнер',\n", + " 'last name': 'Некрасова'}}\n" + ] + } + ], + "source": [ + "# преоразуем обратно из float в int, но уже через dict comprehension\n", + "# для начала просто выведем словарь employees без изменений\n", + "pprint({id: info for id, info in employees.items()})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qaPZCn-PnHpn", + "outputId": "38d50011-d70a-4cc0-b366-21a5fb6dc926" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id1': {'age': 30,\n", + " 'first name': 'Александр',\n", + " 'job': 'программист',\n", + " 'last name': 'Иванов'},\n", + " 'id2': {'age': 35,\n", + " 'first name': 'Ольга',\n", + " 'job': 'ML-engineer',\n", + " 'last name': 'Петрова'},\n", + " 'id3': {'age': 26,\n", + " 'first name': 'Дарья',\n", + " 'job': 'веб-дизайнер',\n", + " 'last name': 'Некрасова'}}\n" + ] + } + ], + "source": [ + "# а затем заменим значение внешнего словаря info (т.е. вложенный словарь)\n", + "# на еще один dict comprehension с условием if-else\n", + "pprint(\n", + " {\n", + " id: {\n", + " key: (int(value_i) if key == \"age\" else value_i)\n", + " for key, value_i in info.items()\n", + " }\n", + " for id, info in employees.items()\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KQw8_33YMLU2" + }, + "source": [ + "#### Частота слов в тексте" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ysL5RwJEMO7M" + }, + "outputs": [], + "source": [ + "# возьмем знакомый нам текст\n", + "corpus = (\n", + " \"When we were in Paris we visited a lot of museums. \"\n", + " \"We first went to the Louvre, the largest art museum in the world.\"\n", + " \"I have always been interested in art so I spent many hours there. \"\n", + " \"The museum is enormous, so a week there would not be enough.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vu3yQDqNRb9o" + }, + "source": [ + "##### Предварительная обработка текста" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Y0Q0RhvlRRqJ", + "outputId": "d3ec9c90-e0b7-430b-b636-4bc33801ff6b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['When', 'we', 'were', 'in', 'Paris', 'we', 'visited', 'a', 'lot', 'of', 'museums.', 'We', 'first', 'went', 'to', 'the', 'Louvre,', 'the', 'largest', 'art', 'museum', 'in', 'the', 'world.', 'I', 'have', 'always', 'been', 'interested', 'in', 'art', 'so', 'I', 'spent', 'many', 'hours', 'there.', 'The', 'museum', 'is', 'enourmous,', 'so', 'a', 'week', 'there', 'would', 'not', 'be', 'enough.']\n" + ] + } + ], + "source": [ + "# разделим его на слова\n", + "words = corpus.split()\n", + "print(words)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VPFYOtFWXmm2", + "outputId": "c3c416a3-d537-40d8-e823-6c81c3f4b72e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['when', 'we', 'were', 'in', 'paris', 'we', 'visited', 'a', 'lot', 'of', 'museums', 'we', 'first', 'went', 'to', 'the', 'louvre', 'the', 'largest', 'art', 'museum', 'in', 'the', 'world', 'i', 'have', 'always', 'been', 'interested', 'in', 'art', 'so', 'i', 'spent', 'many', 'hours', 'there', 'the', 'museum', 'is', 'enourmous', 'so', 'a', 'week', 'there', 'would', 'not', 'be', 'enough']\n" + ] + } + ], + "source": [ + "# с помощью list comprehension удалим точки, запятые\n", + "# и переведем все слова в нижний регистр\n", + "words = [word.strip(\".\").strip(\",\").lower() for word in words]\n", + "print(words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JvApRTwkRfaS" + }, + "source": [ + "##### Способ 1. Условие if-else" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0FbwZ4ODSlxH", + "outputId": "8c0b1ea0-041d-4ee3-cae7-46a191f0a546" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)]" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим пустой словарь для мешка слов bow\n", + "bow_1: dict[str, int] = {}\n", + "\n", + "# пройдемся по словам текста\n", + "for word in words:\n", + "\n", + " # если нам встретилось слово, которое уже есть в словаре\n", + " if word in bow_1:\n", + "\n", + " # увеличим его значение (частоту) на 1\n", + " bow_1[word] = bow_1[word] + 1\n", + "\n", + " # в противном случае, если слово встречается впервые\n", + " else:\n", + "\n", + " # зададим ему значение 1\n", + " bow_1[word] = 1\n", + "\n", + "# отсортируем словарь по значению в убываюем порядке (reverse = True)\n", + "# и выведем шесть наиболее частотных слов\n", + "print(sorted(bow_1.items(), key=lambda item: item[1], reverse=True)[:6])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DckCYLgvaF3D" + }, + "source": [ + "##### Способ 2. Метод .get()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-cfKJK7dUx6E", + "outputId": "01b035d5-f1cb-4e1d-d455-d6175a15b9c3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)]" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bow_2: dict[str, int] = {}\n", + "\n", + "# снова пройдемся в цикле по словам\n", + "for word in words:\n", + "\n", + " # если слова еще нет в словаре, .get() выведет значение 0,\n", + " # к которому мы прибавим единицу\n", + " # если слово есть, метод .get() выведет существующее значение,\n", + " # например, 2 или 3,\n", + " # и мы также увеличим счетчик на 1\n", + " bow_2[word] = bow_2.get(word, 0) + 1\n", + "\n", + "# выведем наиболее популярные слова\n", + "print(sorted(bow_2.items(), key=lambda item: item[1], reverse=True)[:6])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p9pnHw9JdTDb" + }, + "source": [ + "##### Способ 3. Модуль collections" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DBxI0C6GMO-P", + "outputId": "b688998c-0316-40a1-a3eb-38839afd60fd" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)]" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим объект этого класса, передав ему список слов\n", + "bow_3 = Counter(words)\n", + "\n", + "# выведем шесть наиболее часто встречающихся слов с помощью метода .most_common()\n", + "bow_3.most_common(6)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Encet8AUTxaK" + }, + "source": [ + "### Дополнительные материалы" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PMf-4k76T17z" + }, + "source": [ + "#### Изменяемые и неизменяемые типы данных" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YSZEqdoqZPFJ" + }, + "source": [ + "Неизменяемый тип данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "evIywP2QteRj", + "outputId": "41dc2190-2e36-4393-d42d-594a8c8c0a86" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(9739968, str, 'Python')" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим строковый объект\n", + "string = \"Python\"\n", + "\n", + "# посмотрим на identity, type и value\n", + "# функция id() выводит адрес объекта в памяти компьютера\n", + "print(id(string), type(string), string)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ddda36SbVNP3", + "outputId": "1efe542a-2076-48b7-d8ff-f38f526ca50f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(132833144673840, str, 'Python is cool')" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# попробуем изменить этот объект\n", + "string = string + \" is cool\"\n", + "\n", + "# посмотрим на identity, type и value\n", + "print(id(string), type(string), string)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zD5pA-shZS9y" + }, + "source": [ + "Изменяемый тип данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "m3v7sz66WaG2", + "outputId": "79bed439-00cc-4d22-ff23-ea2ac933869b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(132833144305664, list, [1, 2, 3])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим список\n", + "lst: list[int] = [1, 2, 3]\n", + "\n", + "# посмотрим на identity, type и value\n", + "print(id(lst), type(lst), lst)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MwsfBPKuY44t", + "outputId": "e68fe4a2-9de8-4649-aab7-284c6868c3df" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(132833144305664, list, [1, 2, 3, 4])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим элемент в список\n", + "lst.append(4)\n", + "\n", + "# снова выведем identity, type и value\n", + "print(id(lst), type(lst), lst)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RN5eo53UcJ8v" + }, + "source": [ + "Копирование объектов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WHkQSdiHe7z6", + "outputId": "3214f311-d3a7-4f47-b029-413b6f76729c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('Python', 'Python is cool')" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вновь создадим строку\n", + "string = \"Python\"\n", + "\n", + "# скопируем через присваивание\n", + "string2 = string\n", + "\n", + "# изменим копию\n", + "string2 = string2 + \" is cool\"\n", + "\n", + "# посмотрим на результат\n", + "print(string, string2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4acHz97ajcSH", + "outputId": "4cff9e9d-be08-41b3-9a3d-6883e02d0a71" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(False, False)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# оператор == сравнивает значения (values)\n", + "# оператор is сравнивает identities\n", + "print(string == string2, string is string2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LHQ7dLSjbLiF", + "outputId": "d24c9d3f-af32-4d65-9219-7336ad8b5cae" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], [1, 2, 3, 4])" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# создадим список\n", + "lst = [1, 2, 3]\n", + "\n", + "# скопируем его в новую переменную через присваивание\n", + "lst2 = lst\n", + "\n", + "# добавим новый элемент в скопированный список\n", + "lst2.append(4)\n", + "\n", + "# выведем исходный список и копию\n", + "print(lst, lst2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Oje2eXOYjQwq", + "outputId": "750ce99e-09f8-42a1-f584-da308d4a4b51" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(True, True)" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# убедимся, что речь идет об одном и том же объекте\n", + "print(lst == lst2, lst is lst2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bMxYXjZ1fwwV", + "outputId": "a4da24b7-1247-4586-a96f-b461ad1f9d47" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3], [1, 2, 3, 4])" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# вновь создадим список\n", + "lst = [1, 2, 3]\n", + "\n", + "# скопируем с помощью метода .copy()\n", + "lst2 = lst.copy()\n", + "\n", + "# добавим новый элемент в скопированный список\n", + "lst2.append(4)\n", + "\n", + "# выведем исходный список и копию\n", + "print(lst, lst2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uajQvKK5gN2x", + "outputId": "17e77160-2826-4cdf-9328-06c7844b6dfd" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], [1, 2, 3, 4], True, False)" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# теперь сделаем значения списков одинаковыми\n", + "lst.append(4)\n", + "\n", + "# и убедимся, что это по-прежнему разные объекты\n", + "print(lst, lst2, lst == lst2, lst is lst2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Etf2OVxIsK7R" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_09_dictionaries.py b/Python/makarov/chapter_09_dictionaries.py new file mode 100644 index 00000000..dd16aad6 --- /dev/null +++ b/Python/makarov/chapter_09_dictionaries.py @@ -0,0 +1,616 @@ +"""Макаров. + +Словарь в Питоне +""" + +# ## Словарь в Питоне + +# ### Понятие словаря + +# #### Создание словаря + +# + +# пустой словарь можно создать с помощью {} или функции dict() +# импортируем класс Counter +from collections import Counter + +# импортируем функцию pprint() из модуля pprint +# некоторые структуры данных она выводит лучше, чем обычная print() +from pprint import pprint + +import numpy as np + +dict_1: dict[str, str] = {} +dict_2: dict[str, str] = dict() # pylint: disable=R1735 +print(dict_1, dict_2) +# - + +# словарь можно сразу заполнить ключами и значениями +company: dict[str, str | int] = { + "name": "Toyota", + "founded": 1937, + "founder": "Kiichiro Toyoda", +} +company + +# словарь можно создать из вложенных списков +tickers: dict[str, str] = dict( + [["TYO", "Toyota"], ["TSLA", "Tesla"], ["F", "Ford"]] +) +tickers + +# + +# если поместить ключи в кортеж +keys: tuple[str, ...] = ("k1", "k2", "k3") +# и задать значение +value_global = 0 + +# то с помощью метода .fromkeys() можно создать словарь +# с этими ключами и заданным значением для каждого из них +empty_values = dict.fromkeys(keys, value_global) +empty_values +# - + +# #### Ключи и значения словаря + +# Виды значений словаря + +# + +# приведем пример того, какими могут быть значения словаря +value_types: dict[ + str, int | str | bool | None | list[int] | object | float +] = { + "k1": 123, + "k2": "string", + "k3": np.nan, # тип "Пропущенное значение" + "k4": True, # логическое значение + "k5": None, + "k6": [1, 2, 3], + "k7": np.array([1, 2, 3]), + "k8": {1: "v1", 2: "v2", 3: "v3"}, +} + +value_types +# - + +# Методы .keys(), .values() и .items() + +# создадим несложный словарь с информацией о сотруднике +person: dict[str, str | int | list[str]] = { + "first name": "Иван", + "last name": "Иванов", + "born": 1980, + "dept": "IT", +} + +# посмотрим на ключи и +print(person.keys()) + +# значения +print(person.values()) + +# а также на пары ключ-значение в виде списка из кортежей +print(person.items()) + +# Использование цикла for + +# ключи и значения можно вывести в цикле for +for key, value_p in person.items(): + print(key, value_p) + +# Доступ по ключу и метод .get() + +# значение можно посмотреть по ключу +person["last name"] + +# + +# если такого ключа нет, Питон выдаст ошибку +# person['education'] --> Error +# - + +# чтобы этого не произошло, можно использовать метод .get() +# по умолчанию при отсутствии ключа он выводит значение None +print(person.get("education")) + +# если ключ все-таки есть, .get() выведет соответствующее значение +print(person.get("born")) + +# Проверка вхождения ключа и значения в словарь + +# проверим есть ли такой ключ +print("born" in person) + +# и такое значение +print(1980 in person.values()) + +# можно также проверить наличие и ключа, и значения одновременно +print(("born", 1980) in person.items()) + +# ### Операции со словарями + +# #### Добавление и изменение элементов + +# добавить элемент можно, передав новому ключу новое значение +# обратите внимание, в данном случае новое значение - это список +person["languages"] = ["Python", "C++"] +person + +# изменить элемент можно, передав существующему ключу новое значение, +# значение - это по-прежнему список, но из одного элемента +person["languages"] = ["Python"] +person + +# + +# возьмем еще один словарь +new_elements: dict[str, str | int] = {"job": "программист", "experience": 7} + +# и присоединим его к существующему словарю с помощью метода .update() +person.update(new_elements) +person +# - + +# метод .setdefault() проверит есть ли ключ в словаре, +# если "да", значение не изменится +person.setdefault("last name", "Петров") +person + +# если нет, будет добавлен новый ключ и соответствующее значение +person.setdefault("f_languages", ["русский", "английский"]) +person + +# #### Удаление элементов + +# метод .pop() удаляет элемент по ключу и выводит удаляемое значение +print(person.pop("dept")) + +# мы видим, что пары 'dept' : 'IT' больше нет +person + +# ключевое слово del также удаляет элемент по ключу +# удаляемое значение не выводится +del person["born"] + +# метод .popitem() удаляет последний добавленный элемент и выводит его +person.popitem() + +# метод .clear() удаляет все элементы словаря +person.clear() +person + +# ключевое слово del также позволяет удалить словарь целиком +del person + +# убедимся, что такого словаря больше нет +person + +# #### Сортировка словарей + +# возьмем несложный словарь +dict_to_sort: dict[str, int] = {"k2": 30, "k1": 20, "k3": 10} + +# отсортируем ключи +sorted(dict_to_sort) + +# и значения +sorted(dict_to_sort.values()) + +# посмотрим на пары ключ : значение +dict_to_sort.items() + +# для их сортировки по ключу (индекс [0]) +# воспользуемся методом .items() и lambda-функцией +sorted(dict_to_sort.items(), key=lambda item: item[0]) + +# сортировка по значению выполняется так же, однако +# lambda-функции мы передаем индекс [1] +sorted(dict_to_sort.items(), key=lambda item: item[1]) + +# #### Копирование словарей + +# создадим исходный словарь с количеством студентов на первом и втором курсах университета +original: dict[str, int] = {"Первый курс": 174, "Второй курс": 131} + +# Копирование с помощью метода .copy() + +# + +# создадим копию этого словаря с помощью метода .copy() +new_1 = original.copy() + +# добавим информацию о третьем курсе в новый словарь +new_1["Третий курс"] = 117 + +# исходный словарь не изменился +print(original) +print(new_1) +# - + +# Копирование через оператор присваивания `=` (так делать не стоит!) + +# + +# передадим исходный словарь в новую переменную +new_2 = original + +# удалим элементы нового словаря +new_2.clear() + +# из исходного словаря данные также удалились +print(original) +print(new_2) +# - + +# ### Функция `dir()` + +# + +# функция dir() возвращает все методы передаваемого ей объекта +some_dict = {"k": 1} + +# вначале идут специальные методы, +# они начинаются и заканчиваются символом '__' +# выведем первые 11 элементов +print(dir(some_dict)[:11]) +# - + +# когда мы передаем наш словарь функции print(), +print(some_dict) + +# на самом деле мы применяем к объекту метод .__str__() +some_dict.__str__() # pylint: disable=C2801 + +# + +# в большинстве случаев нас будут интересовать методы без '__' +methods = dir(some_dict)[-11:] + +print(methods) +# - + +# ### Dict comprehension + +# создадим еще один несложный словарь +source_dict: dict[str, int] = {"k1": 2, "k2": 4, "k3": 6} + +# с помощью dict comprehension умножим каждое значение на два +new_source_dict = {key: value * 2 for (key, value) in source_dict.items()} + +# сделаем символы всех ключей заглавными +new_source_dict = {key.upper(): value for (key, value) in source_dict.items()} + +# + +# добавим условие, что значение должно быть больше двух И меньше шести +new_dict_1 = { + key: value + for (key, value) in source_dict.items() + if value > 2 + if value < 6 +} + +print(new_dict_1) + +# + +new_dict: dict[str, int] = {} + +# при решении этой же задачи в цикле for +for key, value in source_dict.items(): + + # мы бы использовали логическое И (and) + if 2 < value < 6: + + # если условия верны, записываем ключ и значение в новый словарь + new_dict[key] = value + +new_dict +# - + +# условие с if-else ставится в самом начале схемы dict comprehension +# заменим значение на слово even, если оно четное, и odd, если нечетное +even_odd_dict: dict[str, str] = { + key: ("even" if value % 2 == 0 else "odd") + for (key, value) in source_dict.items() +} + +# + +# dict comprehension можно использовать вместо метода .fromkeys() +keys = ("k1", "k2", "k3") + +# передадим словарю ключи из кортежа keys и зададим значение 0 каждому из них +zeros_dict = {key: 0 for key in keys} +# - + +# ### Дополнительные примеры + +# #### lambda-функции, функции `map()` и `zip()` + +# Пример со списком + +# возьмем список слов +words: list[str] = ["apple", "banana", "fig", "blackberry"] + +# создадим lambda-функцию, которая посчитает длину передаваемого ей слова +# с помощью функции map() применим lambda-функцию +# к каждому элементу списка words +# и поместим длины слов в новый список length с помощью функции list() +length = list(map(len, words)) +length + +# с помощью функции zip() поэлементно соединим оба списка и +# преобразуем в словарь +from_zip_dict = dict(zip(words, length)) + +# то же самое можно сделать с помощью функции zip() и list comprehension +zip_length_dict = dict(zip(words, [len(word) for word in words])) + +# Пример со словарем + +# возьмем словарь с ростом людей в футах +height_feet: dict[str, float] = {"Alex": 6.1, "Jerry": 5.4, "Ben": 5.8} + +# для преобразования футов в метры создадим lambda-функцию lambda m: m * 0.3048 +# применим эту функцию к значениям словаря с помощью функции map() +# преобразуем в список +metres = list(map(lambda m: m * 0.3048, height_feet.values())) +metres + +# с помощью функции zip() соединим ключи исходного словаря с элементами списка metres +dict(zip(height_feet.keys(), np.round(metres, 2))) + +# то же самое можно выполнить с помощью dict comprehensions +# всего в одну строчку +# мы просто преобразуем значения словаря в метры +students_heights_dict = { + key: np.round(value * 0.3048, 2) for (key, value) in height_feet.items() +} + +# #### Вложенные словари + +# возьмем словарь, ключами которого будут id сотрудников +employees: dict[str, dict[str, str | int | float]] = { + "id1": { + "first name": "Александр", + "last name": "Иванов", + "age": 30, + "job": "программист", + }, + "id2": { + "first name": "Ольга", + "last name": "Петрова", + "age": 35, + "job": "ML-engineer", + }, +} + +# а значениями - вложенные словари с информацией о них +for value_e in employees.values(): + print(value_e) + +# ##### Базовый операции + +# для того чтобы вывести значение элемента вложенного словаря, +# воспользуемся двойным ключом +print(employees["id1"]["age"]) + +# + +# добавим информацию о новом сотруднике +employees["id3"] = { + "first name": "Дарья", + "last name": "Некрасова", + "age": 27, + "job": "веб-дизайнер", +} + +# и выведем обновленный словарь с помощью функции pprint() +pprint(employees) +# - + +# изменить значение вложенного словаря можно также с помощью двойного ключа +employees["id3"]["age"] = 26 +pprint(employees) + +# ##### Циклы `for` + +# + +# заменим тип данных в информации о возрасте с int на float + +# для этого вначале пройдемся по вложенным словарям, +# т.е. по значениям info внешнего словаря employees +for info in employees.values(): + + # затем по ключам и значениям вложенного словаря info + for key_info, value_info in info.items(): + + # если ключ совпадет со словом 'age' + if key_info == "age": + + # преобразуем значение в тип float + info[key_info] = float(value_info) + +pprint(employees) +# - + +# ##### Вложенные словари и dict comprehension + +# преоразуем обратно из float в int, но уже через dict comprehension +# для начала просто выведем словарь employees без изменений +pprint({id: info for id, info in employees.items()}) + +# а затем заменим значение внешнего словаря info (т.е. вложенный словарь) +# на еще один dict comprehension с условием if-else +pprint( + { + id: { + key: (int(value_i) if key == "age" else value_i) + for key, value_i in info.items() + } + for id, info in employees.items() + } +) + +# #### Частота слов в тексте + +# возьмем знакомый нам текст +corpus = ( + "When we were in Paris we visited a lot of museums. " + "We first went to the Louvre, the largest art museum in the world." + "I have always been interested in art so I spent many hours there. " + "The museum is enormous, so a week there would not be enough." +) + +# ##### Предварительная обработка текста + +# разделим его на слова +words = corpus.split() +print(words) + +# с помощью list comprehension удалим точки, запятые +# и переведем все слова в нижний регистр +words = [word.strip(".").strip(",").lower() for word in words] +print(words) + +# ##### Способ 1. Условие if-else + +# + +# создадим пустой словарь для мешка слов bow +bow_1: dict[str, int] = {} + +# пройдемся по словам текста +for word in words: + + # если нам встретилось слово, которое уже есть в словаре + if word in bow_1: + + # увеличим его значение (частоту) на 1 + bow_1[word] = bow_1[word] + 1 + + # в противном случае, если слово встречается впервые + else: + + # зададим ему значение 1 + bow_1[word] = 1 + +# отсортируем словарь по значению в убываюем порядке (reverse = True) +# и выведем шесть наиболее частотных слов +print(sorted(bow_1.items(), key=lambda item: item[1], reverse=True)[:6]) +# - + +# ##### Способ 2. Метод .get() + +# + +bow_2: dict[str, int] = {} + +# снова пройдемся в цикле по словам +for word in words: + + # если слова еще нет в словаре, .get() выведет значение 0, + # к которому мы прибавим единицу + # если слово есть, метод .get() выведет существующее значение, + # например, 2 или 3, + # и мы также увеличим счетчик на 1 + bow_2[word] = bow_2.get(word, 0) + 1 + +# выведем наиболее популярные слова +print(sorted(bow_2.items(), key=lambda item: item[1], reverse=True)[:6]) +# - + +# ##### Способ 3. Модуль collections + +# + +# создадим объект этого класса, передав ему список слов +bow_3 = Counter(words) + +# выведем шесть наиболее часто встречающихся слов с помощью метода .most_common() +bow_3.most_common(6) +# - + +# ### Дополнительные материалы + +# #### Изменяемые и неизменяемые типы данных + +# Неизменяемый тип данных + +# + +# создадим строковый объект +string = "Python" + +# посмотрим на identity, type и value +# функция id() выводит адрес объекта в памяти компьютера +print(id(string), type(string), string) + +# + +# попробуем изменить этот объект +string = string + " is cool" + +# посмотрим на identity, type и value +print(id(string), type(string), string) +# - + +# Изменяемый тип данных + +# + +# создадим список +lst: list[int] = [1, 2, 3] + +# посмотрим на identity, type и value +print(id(lst), type(lst), lst) + +# + +# добавим элемент в список +lst.append(4) + +# снова выведем identity, type и value +print(id(lst), type(lst), lst) +# - + +# Копирование объектов + +# + +# вновь создадим строку +string = "Python" + +# скопируем через присваивание +string2 = string + +# изменим копию +string2 = string2 + " is cool" + +# посмотрим на результат +print(string, string2) +# - + +# оператор == сравнивает значения (values) +# оператор is сравнивает identities +print(string == string2, string is string2) + +# + +# создадим список +lst = [1, 2, 3] + +# скопируем его в новую переменную через присваивание +lst2 = lst + +# добавим новый элемент в скопированный список +lst2.append(4) + +# выведем исходный список и копию +print(lst, lst2) +# - + +# убедимся, что речь идет об одном и том же объекте +print(lst == lst2, lst is lst2) + +# + +# вновь создадим список +lst = [1, 2, 3] + +# скопируем с помощью метода .copy() +lst2 = lst.copy() + +# добавим новый элемент в скопированный список +lst2.append(4) + +# выведем исходный список и копию +print(lst, lst2) + +# + +# теперь сделаем значения списков одинаковыми +lst.append(4) + +# и убедимся, что это по-прежнему разные объекты +print(lst, lst2, lst == lst2, lst is lst2) diff --git a/Python/makarov/chapter_15_iterators.ipynb b/Python/makarov/chapter_15_iterators.ipynb new file mode 100644 index 00000000..1ca189f9 --- /dev/null +++ b/Python/makarov/chapter_15_iterators.ipynb @@ -0,0 +1,1410 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Итераторы и генераторы.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0AoPSXyUTTLc" + }, + "source": [ + "## Итераторы и генераторы" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5Vlh7nreAD2D" + }, + "source": [ + "### Итерируемый объект и итератор" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kthx3B9dGQY5" + }, + "source": [ + "#### Основные определения" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lgrM3Gnp25o-", + "outputId": "916aa0be-5648-4449-8910-127bffe16165" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "from collections.abc import Iterator\n", + "from itertools import chain, count, cycle\n", + "from typing import Generator\n", + "\n", + "for index in [1, 2, 3]:\n", + " print(index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YOd5fX1T4VDs", + "outputId": "fda5b2fd-f3ca-40b6-d5e5-782e899731e3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# встроенная функция iter() вызывает метод .__iter__(),\n", + "# создающий итератор\n", + "iterator1 = iter([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1s_bw0wr6dn_", + "outputId": "85e8b0e2-a4cd-443d-a699-e52cf88dbd66" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "iterable_object: list[int] = [1, 2, 3]\n", + "\n", + "iterator = iter(iterable_object)\n", + "print(iterator)\n", + "print()\n", + "\n", + "print(next(iterator))\n", + "print(next(iterator))\n", + "print(next(iterator))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tbGGt-NK8Scw", + "outputId": "b51b2e38-fe17-4362-cc76-2a1e5adfb6b9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "for iterator2 in iterable_object:\n", + " print(iterator2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fPbwZ8UO93lC", + "outputId": "bee708e4-d96a-4409-b9d9-4884ecd6851d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A: 1\n", + "A: 2\n", + "A: 3\n", + "B: 1\n" + ] + } + ], + "source": [ + "iterator_a = iter(iterable_object)\n", + "iterator_b = iter(iterable_object)\n", + "\n", + "print(f\"A: {next(iterator_a)}\")\n", + "print(f\"A: {next(iterator_a)}\")\n", + "print(f\"A: {next(iterator_a)}\")\n", + "print(f\"B: {next(iterator_b)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HkVfe8K3CSD3", + "outputId": "fe0d9740-3d21-43fc-8992-85d88c4f31f3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iterable_object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gPsw6ywr_642" + }, + "outputs": [], + "source": [ + "# print(f'A: {next(iterator_a)}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PFhBl_4Cx06v", + "outputId": "d30c68e5-c78d-4aed-8c9d-5517438785ce" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([], [2, 3])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(list(iterator_a), list(iterator_b))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ahG3y2qmgY7i", + "outputId": "d6cd205d-d974-4af5-94d4-895e1ece8d57" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "for item_s in {1, 1, 2, 3}: # pylint: disable=C0208,W0130\n", + " print(item_s)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Sj4h2zyAJTGd" + }, + "source": [ + "#### Отсутствие \"обратного хода\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0SBq2VSKJQkl", + "outputId": "a3e37ec9-7506-4dbe-e1e5-3ac827ddc2cb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "iterator_c = iter(iterable_object)\n", + "\n", + "for item_c_1 in iterator_c:\n", + " print(item_c_1)\n", + " break\n", + "\n", + "for item_c_2 in iterator_c:\n", + " print(item_c_2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KvUcGyCOF2dl" + }, + "source": [ + "#### Функция `zip()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "G-Z4ToVzFe0n", + "outputId": "403a9165-5fa7-487e-9b82-6d44fd8b73af" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iterator_tuple = zip(iterable_object, iterable_object)\n", + "print(iterator_tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1Zici5FDGpCL", + "outputId": "6be383b2-6215-4b94-93d5-18b2b6faa4f8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 1)\n", + "(2, 2)\n", + "(3, 3)\n" + ] + } + ], + "source": [ + "print(next(iterator_tuple))\n", + "print(next(iterator_tuple))\n", + "print(next(iterator_tuple))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Gf2CXrVGFU2I", + "outputId": "c4995972-8e75-4246-b694-9437af79c838" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 1)\n", + "(2, 2)\n", + "(3, 3)\n" + ] + } + ], + "source": [ + "for item_z in zip(iterable_object, iterable_object):\n", + " print(item_z)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e9y3-HUEKlXe" + }, + "source": [ + "#### Примеры итераторов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GiQAx3rds0-3" + }, + "source": [ + "Возведение в квадрат" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Gp1fVFW3qJwE" + }, + "outputs": [], + "source": [ + "class Square:\n", + " \"\"\"Итератор, возводящий в квадрат числа из переданной последовательности.\"\"\"\n", + "\n", + " def __init__(self, seq: list[int]) -> None:\n", + " \"\"\"Инициализация итератора.\"\"\"\n", + " self._seq = seq\n", + " self._idx = 0\n", + "\n", + " def __iter__(self) -> \"Square\":\n", + " \"\"\"Возвращает сам объект как итератор.\"\"\"\n", + " return self\n", + "\n", + " def __next__(self) -> int:\n", + " \"\"\"Возвращает следующее значение итератора.\"\"\"\n", + " if self._idx < len(self._seq):\n", + " square = self._seq[self._idx] ** 2\n", + " self._idx += 1\n", + " return square\n", + "\n", + " raise StopIteration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tO8DeWnSqmSZ", + "outputId": "987af348-d6ab-417d-c4b9-99599e2f87b7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.Square at 0x7ec755cf6450>" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "square_1 = Square([1, 2, 3, 4, 5])\n", + "print(square_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "o2PxHYNJqt6I", + "outputId": "0e717160-6d5c-4d95-d6dc-d0e3137e4d47" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "4\n", + "9\n", + "16\n", + "25\n" + ] + } + ], + "source": [ + "for num_square in square_1:\n", + " print(num_square)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oUor5ZgJs3r3" + }, + "source": [ + "Счетчик" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k3PWHN0xKoO6" + }, + "outputs": [], + "source": [ + "class Counter:\n", + " \"\"\"Итератор, генерирующий последовательность целых чисел в заданном диапазоне.\"\"\"\n", + "\n", + " def __init__(self, start: int = 3, stop: int = 9) -> None:\n", + " \"\"\"Инициализирует итератор с заданными границами диапазона.\"\"\"\n", + " self._current = start - 1\n", + " self._stop = stop\n", + "\n", + " def __iter__(self) -> \"Counter\":\n", + " \"\"\"Возвращает сам объект как итератор.\"\"\"\n", + " return self\n", + "\n", + " def __next__(self) -> int:\n", + " \"\"\"Возвращает следующее значение в последовательности.\"\"\"\n", + " self._current += 1\n", + " if self._current < self._stop:\n", + " return self._current\n", + "\n", + " raise StopIteration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "g-8TFk7MKzjJ", + "outputId": "2cde6f1e-b01c-432c-c90d-354b589a8990" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.Counter at 0x7ec755d18490>" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "counter = Counter()\n", + "print(counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "B8LRePjDLZA_", + "outputId": "7eb4976f-30f1-4fd4-84dc-3f2d98d4b6d6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "4\n" + ] + } + ], + "source": [ + "print(next(counter))\n", + "print(next(counter))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nWMjrjAzeuOk", + "outputId": "6ed0a768-e606-453e-ff5d-f06c5040b3a6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "6\n", + "7\n", + "8\n" + ] + } + ], + "source": [ + "for current_count in counter:\n", + " print(current_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XQ79dGrps8_v" + }, + "source": [ + "Класс Iterator модуля collections.abc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "10ZltZGVtEFo" + }, + "outputs": [], + "source": [ + "class Counter2(Iterator[int]):\n", + " \"\"\"Итератор.\n", + "\n", + " Генерирует последовательность целых чисел в заданном диапазоне.\n", + " \"\"\"\n", + "\n", + " def __init__(self, start: int = 3, stop: int = 9) -> None:\n", + " \"\"\"Инициализирует итератор с заданными границами диапазона.\"\"\"\n", + " self._current = start - 1\n", + " self._stop = stop\n", + "\n", + " def __next__(self) -> int:\n", + " \"\"\"Возвращает следующее значение в последовательности.\"\"\"\n", + " self._current += 1\n", + " if self._current < self._stop:\n", + " return self._current\n", + "\n", + " raise StopIteration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zYVob0XttPTm", + "outputId": "9513ea1b-3b75-4f89-eff3-59b0a11e9e10" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "4\n", + "5\n", + "6\n", + "7\n", + "8\n" + ] + } + ], + "source": [ + "for current_count in Counter2():\n", + " print(current_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F5EjaYegFtQf" + }, + "source": [ + "Бесконечный итератор" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1K9r-t9jFvZB" + }, + "outputs": [], + "source": [ + "class FibIterator:\n", + " \"\"\"Бесконечный Итератор.\n", + "\n", + " Генерирует последовательность целых чисел, начиная с 0.\n", + " \"\"\"\n", + "\n", + " def __init__(self) -> None:\n", + " \"\"\"Инициализирует итератор.\"\"\"\n", + " self._idx = 0\n", + " self._current = 0\n", + " self._next = 1\n", + "\n", + " def __iter__(self) -> \"FibIterator\":\n", + " \"\"\"Возвращает сам объект как итератор.\"\"\"\n", + " return self\n", + "\n", + " def __next__(self) -> int:\n", + " \"\"\"Возвращает следующее значение в последовательности.\"\"\"\n", + " self._idx += 1\n", + " self._current, self._next = (self._next, self._current + self._next)\n", + " return self._current" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "13nuDa2SG2sK", + "outputId": "de87755e-ed16-447f-9a2b-cefc0fc7a12a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "1\n", + "2\n", + "3\n", + "5\n", + "8\n", + "13\n", + "21\n", + "34\n", + "55\n" + ] + } + ], + "source": [ + "limit = 10\n", + "\n", + "for item_f in FibIterator():\n", + " print(item_f)\n", + " limit -= 1\n", + " if limit == 0:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "i4lNIXO_Edk7" + }, + "source": [ + "### Генератор" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CWU2zNr6-1AZ" + }, + "source": [ + "#### Простой пример" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "g38UdGuA9TRD" + }, + "outputs": [], + "source": [ + "def sequence(end: int) -> list[int]:\n", + " \"\"\"Возвращает список целых чисел от 1 до n включительно.\"\"\"\n", + " res = [num for num in range(1, end + 1)]\n", + " return res" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qk2gw-wH9dUa", + "outputId": "13911636-550d-4ff0-81a6-95a8ee420a52" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(sequence(5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kX__mcgp8WJm" + }, + "outputs": [], + "source": [ + "def sequence_gen(num_arg: int) -> Generator[int, None, None]:\n", + " \"\"\"Генератор, возвращающий целые числа от 1 до n включительно.\"\"\"\n", + " yield from range(1, num_arg + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Cajg_qXU8oPt", + "outputId": "66943925-5e95-42c8-8b93-d5cdd7b53a85" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(sequence_gen(5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rOKU_bf68uP8", + "outputId": "253bc200-2441-4cab-f7a5-b0f0308b13a0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n" + ] + } + ], + "source": [ + "seq_5 = sequence_gen(5)\n", + "\n", + "print(next(seq_5))\n", + "print(next(seq_5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d573ZcOZ8yty", + "outputId": "2f2cb9ef-7348-44ac-ebb0-fd47642f93e2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "4\n", + "5\n" + ] + } + ], + "source": [ + "for item_s in seq_5:\n", + " print(item_s)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9iW5ZNZuORsE" + }, + "outputs": [], + "source": [ + "# next(seq_5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mFFbJfjvD27P" + }, + "source": [ + "#### Generator comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xGCSq8C4BLDQ", + "outputId": "e7c42fd1-39a5-4394-b844-28197b61c6c0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7ec755ccfe00>" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(num for num in range(1, 5 + 1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "33g4ikqqFcKJ", + "outputId": "4e36c66e-6fe5-4162-fc81-1acf2c15bc61" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(list(num for num in range(1, 5 + 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DyOGWjE2LrWx", + "outputId": "91736c7b-c50a-4990-cbc5-b81593c40586" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(sum(num for num in range(1, 5 + 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nzxrUQmEMR27", + "outputId": "f60aaee5-4be5-4905-f85e-4487d6d683b3" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(5 in (num for num in range(1, 5 + 1)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dh__pqr-ImTX" + }, + "source": [ + "### Модуль itertools" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zKzMJtVeLHp0" + }, + "source": [ + "#### Функция `count()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MzPekSQWJBPi", + "outputId": "46fd6fd0-3129-4109-dad8-e4939e62bf0c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "1.5\n", + "2.0\n" + ] + } + ], + "source": [ + "natural_numbers = count(start=1, step=0.5)\n", + "\n", + "for num in natural_numbers:\n", + " print(num)\n", + " if num == 2:\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DbmVDVFKnTlx", + "outputId": "4d7874e9-96fc-4e36-f445-55f4fc341c79" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 'A')\n", + "(1, 'B')\n", + "(2, 'C')\n", + "(3, 'D')\n" + ] + } + ], + "source": [ + "list_: list[str] = [\"A\", \"B\", \"C\", \"D\"]\n", + "for item_1 in zip(count(), list_):\n", + " print(item_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nKQCJ_hhoJaH", + "outputId": "a9cdc460-6cd9-4595-de7a-a8525207d65a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-2" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def quadratic_poly(num_arg: int) -> int:\n", + " \"\"\"Вычисляет значение квадратичной функции f(x) = x² + x - 2.\"\"\"\n", + " return num_arg**2 + num_arg - 2\n", + "\n", + "\n", + "f_x = map(quadratic_poly, count())\n", + "next(f_x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nmLgF8Xio_Kd", + "outputId": "8c8407b8-8043-4ef5-b12c-13402ac22752" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "4\n", + "10\n", + "18\n" + ] + } + ], + "source": [ + "for value in f_x:\n", + " print(value)\n", + " if value > 10:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_G5eWGYGmYMp" + }, + "source": [ + "#### Функция `cycle()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_kC3VssumbF5", + "outputId": "7aea461d-18b5-4454-9837-86f65c7280a0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n", + "1\n", + "2\n" + ] + } + ], + "source": [ + "list_1: list[int] = [1, 2, 3]\n", + "iterator_d = cycle(list_1)\n", + "\n", + "limit = 5\n", + "for item_2 in iterator_d:\n", + " print(item_2)\n", + " limit -= 1\n", + " if limit == 0:\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-IqqesXxrT9M", + "outputId": "8877b29b-24f7-4a4a-eaae-c24bc64786ef" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "P\n", + "y\n", + "t\n", + "h\n", + "o\n", + "n\n", + "P\n", + "y\n", + "t\n", + "h\n" + ] + } + ], + "source": [ + "string = \"Python\"\n", + "iterator_e = cycle(string)\n", + "\n", + "limit = 10\n", + "for item_3 in iterator_e:\n", + " print(item_3)\n", + " limit -= 1\n", + " if limit == 0:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ih5bXVq9sGh6" + }, + "source": [ + "#### Функция `chain()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EzUlrbwZrmG4", + "outputId": "86f0b35e-e9dc-4749-d987-f02b86d88485" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iterator_f = chain([\"abc\", \"d\", \"e\", \"f\"], \"abc\", [1, 2, 3])\n", + "print(iterator_f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Uzij2-C2ttuk", + "outputId": "6895d222-7929-47f6-c8b1-0891648b34ac" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['abc', 'd', 'e', 'f', 'a', 'b', 'c', 1, 2, 3]" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(list(iterator))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "exjJKXZjvZJb", + "outputId": "7c677fcf-7cad-45b4-b4c6-6e61ff8d15f2" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'd', 'e', 'f']" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(list(chain.from_iterable([\"abc\", \"def\"])))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qE3YDv6SufLi", + "outputId": "9e2037bc-b04e-4cf2-d8ba-9b9d7e045ce7" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "45" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result_1 = sum(chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HApgWQJPyGPa" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_15_iterators.py b/Python/makarov/chapter_15_iterators.py new file mode 100644 index 00000000..d5505c39 --- /dev/null +++ b/Python/makarov/chapter_15_iterators.py @@ -0,0 +1,333 @@ +"""Макаров. + +Итераторы и генераторы. +""" + +# ## Итераторы и генераторы + +# ### Итерируемый объект и итератор + +# #### Основные определения + +# + +from collections.abc import Generator, Iterator +from itertools import chain, count, cycle + +for index in [1, 2, 3]: + print(index) +# - + +# встроенная функция iter() вызывает метод .__iter__(), +# создающий итератор +iterator1 = iter([1, 2, 3]) + +# + +iterable_object: list[int] = [1, 2, 3] + +iterator = iter(iterable_object) +print(iterator) +print() + +print(next(iterator)) +print(next(iterator)) +print(next(iterator)) +# - + +for iterator2 in iterable_object: + print(iterator2) + +# + +iterator_a = iter(iterable_object) +iterator_b = iter(iterable_object) + +print(f"A: {next(iterator_a)}") +print(f"A: {next(iterator_a)}") +print(f"A: {next(iterator_a)}") +print(f"B: {next(iterator_b)}") +# - + +iterable_object + +# + +# print(f'A: {next(iterator_a)}') +# - + +print(list(iterator_a), list(iterator_b)) + +for item_s in {1, 1, 2, 3}: # pylint: disable=C0208,W0130 + print(item_s) + +# #### Отсутствие "обратного хода" + +# + +iterator_c = iter(iterable_object) + +for item_c_1 in iterator_c: + print(item_c_1) + break + +for item_c_2 in iterator_c: + print(item_c_2) +# - + +# #### Функция `zip()` + +iterator_tuple = zip(iterable_object, iterable_object) +print(iterator_tuple) + +print(next(iterator_tuple)) +print(next(iterator_tuple)) +print(next(iterator_tuple)) + +for item_z in zip(iterable_object, iterable_object): + print(item_z) + + +# #### Примеры итераторов + +# Возведение в квадрат + + +class Square: + """Итератор, возводящий в квадрат числа из переданной + последовательности.""" + + def __init__(self, seq: list[int]) -> None: + """Инициализация итератора.""" + self._seq = seq + self._idx = 0 + + def __iter__(self) -> "Square": + """Возвращает сам объект как итератор.""" + return self + + def __next__(self) -> int: + """Возвращает следующее значение итератора.""" + if self._idx < len(self._seq): + square = self._seq[self._idx] ** 2 + self._idx += 1 + return square + + raise StopIteration + + +square_1 = Square([1, 2, 3, 4, 5]) +print(square_1) + +for num_square in square_1: + print(num_square) + + +# Счетчик + + +class Counter: + """Итератор, генерирующий последовательность целых чисел в заданном + диапазоне.""" + + def __init__(self, start: int = 3, stop: int = 9) -> None: + """Инициализирует итератор с заданными границами диапазона.""" + self._current = start - 1 + self._stop = stop + + def __iter__(self) -> "Counter": + """Возвращает сам объект как итератор.""" + return self + + def __next__(self) -> int: + """Возвращает следующее значение в последовательности.""" + self._current += 1 + if self._current < self._stop: + return self._current + + raise StopIteration + + +counter = Counter() +print(counter) + +print(next(counter)) +print(next(counter)) + +for current_count in counter: + print(current_count) + + +# Класс Iterator модуля collections.abc + + +class Counter2(Iterator[int]): + """Итератор. + + Генерирует последовательность целых чисел в заданном диапазоне. + """ + + def __init__(self, start: int = 3, stop: int = 9) -> None: + """Инициализирует итератор с заданными границами диапазона.""" + self._current = start - 1 + self._stop = stop + + def __next__(self) -> int: + """Возвращает следующее значение в последовательности.""" + self._current += 1 + if self._current < self._stop: + return self._current + + raise StopIteration + + +for current_count in Counter2(): + print(current_count) + + +# Бесконечный итератор + + +class FibIterator: + """Бесконечный Итератор. + + Генерирует последовательность целых чисел, начиная с 0. + """ + + def __init__(self) -> None: + """Инициализирует итератор.""" + self._idx = 0 + self._current = 0 + self._next = 1 + + def __iter__(self) -> "FibIterator": + """Возвращает сам объект как итератор.""" + return self + + def __next__(self) -> int: + """Возвращает следующее значение в последовательности.""" + self._idx += 1 + self._current, self._next = (self._next, self._current + self._next) + return self._current + + +# + +limit = 10 + +for item_f in FibIterator(): + print(item_f) + limit -= 1 + if limit == 0: + break + + +# - + +# ### Генератор + +# #### Простой пример + + +def sequence(end: int) -> list[int]: + """Возвращает список целых чисел от 1 до n включительно.""" + res = [num for num in range(1, end + 1)] + return res + + +print(sequence(5)) + + +def sequence_gen(num_arg: int) -> Generator[int, None, None]: + """Генератор, возвращающий целые числа от 1 до n включительно.""" + yield from range(1, num_arg + 1) + + +print(sequence_gen(5)) + +# + +seq_5 = sequence_gen(5) + +print(next(seq_5)) +print(next(seq_5)) +# - + +for item_s in seq_5: + print(item_s) + +# + +# next(seq_5) +# - + +# #### Generator comprehension + +print(num for num in range(1, 5 + 1)) + +print(list(num for num in range(1, 5 + 1))) + +print(sum(num for num in range(1, 5 + 1))) + +print(5 in (num for num in range(1, 5 + 1))) + +# ### Модуль itertools + +# #### Функция `count()` + +# + +natural_numbers = count(start=1, step=0.5) + +for num in natural_numbers: + print(num) + if num == 2: + break +# - + +list_: list[str] = ["A", "B", "C", "D"] +for item_1 in zip(count(), list_): + print(item_1) + + +# + +def quadratic_poly(num_arg: int) -> int: + """Вычисляет значение квадратичной функции f(x) = x² + x - 2.""" + return num_arg**2 + num_arg - 2 + + +f_x = map(quadratic_poly, count()) +next(f_x) +# - + +for value in f_x: + print(value) + if value > 10: + break + +# #### Функция `cycle()` + +# + +list_1: list[int] = [1, 2, 3] +iterator_d = cycle(list_1) + +limit = 5 +for item_2 in iterator_d: + print(item_2) + limit -= 1 + if limit == 0: + break + +# + +string = "Python" +iterator_e = cycle(string) + +limit = 10 +for item_3 in iterator_e: + print(item_3) + limit -= 1 + if limit == 0: + break +# - + +# #### Функция `chain()` + +iterator_f = chain(["abc", "d", "e", "f"], "abc", [1, 2, 3]) +print(iterator_f) + +print(list(iterator)) + +print(list(chain.from_iterable(["abc", "def"]))) + +result_1 = sum(chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) diff --git a/Python/makarov/chapter_16_decorators.ipynb b/Python/makarov/chapter_16_decorators.ipynb new file mode 100644 index 00000000..2f65d8fe --- /dev/null +++ b/Python/makarov/chapter_16_decorators.ipynb @@ -0,0 +1,2070 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Макаров.\n", + "\n", + "Декораторы.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uj3W4pZ3dgXv" + }, + "source": [ + "# Декораторы" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UWUu0aaT-u4B" + }, + "source": [ + "### Объекты первого класса" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IXifYlrj_YbX" + }, + "source": [ + "Присвоение функции переменной" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ahhHl0zTdfxX" + }, + "outputs": [], + "source": [ + "import functools\n", + "import time\n", + "from typing import Callable, ParamSpec, TypeVar\n", + "\n", + "# ParamSpec - это переменная типа для сигнатуры функции: она «запоминает»\n", + "# все аргументы (позиционные и именованные) функции,\n", + "# чтобы их можно было точно передать дальше с сохранением типобезопасности.\n", + "# Основное применение - функции высшего порядка (в отм числе декораторы)\n", + "Params = ParamSpec(\"Params\")\n", + "\n", + "# TypeVar - это переменная типа, которая позволяет писать обобщённые (generic)\n", + "# функции и классы, сохраняя связь между входными и выходными типами.\n", + "Return = TypeVar(\"Return\")\n", + "\n", + "# объявим функцию\n", + "\n", + "\n", + "def say_hello(name: str) -> None:\n", + " \"\"\"Выводит приветствие.\"\"\"\n", + " print(f\"Привет, {name}!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VKrmngnh-xqg", + "outputId": "43f26846-32a2-4a7c-ab74-ac3066ddbd57" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Привет, Алексей!\n" + ] + } + ], + "source": [ + "# присвоим эту функцию переменной (без скобок)\n", + "say_hello_function = say_hello\n", + "# вызовем функцию из новой переменной\n", + "say_hello_function(\"Алексей\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "INxKkQT__c41" + }, + "source": [ + "Передача функции в качестве аргумента другой функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YEwkUtPu_Iaf" + }, + "outputs": [], + "source": [ + "def simple_calculator(\n", + " operation: Callable[[int, int], int | float], a_arg: int, b_arg: int\n", + ") -> int | float:\n", + " \"\"\"Функция обертка.\n", + "\n", + " Вызывает переданную функцию с переданными аргументами.\n", + " \"\"\"\n", + " return operation(a_arg, b_arg)\n", + "\n", + "\n", + "def add(a_arg: int, b_arg: int) -> int:\n", + " \"\"\"Возвращает сумму двух аргументов.\"\"\"\n", + " return a_arg + b_arg\n", + "\n", + "\n", + "def subtract(a_arg: int, b_arg: int) -> int:\n", + " \"\"\"Возвращает разность двух аргументов.\"\"\"\n", + " return a_arg - b_arg\n", + "\n", + "\n", + "def multiply(a_arg: int, b_arg: int) -> int:\n", + " \"\"\"Возвращает произведение двух аргументов.\"\"\"\n", + " return a_arg * b_arg\n", + "\n", + "\n", + "def divide(a_arg: int, b_arg: int) -> float:\n", + " \"\"\"Возвращает частное от деления первого аргумента на второй аргумент.\"\"\"\n", + " return a_arg / b_arg" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "C0tKH9zgACpY", + "outputId": "73672462-f6be-4036-f019-628a3d0e23c0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.3333333333333333" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "simple_calculator(divide, 1, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jU0WfJWIN-cs" + }, + "source": [ + "### Внутренние функции" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uOl-kzspPzwv" + }, + "source": [ + "Вызов внутренней функции" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "wom-NUQnOZYs" + }, + "outputs": [], + "source": [ + "def outer() -> None:\n", + " \"\"\"Внешняя функция.\"\"\"\n", + " print(\"Вызов внешней функции.\")\n", + "\n", + " # обратите внимание, мы объявляем, а затем\n", + " def inner() -> None:\n", + " \"\"\"Внутренняя функция.\"\"\"\n", + " print(\"Вызов внутренней функции.\")\n", + "\n", + " # вызываем внутреннюю функцию\n", + " inner()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EG7HyDpGOpqF", + "outputId": "7afd2d51-8199-4eb9-8a4c-27afe15b655d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Вызов внешней функции.\n", + "Вызов внутренней функции.\n" + ] + } + ], + "source": [ + "outer()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lq8mXyyFPmNi" + }, + "outputs": [], + "source": [ + "# inner()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ggx5eY1BP2E_" + }, + "source": [ + "Возвращение функции из функции и замыкание" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "f92IsJmiAGEm" + }, + "outputs": [], + "source": [ + "def create_multiplier(factor: int) -> Callable[[int], int]:\n", + " \"\"\"Создает функцию-умножитель.\"\"\"\n", + "\n", + " def multiplier(number: int) -> int:\n", + " return number * factor\n", + "\n", + " return multiplier" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RKJh1YXZPfvy" + }, + "outputs": [], + "source": [ + "double = create_multiplier(factor=2)\n", + "triple = create_multiplier(factor=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 103 + }, + "id": "n3gfeRpjQJv7", + "outputId": "cf19c87f-2159-40cb-93a8-476f8954e477" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ".multiplier at 0x000002DBFA7B39C0>\n" + ] + } + ], + "source": [ + "print(double)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8e_BM_ZbPSrL", + "outputId": "e2e72c8d-1599-4a9b-8c9a-895845021852" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 6)" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(double(2), triple(2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iiyiBcU8dAqM" + }, + "outputs": [], + "source": [ + "def create_multiplier_1(factor: int) -> Callable[[int], int]:\n", + " \"\"\"Создает функцию-умножитель.\"\"\"\n", + " return lambda number: factor * number" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VuEd1WLtdMVY", + "outputId": "779967db-9a33-42e6-9f29-1f9b57b098a6" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "triple_1 = create_multiplier_1(factor=3)\n", + "triple_1(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jOxX3byTQcvZ" + }, + "source": [ + "### Знакомство с декораторами" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fl2py8bY_MGw" + }, + "source": [ + "Простой декоратор" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DqWzDydyPdS6" + }, + "outputs": [], + "source": [ + "def simple_decorator(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Простой декоратор.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(\"Текст до вызова функции func().\")\n", + " result = func(*args, **kwargs)\n", + " print(\"Текст после вызова функции func().\")\n", + "\n", + " return result\n", + "\n", + " return wrapper\n", + "\n", + "\n", + "def say_hello_1() -> None:\n", + " \"\"\"Функция-приветствие.\"\"\"\n", + " print(\"Привет!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YeZqM0Ri-0cE" + }, + "outputs": [], + "source": [ + "say_hello_var = simple_decorator(say_hello_1)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bRD44QdeTtbL", + "outputId": "26d50e1d-8dac-422f-a4b1-7250cd64f8c3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст до вызова функции func().\n", + "Привет!\n", + "Текст после вызова функции func().\n" + ] + } + ], + "source": [ + "say_hello_var()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3_n1GHNRDn-u" + }, + "source": [ + "Конструкция @decorator" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "id": "aPTsoheWUyqO" + }, + "outputs": [], + "source": [ + "@simple_decorator\n", + "def say_hi() -> None:\n", + " \"\"\"Функция повторного приветствия.\"\"\"\n", + " print(\"Снова, привет!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8FRvX4NEUl6T", + "outputId": "85a01dca-c68d-41b9-ea86-a489a8d45d50" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст до вызова функции func().\n", + "Снова, привет!\n", + "Текст после вызова функции func().\n" + ] + } + ], + "source": [ + "say_hi()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JaGa8Qwr_N-5" + }, + "source": [ + "Функции с аргументами" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fKHekNgUHFLo" + }, + "outputs": [], + "source": [ + "@simple_decorator\n", + "def say_hello_with_name(name_arg: str) -> None:\n", + " \"\"\"Приветствие с именем.\"\"\"\n", + " print(f\"Привет, {name_arg}!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EeW8G0LsHPJu" + }, + "outputs": [], + "source": [ + "# say_hello_with_name('Алексей')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WLoje-LpU_tJ" + }, + "outputs": [], + "source": [ + "def decorator_with_name_argument(\n", + " func: Callable[[str], None],\n", + ") -> Callable[[str], None]:\n", + " \"\"\"Декоратор, принимающий имя.\"\"\"\n", + "\n", + " def wrapper(name: str) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(\"Текст до вызова функции func().\")\n", + " func(name)\n", + " print(\"Текст после вызова функции func().\")\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HcQaW7IAVm5Q" + }, + "outputs": [], + "source": [ + "@decorator_with_name_argument\n", + "def say_hello_with_name_1(name: str) -> None:\n", + " \"\"\"Приветствие с именем.\"\"\"\n", + " print(f\"Привет, {name}!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ps96hpvjV9Ra", + "outputId": "95ea1054-a1e0-49ee-cc9b-a93fa8075804" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст до вызова функции func().\n", + "Привет, Алексей!\n", + "Текст после вызова функции func().\n" + ] + } + ], + "source": [ + "say_hello_with_name_1(\"Алексей\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XLO10uz8jTgf" + }, + "outputs": [], + "source": [ + "def decorator_with_arguments(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Декоратор с аргументами.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(\"Текст до вызова функции func().\")\n", + "\n", + " result = func(*args, **kwargs)\n", + "\n", + " print(\"Текст после вызова функции func().\")\n", + "\n", + " return result\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mmkQw_NeQgDo" + }, + "outputs": [], + "source": [ + "@decorator_with_arguments\n", + "def say_hello_with_argument(name: str) -> None:\n", + " \"\"\"Функция-приветствие, принимающая имя.\"\"\"\n", + " print(f\"Привет, {name}!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6an3iiFLQjhx", + "outputId": "709e6c14-596e-4feb-ffd3-f8c3d0ef796f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст до вызова функции func().\n", + "Привет, Алексей!\n", + "Текст после вызова функции func().\n" + ] + } + ], + "source": [ + "say_hello_with_argument(\"Алексей\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nYfQs9qTTvpx" + }, + "source": [ + "Возвращение значения декорируемой функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n3O_bUeDcd6H" + }, + "outputs": [], + "source": [ + "def another_decorator(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Другой декоратор с аргументами.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(\"Текст внутренней функции.\")\n", + "\n", + " return func(*args, **kwargs)\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0QfyEIQ0Tyo1" + }, + "outputs": [], + "source": [ + "@another_decorator\n", + "def return_name_1(name: str) -> str:\n", + " \"\"\"Возвращает переданное имя.\"\"\"\n", + " return name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Odulil-rT_6L", + "outputId": "c87fc41e-03c5-4af8-d8eb-4cc98e3a7db3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст внутренней функции.\n" + ] + } + ], + "source": [ + "returned_value = return_name_1(\"Алексей\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Z4mn1aNqUItV", + "outputId": "c86cc40d-161b-4196-8fa5-640d6ad05008" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "print(returned_value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vY4KoCEpUWPF" + }, + "outputs": [], + "source": [ + "def another_decorator_1(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Другой декоратор.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(\"Текст внутренней функции.\")\n", + " return func(*args, **kwargs) # внутренняя функция возвращает func()\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pdBi1JIgUfbA" + }, + "outputs": [], + "source": [ + "@another_decorator_1\n", + "def return_name(name_arg: str) -> str:\n", + " \"\"\"Возвращает переданное имя.\"\"\"\n", + " return name_arg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DgmF5Y80UhxE", + "outputId": "fe7a2730-8dec-4c6e-9cba-3b6e16a71c04" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Текст внутренней функции.\n" + ] + } + ], + "source": [ + "returned_value = return_name(\"Алексей\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "C2pBMz0WcDdL", + "outputId": "76d1d18f-d81e-49c9-eecc-9bcfddcf88ff" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Алексей\n" + ] + } + ], + "source": [ + "print(returned_value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "96f9wVE9V9Vr" + }, + "source": [ + "Декоратор @functools.wraps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MdPnhLh7UjM8" + }, + "outputs": [], + "source": [ + "def square(num: int) -> int:\n", + " \"\"\"Squares a number.\"\"\"\n", + " return num * num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "V4UwZvebVvGN", + "outputId": "2fd308ec-95f3-4dad-e197-8c77a432f69c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('square', 'Squares a number')" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(square.__name__, square.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0iKP_IpEWNnn" + }, + "outputs": [], + "source": [ + "def repeat_twice(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, None]:\n", + " \"\"\"Декоратор, который дважды вызывает функция.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " func(*args, **kwargs)\n", + " func(*args, **kwargs)\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "id": "XQPua1yJVwWl" + }, + "outputs": [], + "source": [ + "@repeat_twice\n", + "def square_1(num: int) -> int:\n", + " \"\"\"Squares a number.\"\"\"\n", + " result = num * num\n", + "\n", + " print(result)\n", + "\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "id": "Xp1b9jLebCpC" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9\n", + "9\n" + ] + } + ], + "source": [ + "square_1(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "g5Ijk3fvXDtD", + "outputId": "c0680c69-21fa-47c6-c89d-22c5083f7a2b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('wrapper', None)" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "square.__name__, square.__doc__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TlJNvigxXMZE" + }, + "outputs": [], + "source": [ + "def repeat_twice_1(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, None]:\n", + " \"\"\"Декоратор, который дважды вызывает функция.\"\"\"\n", + "\n", + " @functools.wraps(func)\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " func(*args, **kwargs)\n", + " func(*args, **kwargs)\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "id": "DpxKd94oXQlA" + }, + "outputs": [], + "source": [ + "@repeat_twice_1\n", + "def square_2(num: int) -> int:\n", + " \"\"\"Squares a number.\"\"\"\n", + " result = num * num\n", + "\n", + " print(result)\n", + "\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YsaelJl3YVA0", + "outputId": "9e453de0-6370-41d4-ac20-1d060b0592f3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "square_2 Squares a number.\n" + ] + } + ], + "source": [ + "print(square_2.__name__, square_2.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 103 + }, + "id": "Wvl0I71Oh4dc", + "outputId": "3ce27822-0bbe-4008-f36e-3d5a9d9807db" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print(square_2.__wrapped__) # type: ignore" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tNFRtACYf2Ik" + }, + "outputs": [], + "source": [ + "def repeat_twice_2(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, None]:\n", + " \"\"\"Декоратор, который дважды вызывает функция.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " func(*args, **kwargs)\n", + " func(*args, **kwargs)\n", + " functools.update_wrapper(wrapper, func)\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OqtPbkS0f-Ox" + }, + "outputs": [], + "source": [ + "@repeat_twice_2\n", + "def power(num: int, pow_arg: int) -> None:\n", + " \"\"\"Raise to a power.\"\"\"\n", + " print(num**pow_arg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cUOkkwwdgFHh", + "outputId": "898e551c-b7bc-4f4f-a647-5342b1d174f5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8\n", + "8\n" + ] + } + ], + "source": [ + "power(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 37 + }, + "id": "HZvDG-hRgHbZ", + "outputId": "258e9681-6bac-4d0f-c4bd-7f639e2ff18b" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Raises to a power'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(power.__doc__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DRNFII11hPme" + }, + "source": [ + "### Примеры декораторов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "90ciKrTXhR4y" + }, + "source": [ + "Создание логов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PjrkD7rTgQZh" + }, + "outputs": [], + "source": [ + "def logging(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Логирование вызовов функции.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(f\"Calling {func.__name__} with args: {args}, kwargs: {kwargs}\")\n", + "\n", + " result = func(*args, **kwargs)\n", + "\n", + " print(f\"{func.__name__} returned: {result}\")\n", + "\n", + " return result\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "q5a47uz5iDcE", + "outputId": "f98c0fa3-3b97-4247-a5ae-cbc0da48e92a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calling power_1 with args: (5, 3), kwargs: {}\n", + "power_1 returned: 125\n" + ] + }, + { + "data": { + "text/plain": [ + "125" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@logging\n", + "def power_1(num: int, pow_arg: int) -> int:\n", + " \"\"\"Raise to a power.\"\"\"\n", + " return int(num**pow_arg)\n", + "\n", + "\n", + "power_1(5, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BATiTOZch5Hd" + }, + "source": [ + "Время исполнения функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JzUr3gBrhgsg" + }, + "outputs": [], + "source": [ + "def timer(\n", + " func: Callable[Params, Return],\n", + ") -> Callable[Params, Return]:\n", + " \"\"\"Декоратор для вычисления времени выполнения функции.\"\"\"\n", + "\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " start_time = time.time()\n", + "\n", + " result = func(*args, **kwargs)\n", + "\n", + " end_time = time.time()\n", + "\n", + " print(\n", + " f\"{func.__name__} executed in {end_time - start_time:.4f} seconds\"\n", + " )\n", + "\n", + " return result\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 55 + }, + "id": "MvgB0y7IiKEJ", + "outputId": "35bdcec2-a698-40c0-8357-d49bf741c4fb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delayed_function executed in 2.0012 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "'execution completed'" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@timer\n", + "def delayed_function(delay: int) -> str:\n", + " \"\"\"Функция с задержкой выполнения.\"\"\"\n", + " time.sleep(delay)\n", + " return \"execution completed\"\n", + "\n", + "\n", + "delayed_function(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Far9nIP_lBcV" + }, + "source": [ + "### Типы методов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yiBHRhX0mr1X" + }, + "source": [ + "Методы экземпляра" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uMswH5MoiBQg" + }, + "outputs": [], + "source": [ + "class CatClass:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"\n", + "\n", + " def info(self) -> None:\n", + " \"\"\"Вывод информации о коте.\"\"\"\n", + " print(self.color, self.type_, sep=\", \")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ORHkpRULnV48", + "outputId": "5fab4183-8729-4a02-8996-a35b50a330fd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "black, cat\n" + ] + } + ], + "source": [ + "cat = CatClass(color=\"black\")\n", + "cat.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VUKAK7pIO0OU" + }, + "outputs": [], + "source": [ + "# CatClass.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mXPMkHLjQn_i" + }, + "outputs": [], + "source": [ + "# CatClass.color" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ny5x6rT7o3sN" + }, + "source": [ + "Методы класса" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "id": "CIn4oi0TniKB" + }, + "outputs": [], + "source": [ + "class CatClass1:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " species = \"кошка\" # переменная класса доступна всем экземплярам\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + "\n", + " def info(self) -> None:\n", + " \"\"\"Вывод информации о коте.\"\"\"\n", + " print(self.color)\n", + "\n", + " @classmethod\n", + " def get_species(cls) -> None:\n", + " \"\"\"Вывод значения аттрибута класса species.\"\"\"\n", + " print(cls.species)\n", + " # нет доступа к переменным color и type_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 37 + }, + "id": "OeiyzIrgoJnW", + "outputId": "f7cd1440-f237-4aa4-cff6-e2d35fa3be4d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'кошка'" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(CatClass1.species)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qq-lk-R1nInf", + "outputId": "764649e5-7dcb-4178-c46c-b11d81724efe" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "кошка\n" + ] + } + ], + "source": [ + "CatClass1.get_species()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ksfgajzZo9wu" + }, + "source": [ + "Статические методы" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JuPIH3Z7o0n2" + }, + "outputs": [], + "source": [ + "class CatClass2:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " species = \"кошка\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"\n", + "\n", + " def info(self) -> None:\n", + " \"\"\"Вывод информации о коте.\"\"\"\n", + " print(self.color, self.type_)\n", + "\n", + " @classmethod\n", + " def get_species(cls) -> None:\n", + " \"\"\"Вывод значения аттрибута класса species.\"\"\"\n", + " print(cls.species)\n", + " # нет доступа к переменным color и type_\n", + "\n", + " @staticmethod\n", + " def convert_to_pounds(kg: int) -> None:\n", + " \"\"\"Конвертирует килограммы в фунты.\"\"\"\n", + " print(f\"{kg} kg is approximately {kg * 2.205} pounds\")\n", + " # нет доступа к переменным species, color и type_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bfz6Wubqp-AO", + "outputId": "d42fac14-e6b5-4be2-ab69-66b1e7e7c049" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 kg is approximately 8.82 pounds\n" + ] + } + ], + "source": [ + "CatClass2.convert_to_pounds(4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DaLLVoiEqEQF", + "outputId": "486cda8c-a04f-4e3c-db24-13281f3cee9e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 kg is approximately 11.025 pounds\n" + ] + } + ], + "source": [ + "cat2 = CatClass2(\"gray\")\n", + "cat2.convert_to_pounds(5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xy2HkzH6ZrRG" + }, + "source": [ + "### Декорирование класса" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zKj05Fh06Sm_" + }, + "source": [ + "Декорирование методов" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "id": "G9MuM7PCZs8-" + }, + "outputs": [], + "source": [ + "class CatClass3:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " @logging\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"\n", + "\n", + " @timer\n", + " def info(self) -> None:\n", + " \"\"\"Вывод информации о коте.\"\"\"\n", + " time.sleep(2)\n", + " print(self.color, self.type_, sep=\", \")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3fiHjwEJ4cTV", + "outputId": "cd8d8c29-666c-44c3-a103-087093a1db83" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calling __init__ with args: (<__main__.CatClass3 object at 0x000002DBFA7C5550>, 'black'), kwargs: {}\n", + "__init__ returned: None\n" + ] + } + ], + "source": [ + "cat3 = CatClass3(\"black\")" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Kt7-LubB4y6g", + "outputId": "ee6e78e7-6269-4b3d-8a7b-075938562c9d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "black, cat\n", + "info executed in 2.0005 seconds\n" + ] + } + ], + "source": [ + "cat3.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "epmndsSc6Uvh" + }, + "source": [ + "Декорирование всего класса" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "id": "0cXQX0004174" + }, + "outputs": [], + "source": [ + "@timer\n", + "class CatClass4:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"\n", + "\n", + " def info(self) -> None:\n", + " \"\"\"Вывод информации о коте.\"\"\"\n", + " time.sleep(2)\n", + " print(self.color, self.type_, sep=\", \")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "P6aIA7_a6gZh", + "outputId": "c40ae050-d7dc-4488-a3ec-cfdf93be806b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CatClass4 executed in 0.0000 seconds\n" + ] + } + ], + "source": [ + "cat4 = CatClass4(\"gray\")" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-HqSIPFG6jo2", + "outputId": "bc78ba12-4f6c-4a8f-9499-96b6feb5a561" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gray, cat\n" + ] + } + ], + "source": [ + "cat4.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "id": "wN17-w_s7WI7" + }, + "outputs": [], + "source": [ + "setattr(cat4, \"weight\", 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "w-3c9Vny7d4f", + "outputId": "81981202-4e38-491b-8078-1100747bf04b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 5\n" + ] + } + ], + "source": [ + "print(\n", + " cat4.weight, # type: ignore # pylint: disable=E1101\n", + " getattr(cat4, \"weight\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "suehA7UO6ldC" + }, + "outputs": [], + "source": [ + "# TypeVar - это переменная типа, которая позволяет писать обобщённые (generic)\n", + "# функции и классы, сохраняя связь между входными и выходными типами.\n", + "Class = TypeVar(\"Class\", bound=type)\n", + "\n", + "\n", + "def add_attribute(\n", + " attribute_name: str, attribute_value: str\n", + ") -> Callable[[Class], Class]:\n", + " \"\"\"Декоратор класса, добавляющий указанный атрибут к классу.\"\"\"\n", + "\n", + " def wrapper(cls: Class) -> Class:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " setattr(cls, attribute_name, attribute_value)\n", + " return cls\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "id": "ra4O3lsm62RO" + }, + "outputs": [], + "source": [ + "@add_attribute(\"species\", \"кошка\")\n", + "class CatClass5:\n", + " \"\"\"Класс кот.\"\"\"\n", + "\n", + " def __init__(self, color: str) -> None:\n", + " \"\"\"Инициализация кота с цветом.\"\"\"\n", + " self.color = color\n", + " self.type_ = \"cat\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 37 + }, + "id": "VRPuN3Wk75y4", + "outputId": "201d5a8f-811b-4812-e834-2874b53ccd8d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "кошка\n" + ] + } + ], + "source": [ + "print(CatClass5.species) # type: ignore # pylint: disable=E1101" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D9EuxH7p8Tcz" + }, + "source": [ + "### Несколько декораторов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NqYf_Co077ii" + }, + "outputs": [], + "source": [ + "@logging\n", + "@timer\n", + "def delayed_function_1(delay: int) -> str:\n", + " \"\"\"Функция с задержкой выполнения.\"\"\"\n", + " time.sleep(delay)\n", + " return \"execution completed\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 91 + }, + "id": "Px_2Rz_Z8goN", + "outputId": "7f998623-0dd3-4a0f-e8dd-27eab7506d4a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calling wrapper with args: (2,), kwargs: {}\n", + "delayed_function executed in 2.0001 seconds\n", + "wrapper returned: execution completed\n" + ] + }, + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'execution completed'" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "delayed_function_1(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6vA85Et4Gs4v" + }, + "outputs": [], + "source": [ + "# не забудем заново объявить функцию без декораторов\n", + "\n", + "\n", + "def delayed_function_3(delay: int) -> str:\n", + " \"\"\"Функция с задержкой выполнения.\"\"\"\n", + " time.sleep(delay)\n", + " return \"execution completed\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 91 + }, + "id": "oBdmnqV18ifq", + "outputId": "91a5a86a-5090-49ae-8476-8c7c6b4bf981" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calling wrapper with args: (2,), kwargs: {}\n", + "delayed_function executed in 2.0001 seconds\n", + "wrapper returned: execution completed\n" + ] + }, + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'execution completed'" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "delayed_function_4 = logging(timer(delayed_function))\n", + "delayed_function_4(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "drHaoUEXEPfP" + }, + "source": [ + "### Декораторы с аргументами" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wrew14E2Fm2L" + }, + "outputs": [], + "source": [ + "def repeat(\n", + " n_times: int,\n", + ") -> Callable[[Callable[Params, Return]], Callable[Params, None]]:\n", + " \"\"\"Декоратор, вызывающий функцию указанное количество раз.\"\"\"\n", + "\n", + " def inner_decorator(\n", + " func: Callable[Params, Return],\n", + " ) -> Callable[Params, None]:\n", + " \"\"\"Внутренний декоратор.\"\"\"\n", + "\n", + " @functools.wraps(func)\n", + " def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " for _ in range(n_times):\n", + " func(*args, **kwargs)\n", + "\n", + " return wrapper\n", + "\n", + " return inner_decorator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1KdWR3viIeBk" + }, + "outputs": [], + "source": [ + "@repeat(n_times=3)\n", + "def say_hello_3(name: str) -> None:\n", + " \"\"\"Функция-обертка.\"\"\"\n", + " print(f\"Привет, {name}!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "N2zEUb1IIiJ6", + "outputId": "542eff2e-1641-421f-a208-12a53e98037e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Привет, Алексей!\n", + "Привет, Алексей!\n", + "Привет, Алексей!\n" + ] + } + ], + "source": [ + "say_hello_3(\"Алексей\")" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Python/makarov/chapter_16_decorators.py b/Python/makarov/chapter_16_decorators.py new file mode 100644 index 00000000..7e7311b4 --- /dev/null +++ b/Python/makarov/chapter_16_decorators.py @@ -0,0 +1,753 @@ +# %% +"""Макаров. + +Декораторы. +""" + +# # Декораторы + +# ### Объекты первого класса + +# Присвоение функции переменной + +# %% +import functools +import time +from typing import Callable, ParamSpec, TypeVar + +# ParamSpec - это переменная типа для сигнатуры функции: она «запоминает» +# все аргументы (позиционные и именованные) функции, +# чтобы их можно было точно передать дальше с сохранением типобезопасности. +# Основное применение - функции высшего порядка (в отм числе декораторы) +Params = ParamSpec("Params") + +# TypeVar - это переменная типа, которая позволяет писать обобщённые (generic) +# функции и классы, сохраняя связь между входными и выходными типами. +Return = TypeVar("Return") + +# объявим функцию + + +def say_hello(name: str) -> None: + """Выводит приветствие.""" + print(f"Привет, {name}!") + + +# %% +# присвоим эту функцию переменной (без скобок) +say_hello_function = say_hello +# вызовем функцию из новой переменной +say_hello_function("Алексей") + + +# Передача функции в качестве аргумента другой функции + + +# %% +def simple_calculator( + operation: Callable[[int, int], int | float], a_arg: int, b_arg: int +) -> int | float: + """Функция обертка. + + Вызывает переданную функцию с переданными аргументами. + """ + return operation(a_arg, b_arg) + + +def add(a_arg: int, b_arg: int) -> int: + """Возвращает сумму двух аргументов.""" + return a_arg + b_arg + + +def subtract(a_arg: int, b_arg: int) -> int: + """Возвращает разность двух аргументов.""" + return a_arg - b_arg + + +def multiply(a_arg: int, b_arg: int) -> int: + """Возвращает произведение двух аргументов.""" + return a_arg * b_arg + + +def divide(a_arg: int, b_arg: int) -> float: + """Возвращает частное от деления первого аргумента на второй аргумент.""" + return a_arg / b_arg + + +# %% +simple_calculator(divide, 1, 3) + + +# ### Внутренние функции + +# Вызов внутренней функции + + +# %% +def outer() -> None: + """Внешняя функция.""" + print("Вызов внешней функции.") + + # обратите внимание, мы объявляем, а затем + def inner() -> None: + """Внутренняя функция.""" + print("Вызов внутренней функции.") + + # вызываем внутреннюю функцию + inner() + + +# %% +outer() + + +# inner() + +# Возвращение функции из функции и замыкание + + +# %% +def create_multiplier(factor: int) -> Callable[[int], int]: + """Создает функцию-умножитель.""" + + def multiplier(number: int) -> int: + return number * factor + + return multiplier + + +# %% +double = create_multiplier(factor=2) +triple = create_multiplier(factor=3) + +# %% +print(double) + +# %% +print(double(2), triple(2)) + + +# %% +def create_multiplier_1(factor: int) -> Callable[[int], int]: + """Создает функцию-умножитель.""" + return lambda number: factor * number + + +# %% +triple_1 = create_multiplier_1(factor=3) +triple_1(2) + + +# ### Знакомство с декораторами + +# Простой декоратор + + +# %% +def simple_decorator( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Простой декоратор.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + print("Текст до вызова функции func().") + result = func(*args, **kwargs) + print("Текст после вызова функции func().") + + return result + + return wrapper + + +def say_hello_1() -> None: + """Функция-приветствие.""" + print("Привет!") + + +# %% +say_hello_var = simple_decorator(say_hello_1) + +# %% +say_hello_var() + + +# Конструкция @decorator + + +# %% +@simple_decorator +def say_hi() -> None: + """Функция повторного приветствия.""" + print("Снова, привет!") + + +# %% +say_hi() + + +# Функции с аргументами + + +# %% +@simple_decorator +def say_hello_with_name(name_arg: str) -> None: + """Приветствие с именем.""" + print(f"Привет, {name_arg}!") + + +# say_hello_with_name('Алексей') + + +# %% +def decorator_with_name_argument( + func: Callable[[str], None], +) -> Callable[[str], None]: + """Декоратор, принимающий имя.""" + + def wrapper(name: str) -> None: + """Функция-обертка.""" + print("Текст до вызова функции func().") + func(name) + print("Текст после вызова функции func().") + + return wrapper + + +# %% +@decorator_with_name_argument +def say_hello_with_name_1(name: str) -> None: + """Приветствие с именем.""" + print(f"Привет, {name}!") + + +# %% +say_hello_with_name_1("Алексей") + + +# %% +def decorator_with_arguments( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Декоратор с аргументами.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + print("Текст до вызова функции func().") + + result = func(*args, **kwargs) + + print("Текст после вызова функции func().") + + return result + + return wrapper + + +# %% +@decorator_with_arguments +def say_hello_with_argument(name: str) -> None: + """Функция-приветствие, принимающая имя.""" + print(f"Привет, {name}!") + + +# %% +say_hello_with_argument("Алексей") + + +# Возвращение значения декорируемой функции + + +# %% +def another_decorator( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Другой декоратор с аргументами.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + print("Текст внутренней функции.") + + return func(*args, **kwargs) + + return wrapper + + +# %% +@another_decorator +def return_name_1(name: str) -> str: + """Возвращает переданное имя.""" + return name + + +# %% +returned_value = return_name_1("Алексей") + +# %% +print(returned_value) + + +# %% +def another_decorator_1( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Другой декоратор.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + print("Текст внутренней функции.") + return func(*args, **kwargs) # внутренняя функция возвращает func() + + return wrapper + + +# %% +@another_decorator_1 +def return_name(name_arg: str) -> str: + """Возвращает переданное имя.""" + return name_arg + + +# %% +returned_value = return_name("Алексей") + +# %% +print(returned_value) + + +# Декоратор @functools.wraps + + +# %% +def square(num: int) -> int: + """Squares a number.""" + return num * num + + +# %% +print(square.__name__, square.__doc__) + + +# %% +def repeat_twice( + func: Callable[Params, Return], +) -> Callable[Params, None]: + """Декоратор, который дважды вызывает функция.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None: + """Функция-обертка.""" + func(*args, **kwargs) + func(*args, **kwargs) + + return wrapper + + +# %% +@repeat_twice +def square_1(num: int) -> int: + """Squares a number.""" + result = num * num + + print(result) + + return result + + +# %% +square_1(3) + +# %% +square.__name__, square.__doc__ + + +# %% +def repeat_twice_1( + func: Callable[Params, Return], +) -> Callable[Params, None]: + """Декоратор, который дважды вызывает функция.""" + + @functools.wraps(func) + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None: + """Функция-обертка.""" + func(*args, **kwargs) + func(*args, **kwargs) + + return wrapper + + +# %% +@repeat_twice_1 +def square_2(num: int) -> int: + """Squares a number.""" + result = num * num + + print(result) + + return result + + +# %% +print(square_2.__name__, square_2.__doc__) + +# %% +print(square_2.__wrapped__) # type: ignore + + +# %% +def repeat_twice_2( + func: Callable[Params, Return], +) -> Callable[Params, None]: + """Декоратор, который дважды вызывает функция.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None: + """Функция-обертка.""" + func(*args, **kwargs) + func(*args, **kwargs) + functools.update_wrapper(wrapper, func) + + return wrapper + + +# %% +@repeat_twice_2 +def power(num: int, pow_arg: int) -> None: + """Raise to a power.""" + print(num**pow_arg) + + +# %% +power(2, 3) + +# %% +print(power.__doc__) + + +# ### Примеры декораторов + +# Создание логов + + +# %% +def logging( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Логирование вызовов функции.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}") + + result = func(*args, **kwargs) + + print(f"{func.__name__} returned: {result}") + + return result + + return wrapper + + +# %% +@logging +def power_1(num: int, pow_arg: int) -> int: + """Raise to a power.""" + return int(num**pow_arg) + + +power_1(5, 3) + + +# Время исполнения функции + + +# %% +def timer( + func: Callable[Params, Return], +) -> Callable[Params, Return]: + """Декоратор для вычисления времени выполнения функции.""" + + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> Return: + """Функция-обертка.""" + start_time = time.time() + + result = func(*args, **kwargs) + + end_time = time.time() + + print( + f"{func.__name__} executed in {end_time - start_time:.4f} seconds" + ) + + return result + + return wrapper + + +# %% +@timer +def delayed_function(delay: int) -> str: + """Функция с задержкой выполнения.""" + time.sleep(delay) + return "execution completed" + + +delayed_function(2) + + +# ### Типы методов + +# Методы экземпляра + + +# %% +class CatClass: + """Класс кот.""" + + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + self.type_ = "cat" + + def info(self) -> None: + """Вывод информации о коте.""" + print(self.color, self.type_, sep=", ") + + +# %% +cat = CatClass(color="black") +cat.info() + + +# CatClass.info() + +# CatClass.color + +# Методы класса + + +# %% +class CatClass1: + """Класс кот.""" + + species = "кошка" # переменная класса доступна всем экземплярам + + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + + def info(self) -> None: + """Вывод информации о коте.""" + print(self.color) + + @classmethod + def get_species(cls) -> None: + """Вывод значения аттрибута класса species.""" + print(cls.species) + # нет доступа к переменным color и type_ + + +# %% +print(CatClass1.species) + +# %% +CatClass1.get_species() + + +# Статические методы + + +# %% +class CatClass2: + """Класс кот.""" + + species = "кошка" + + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + self.type_ = "cat" + + def info(self) -> None: + """Вывод информации о коте.""" + print(self.color, self.type_) + + @classmethod + def get_species(cls) -> None: + """Вывод значения аттрибута класса species.""" + print(cls.species) + # нет доступа к переменным color и type_ + + @staticmethod + def convert_to_pounds(kg: int) -> None: + """Конвертирует килограммы в фунты.""" + print(f"{kg} kg is approximately {kg * 2.205} pounds") + # нет доступа к переменным species, color и type_ + + +# %% +CatClass2.convert_to_pounds(4) + +# %% +cat2 = CatClass2("gray") +cat2.convert_to_pounds(5) + + +# ### Декорирование класса + +# Декорирование методов + + +# %% +class CatClass3: + """Класс кот.""" + + @logging + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + self.type_ = "cat" + + @timer + def info(self) -> None: + """Вывод информации о коте.""" + time.sleep(2) + print(self.color, self.type_, sep=", ") + + +# %% +cat3 = CatClass3("black") + +# %% +cat3.info() + + +# Декорирование всего класса + + +# %% +@timer +class CatClass4: + """Класс кот.""" + + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + self.type_ = "cat" + + def info(self) -> None: + """Вывод информации о коте.""" + time.sleep(2) + print(self.color, self.type_, sep=", ") + + +# %% +cat4 = CatClass4("gray") + +# %% +cat4.info() + +# %% +setattr(cat4, "weight", 5) + +# %% +print( + cat4.weight, # type: ignore # pylint: disable=E1101 + getattr(cat4, "weight"), +) + +# %% +# TypeVar - это переменная типа, которая позволяет писать обобщённые (generic) +# функции и классы, сохраняя связь между входными и выходными типами. +Class = TypeVar("Class", bound=type) + + +def add_attribute( + attribute_name: str, attribute_value: str +) -> Callable[[Class], Class]: + """Декоратор класса, добавляющий указанный атрибут к классу.""" + + def wrapper(cls: Class) -> Class: + """Функция-обертка.""" + setattr(cls, attribute_name, attribute_value) + return cls + + return wrapper + + +# %% +@add_attribute("species", "кошка") +class CatClass5: + """Класс кот.""" + + def __init__(self, color: str) -> None: + """Инициализация кота с цветом.""" + self.color = color + self.type_ = "cat" + + +# %% +print(CatClass5.species) # type: ignore # pylint: disable=E1101 + + +# ### Несколько декораторов + + +# %% +@logging +@timer +def delayed_function_1(delay: int) -> str: + """Функция с задержкой выполнения.""" + time.sleep(delay) + return "execution completed" + + +# %% +delayed_function_1(2) + +# не забудем заново объявить функцию без декораторов + + +def delayed_function_3(delay: int) -> str: + """Функция с задержкой выполнения.""" + time.sleep(delay) + return "execution completed" + + +# %% +delayed_function_4 = logging(timer(delayed_function)) +delayed_function_4(2) + + +# ### Декораторы с аргументами + + +# %% +def repeat( + n_times: int, +) -> Callable[[Callable[Params, Return]], Callable[Params, None]]: + """Декоратор, вызывающий функцию указанное количество раз.""" + + def inner_decorator( + func: Callable[Params, Return], + ) -> Callable[Params, None]: + """Внутренний декоратор.""" + + @functools.wraps(func) + def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> None: + """Функция-обертка.""" + for _ in range(n_times): + func(*args, **kwargs) + + return wrapper + + return inner_decorator + + +# %% +@repeat(n_times=3) +def say_hello_3(name: str) -> None: + """Функция-обертка.""" + print(f"Привет, {name}!") + + +# %% +say_hello_3("Алексей") diff --git a/Python/oop.ipynb b/Python/oop.ipynb new file mode 100644 index 00000000..b21d1c7f --- /dev/null +++ b/Python/oop.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fd9c2db3", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "ООП Молчанов.\n", + "\n", + "Код из первых шести видео-лекций.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2090da2f", + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "response = requests.get(\"https://www.google.ru\", timeout=5000)\n", + "\n", + "print(type(response))\n", + "\n", + "dir(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cacc0c6", + "metadata": {}, + "outputs": [], + "source": [ + "class Person:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " name = \"Ivan\"\n", + "\n", + "\n", + "print(Person.name) # Ivan\n", + "\n", + "print(Person.__name__) # Person\n", + "\n", + "print(Person.__class__) # Person\n", + "\n", + "person = Person()\n", + "\n", + "print(person.__class__) # \n", + "print(person.__class__.__name__) # Person\n", + "print(type(person)) # \n", + "\n", + "print(person.__class__ == type(person) == Person) # True\n", + "\n", + "new_person = Person()\n", + "\n", + "print(id(person)) # 135917969519696\n", + "print(id(new_person)) # 135917969518800\n", + "print(new_person.__class__ == person.__class__) # True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ff9ed09", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(Person.__dict__))\n", + "print(Person.__dict__)\n", + "\n", + "# Person.__dict__['name'] = 'some-new-name' ---> TypeError\n", + "\n", + "print(Person.name) # Ivan\n", + "\n", + "print(getattr(Person, \"name\")) # Ivan\n", + "\n", + "setattr(Person, \"dob\", \"123\")\n", + "\n", + "print(Person.dob) # type: ignore\n", + "# 123\n", + "\n", + "delattr(Person, \"dob\")\n", + "\n", + "print(Person.__dict__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c1cbc9b", + "metadata": {}, + "outputs": [], + "source": [ + "class Person1:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " name = \"Ivan\"\n", + "\n", + " def hello() -> None: # type: ignore # pylint: disable=E0211\n", + " \"\"\"Приветствует.\"\"\"\n", + " print(\"Hello\")\n", + "\n", + "\n", + "Person1.hello()\n", + "\n", + "print(Person1.__dict__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53348a08", + "metadata": {}, + "outputs": [], + "source": [ + "class Person2:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " name = \"Ivan\"\n", + "\n", + "\n", + "print(Person.__dict__)\n", + "\n", + "person2_1 = Person2()\n", + "person2_2 = Person2()\n", + "\n", + "print(id(person2_1))\n", + "print(id(person2_2))\n", + "\n", + "print(person2_1.name)\n", + "print(person2_2.name)\n", + "\n", + "print(id(person2_1.name))\n", + "print(id(person2_2.name))\n", + "\n", + "person2_1.name = \"Oleg\"\n", + "person2_2.name = \"Dima\"\n", + "person2_2.age = 123 # type: ignore # pylint: disable=W0201\n", + "\n", + "print(person2_2.__dict__)\n", + "\n", + "# print(person2_1.age) # AttributeError 'Person2' object has no attribute 'age'\n", + "\n", + "Person2.name = \"Some Name\"\n", + "\n", + "person2_3 = Person2()\n", + "\n", + "print(person2_1.name) # Oleg\n", + "print(person2_2.name) # Dima\n", + "print(person2_3.name) # Some Name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53764e5b", + "metadata": {}, + "outputs": [], + "source": [ + "class Person3:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " def hello() -> None: # type: ignore # pylint: disable=E0211\n", + " \"\"\"Приветствует.\"\"\"\n", + " print(\"Hello\")\n", + "\n", + "\n", + "print(Person3.hello) # \n", + "\n", + "person3 = Person3()\n", + "\n", + "print(person3.hello) # type: ignore\n", + "# Вывод:\n", + "# >\n", + "\n", + "print(hex(id(person3))) # 0x7b9dde555c90\n", + "\n", + "Person3.hello() # Hello\n", + "\n", + "# person3.hello()\n", + "# Вывод:\n", + "# TypeError: Person3.hello() takes 0 positional arguments but 1 was given\n", + "\n", + "print(type(Person3.hello)) # \n", + "print(type(person3.hello)) # type: ignore\n", + "# \n", + "\n", + "print(id(Person3.hello)) # 135917970309984\n", + "print(id(person3.hello)) # type: ignore\n", + "# 135917970326656\n", + "\n", + "print(person3.__dict__) # {}\n", + "\n", + "# ВАЖНО!!!\n", + "# Под капотом вызов метода person3.hello() происходит следующим образом\n", + "# Person3.hello(person3)\n", + "\n", + "print(person3.hello.__self__) # type: ignore\n", + "# 0x7b9dde555c50\n", + "print(hex(id(person3))) # 0x7b9dde555c50\n", + "\n", + "print(person3.hello.__func__) # type: ignore\n", + "# \n", + "\n", + "# Учтем передачу экземпляра класса в метод класса при вызове\n", + "\n", + "\n", + "class Person4:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " def hello(self) -> None:\n", + " \"\"\"Приветствует.\"\"\"\n", + " print(\"Hello\", self)\n", + "\n", + "\n", + "person4 = Person4()\n", + "\n", + "person4.hello() # Hello <__main__.Person4 object at 0x7b9dde4aafd0>" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4be136f", + "metadata": {}, + "outputs": [], + "source": [ + "class Person5:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " def create(self) -> None:\n", + " \"\"\"Создает Личность.\"\"\"\n", + " self.name = \"Ivan\" # pylint: disable=W0201\n", + "\n", + " def display(self) -> None:\n", + " \"\"\"Выводит имя в консоль.\"\"\"\n", + " print(self.name)\n", + "\n", + "\n", + "person5 = Person5()\n", + "\n", + "# person5.display() # AttributeError: 'Person5' object has no attribute 'name'\n", + "\n", + "person5.create()\n", + "person5.display() # Ivan\n", + "\n", + "\n", + "class Person6:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " def __init__(self, name: str) -> None:\n", + " \"\"\"Инициализирует Личность.\"\"\"\n", + " self.name = name\n", + "\n", + " def display(self) -> None:\n", + " \"\"\"Выводит имя в консоль.\"\"\"\n", + " print(self.name)\n", + "\n", + "\n", + "person6 = Person6(\"Ivan\")\n", + "\n", + "print(person6.name) # 'Ivan'\n", + "print(person6.__dict__) # {'name': 'Ivan'}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc4fe42c", + "metadata": {}, + "outputs": [], + "source": [ + "class Person7:\n", + " \"\"\"Класс Личность.\"\"\"\n", + "\n", + " def hello(self) -> None:\n", + " \"\"\"Приветствует.\"\"\"\n", + " print(\"Hello\")\n", + "\n", + " # Не принимает self\n", + " @staticmethod\n", + " def goodbye() -> None:\n", + " \"\"\"Прощается.\"\"\"\n", + " print(\"Goodbye\")\n", + "\n", + "\n", + "person7_1 = Person7()\n", + "person7_1.hello() # Hello\n", + "person7_1.goodbye() # Goodbye\n", + "\n", + "person7_2 = Person7()\n", + "\n", + "print(id(person7_1)) # 135917970333264\n", + "print(id(person7_2)) # 135917970333200\n", + "print(id(person7_1.goodbye)) # 135917970311424\n", + "print(id(person7_2.goodbye)) # 135917970311424\n", + "\n", + "Person7.goodbye() # Goodbye" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/oop.py b/Python/oop.py new file mode 100644 index 00000000..fd61632f --- /dev/null +++ b/Python/oop.py @@ -0,0 +1,245 @@ +# %% +"""ООП Молчанов. + +Код из первых шести видео-лекций. +""" + +# %% +import requests + +response = requests.get("https://www.google.ru", timeout=5000) + +print(type(response)) + +dir(response) + + +# %% +class Person: + """Класс Личность.""" + + name = "Ivan" + + +print(Person.name) # Ivan + +print(Person.__name__) # Person + +print(Person.__class__) # Person + +person = Person() + +print(person.__class__) # +print(person.__class__.__name__) # Person +print(type(person)) # + +print(person.__class__ == type(person) == Person) # True + +new_person = Person() + +print(id(person)) # 135917969519696 +print(id(new_person)) # 135917969518800 +print(new_person.__class__ == person.__class__) # True + +# %% +print(type(Person.__dict__)) +print(Person.__dict__) + +# Person.__dict__['name'] = 'some-new-name' ---> TypeError + +print(Person.name) # Ivan + +print(getattr(Person, "name")) # Ivan + +setattr(Person, "dob", "123") + +print(Person.dob) # type: ignore +# 123 + +delattr(Person, "dob") + +print(Person.__dict__) + + +# %% +class Person1: + """Класс Личность.""" + + name = "Ivan" + + def hello() -> None: # type: ignore # pylint: disable=E0211 + """Приветствует.""" + print("Hello") + + +Person1.hello() + +print(Person1.__dict__) + + +# %% +class Person2: + """Класс Личность.""" + + name = "Ivan" + + +print(Person.__dict__) + +person2_1 = Person2() +person2_2 = Person2() + +print(id(person2_1)) +print(id(person2_2)) + +print(person2_1.name) +print(person2_2.name) + +print(id(person2_1.name)) +print(id(person2_2.name)) + +person2_1.name = "Oleg" +person2_2.name = "Dima" +person2_2.age = 123 # type: ignore # pylint: disable=W0201 + +print(person2_2.__dict__) + +# print(person2_1.age) # AttributeError 'Person2' object has no attribute 'age' + +Person2.name = "Some Name" + +person2_3 = Person2() + +print(person2_1.name) # Oleg +print(person2_2.name) # Dima +print(person2_3.name) # Some Name + + +# %% +class Person3: + """Класс Личность.""" + + def hello() -> None: # type: ignore # pylint: disable=E0211 + """Приветствует.""" + print("Hello") + + +print(Person3.hello) # + +person3 = Person3() + +print(person3.hello) # type: ignore +# Вывод: +# > + +print(hex(id(person3))) # 0x7b9dde555c90 + +Person3.hello() # Hello + +# person3.hello() +# Вывод: +# TypeError: Person3.hello() takes 0 positional arguments but 1 was given + +print(type(Person3.hello)) # +print(type(person3.hello)) # type: ignore +# + +print(id(Person3.hello)) # 135917970309984 +print(id(person3.hello)) # type: ignore +# 135917970326656 + +print(person3.__dict__) # {} + +# ВАЖНО!!! +# Под капотом вызов метода person3.hello() происходит следующим образом +# Person3.hello(person3) + +print(person3.hello.__self__) # type: ignore +# 0x7b9dde555c50 +print(hex(id(person3))) # 0x7b9dde555c50 + +print(person3.hello.__func__) # type: ignore +# + +# Учтем передачу экземпляра класса в метод класса при вызове + + +class Person4: + """Класс Личность.""" + + def hello(self) -> None: + """Приветствует.""" + print("Hello", self) + + +person4 = Person4() + +person4.hello() # Hello <__main__.Person4 object at 0x7b9dde4aafd0> + + +# %% +class Person5: + """Класс Личность.""" + + def create(self) -> None: + """Создает Личность.""" + self.name = "Ivan" # pylint: disable=W0201 + + def display(self) -> None: + """Выводит имя в консоль.""" + print(self.name) + + +person5 = Person5() + +# person5.display() # AttributeError: 'Person5' object has no attribute 'name' + +person5.create() +person5.display() # Ivan + + +class Person6: + """Класс Личность.""" + + def __init__(self, name: str) -> None: + """Инициализирует Личность.""" + self.name = name + + def display(self) -> None: + """Выводит имя в консоль.""" + print(self.name) + + +person6 = Person6("Ivan") + +print(person6.name) # 'Ivan' +print(person6.__dict__) # {'name': 'Ivan'} + + +# %% +class Person7: + """Класс Личность.""" + + def hello(self) -> None: + """Приветствует.""" + print("Hello") + + # Не принимает self + @staticmethod + def goodbye() -> None: + """Прощается.""" + print("Goodbye") + + +person7_1 = Person7() +person7_1.hello() # Hello +person7_1.goodbye() # Goodbye + +person7_2 = Person7() + +print(id(person7_1)) # 135917970333264 +print(id(person7_2)) # 135917970333200 +print(id(person7_1.goodbye)) # 135917970311424 +print(id(person7_2.goodbye)) # 135917970311424 + +Person7.goodbye() # Goodbye diff --git a/Python/the-algorithm-design-manual/chapter_2.ipynb b/Python/the-algorithm-design-manual/chapter_2.ipynb new file mode 100644 index 00000000..990d2e84 --- /dev/null +++ b/Python/the-algorithm-design-manual/chapter_2.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c4af1fc7", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Анализ алгоритмов.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "abd3cad9", + "metadata": {}, + "source": [ + "# Модель вычислений RAM" + ] + }, + { + "cell_type": "markdown", + "id": "7e05d698", + "metadata": {}, + "source": [ + "Разработка машинно-независимых алгоритмов основывается на гипотетическом компьютере, называемом машиной с произвольным доступом к памяти (Random Access Machine), или RАМ-машиной. Согласно этой модели наш компьютер работает таким образом:\n", + "* для исполнения любой простой операции ( +, *, -, =, if, call) требуется ровно одинвременной шаг;\n", + "* циклы и подпрограммы не считаются простыми операциями, а состоят из нескольких простых операций.\n", + "* каждое обращение к памяти занимает один временной шаг. " + ] + }, + { + "cell_type": "markdown", + "id": "25b17621", + "metadata": {}, + "source": [ + "Алгоритмы можно изучать и анализировать, не прибегая к использованию конкретного языка программирования или компьютерной платформы." + ] + }, + { + "cell_type": "markdown", + "id": "9c063f34", + "metadata": {}, + "source": [ + "Чтобы понять, что означает наилучший, наихудший и средний случай сложности алгоритма (т. е. время его исполнения в соответствующем случае), нужно рассмотреть исполнение алгоритма на всех возможных экземплярах входных данных.\n", + "\n", + "* сложность алгоритма в наихудшем случае - это функция, определяемая максимальным количеством шагов, требуемых для обработки любого входного экземпляра размером n. Этот случай отображается кривой, проходящей через самую высшую точку каждого столбца;\n", + "* сложность алгоритма в наилучшем случае - это функция, определяемая минимальным количеством шагов, требуемых для обработки любого входного экземпляра размером n. Этот случай отображается кривой, проходящей через самую низшую точку каждого столбца;\n", + "* сложность алгоритма в среднем случае, или его ожидаемое время, - это функция, определяемая средним количеством шагов, требуемых для обработки всех экземпляров размером n.\n", + "\n", + "На практике обычно наиболее важной является оценка сложности алгоритма в наихудшем случае." + ] + }, + { + "cell_type": "markdown", + "id": "2b39e7a3", + "metadata": {}, + "source": [ + "# Асимптотические («Big Oh») обозначения" + ] + }, + { + "cell_type": "markdown", + "id": "a64bf530", + "metadata": {}, + "source": [ + "Намного легче работать с верхней и нижней границами функций временной сложности, используя для этого асимптотические («Big Oh») обозначения. Асимптотические обозначения позволяют упростить анализ, поскольку игнорируют детали, которые не влияют на сравнение эффективности алгоритмов.\n", + "\n", + "* f(n) = О(g(n)) означает, что функция f(n) ограничена сверху функцией с • g(n)\n", + "* f(n) = Ω(g(n)) означает, что функция f(n) ограничена снизу функцией с • g(n)\n", + "* f(n) = Θ(g(n)) означает, что функция f(n) ограничена сверху функцией c1 • g(n), а снизу функцией c2 • g(n) для всех n >= n0\n", + "\n", + "Анализ наихудшего случая и асимптотические обозначения являются инструментами, которые существенно упрощают задачу сравнения эффективности алгоритмов. " + ] + }, + { + "cell_type": "markdown", + "id": "fc3ebd38", + "metadata": {}, + "source": [ + "# Скорость роста" + ] + }, + { + "cell_type": "markdown", + "id": "6585fc18", + "metadata": {}, + "source": [ + "* время исполнения всех этих алгоритмов примерно одинаково для значений n = 1О;\n", + "* любой алгоритм с временем исполнения n! становится бесполезным для значений n >= 20;\n", + "* диапазон алгоритмов с временем исполнения 2^n несколько шире, но они также становятся непрактичными для значений n > 40;\n", + "* алгоритмы с квадратичным временем исполнения n^2 применяются при n <= 1О ООО, после чего их производительность начинает резко ухудшаться. Эти алгоритмы, скорее всего, будут бесполезны для значений n > 1 ООО ООО;\n", + "* алгоритмы с линейным и логарифмическим временем исполнения остаются полезными при обработке миллиарда элементов;\n", + "* алгоритм O(log n) без труда обрабатывает любое вообразимое количество элементов.\n" + ] + }, + { + "cell_type": "markdown", + "id": "d0389872", + "metadata": {}, + "source": [ + "# Отношения доминирования" + ] + }, + { + "cell_type": "markdown", + "id": "4d776bec", + "metadata": {}, + "source": [ + "Функция с более быстрым темпом роста доминирует над менее быстро растущей функцией.\n", + "\n", + "* **Функции-константы f(n) = 1**\n", + "\n", + "\tТакие функции могут измерять трудоемкость сложения двух чисел, распечатывания текста государственного гимна или рост таких функций, как f(n) = min(n, 100). По большому счету зависимость от параметра n отсутствует.\n", + "\n", + "* **Логарифмические функции f(n) = log n**\n", + "\n", + "\tЛогарифмическая временная сложность проявляется в таких алгоритмах, как двоичный поиск. С увеличением n эти функции возрастают довольно медленно, но быстрее, чем функции-константы (которые вообще не возрастают).\n", + "\n", + "* **Линейные функции f(n) = n**\n", + "\n", + "\tТакие функции измеряют трудоемкость просмотра каждого элемента в массиве элементов один раз (или два раза, или десять раз) например, для определения наибольшего или наименьшего элемента или для вычисления среднего значения.\n", + "\n", + "* **Суперлинейные функции f(n) = n * log n**\n", + "\n", + "\tЭтот важный класс функций возникает в таких алгоритмах, как quicksort и mergesort. Эти функции возрастают лишь немного быстрее, чем линейные, но достаточно быстро, чтобы подняться к более высокому классу доминирования.\n", + "\n", + "* **Квадратичные функции f(n) = n^2**\n", + "\n", + "\tЭти функции измеряют трудоемкость просмотра большинства или всех пар элементов в универсальном множестве из n элементов. Они возникают в таких алгоритмах, как сортировка вставками или сортировка методом выбора.\n", + "\n", + "* **Кубические функции f(n) = n^3**\n", + "\n", + "\tЭти функции перечисляют все триады элементов в универсальном множестве из n элементов. Они также возникают в определенных алгоритмах динамического программирования.\n", + "\n", + "* **Показательные функции f(n) = с^n**, константа с > 1\n", + "\n", + "\tЭти функции возникают при перечислении всех подмножеств множества из n элементов. Экспоненциальные алгоритмы быстро становятся бесполезными с увеличением количества элементов n.\n", + "\n", + "* **Факториальные функции f(n) = n!**\n", + "\n", + "\tФакториальные функции определяют все перестановки n элементов.\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/the-algorithm-design-manual/chapter_2.py b/Python/the-algorithm-design-manual/chapter_2.py new file mode 100644 index 00000000..b9f8f04c --- /dev/null +++ b/Python/the-algorithm-design-manual/chapter_2.py @@ -0,0 +1,75 @@ +"""Анализ алгоритмов.""" + +# # Модель вычислений RAM + +# Разработка машинно-независимых алгоритмов основывается на гипотетическом компьютере, называемом машиной с произвольным доступом к памяти (Random Access Machine), или RАМ-машиной. Согласно этой модели наш компьютер работает таким образом: +# * для исполнения любой простой операции ( +, *, -, =, if, call) требуется ровно одинвременной шаг; +# * циклы и подпрограммы не считаются простыми операциями, а состоят из нескольких простых операций. +# * каждое обращение к памяти занимает один временной шаг. + +# Алгоритмы можно изучать и анализировать, не прибегая к использованию конкретного языка программирования или компьютерной платформы. + +# Чтобы понять, что означает наилучший, наихудший и средний случай сложности алгоритма (т. е. время его исполнения в соответствующем случае), нужно рассмотреть исполнение алгоритма на всех возможных экземплярах входных данных. +# +# * сложность алгоритма в наихудшем случае - это функция, определяемая максимальным количеством шагов, требуемых для обработки любого входного экземпляра размером n. Этот случай отображается кривой, проходящей через самую высшую точку каждого столбца; +# * сложность алгоритма в наилучшем случае - это функция, определяемая минимальным количеством шагов, требуемых для обработки любого входного экземпляра размером n. Этот случай отображается кривой, проходящей через самую низшую точку каждого столбца; +# * сложность алгоритма в среднем случае, или его ожидаемое время, - это функция, определяемая средним количеством шагов, требуемых для обработки всех экземпляров размером n. +# +# На практике обычно наиболее важной является оценка сложности алгоритма в наихудшем случае. + +# # Асимптотические («Big Oh») обозначения + +# Намного легче работать с верхней и нижней границами функций временной сложности, используя для этого асимптотические («Big Oh») обозначения. Асимптотические обозначения позволяют упростить анализ, поскольку игнорируют детали, которые не влияют на сравнение эффективности алгоритмов. +# +# * f(n) = О(g(n)) означает, что функция f(n) ограничена сверху функцией с • g(n) +# * f(n) = Ω(g(n)) означает, что функция f(n) ограничена снизу функцией с • g(n) +# * f(n) = Θ(g(n)) означает, что функция f(n) ограничена сверху функцией c1 • g(n), а снизу функцией c2 • g(n) для всех n >= n0 +# +# Анализ наихудшего случая и асимптотические обозначения являются инструментами, которые существенно упрощают задачу сравнения эффективности алгоритмов. + +# # Скорость роста + +# * время исполнения всех этих алгоритмов примерно одинаково для значений n = 1О; +# * любой алгоритм с временем исполнения n! становится бесполезным для значений n >= 20; +# * диапазон алгоритмов с временем исполнения 2^n несколько шире, но они также становятся непрактичными для значений n > 40; +# * алгоритмы с квадратичным временем исполнения n^2 применяются при n <= 1О ООО, после чего их производительность начинает резко ухудшаться. Эти алгоритмы, скорее всего, будут бесполезны для значений n > 1 ООО ООО; +# * алгоритмы с линейным и логарифмическим временем исполнения остаются полезными при обработке миллиарда элементов; +# * алгоритм O(log n) без труда обрабатывает любое вообразимое количество элементов. +# + +# # Отношения доминирования + +# Функция с более быстрым темпом роста доминирует над менее быстро растущей функцией. +# +# * **Функции-константы f(n) = 1** +# +# Такие функции могут измерять трудоемкость сложения двух чисел, распечатывания текста государственного гимна или рост таких функций, как f(n) = min(n, 100). По большому счету зависимость от параметра n отсутствует. +# +# * **Логарифмические функции f(n) = log n** +# +# Логарифмическая временная сложность проявляется в таких алгоритмах, как двоичный поиск. С увеличением n эти функции возрастают довольно медленно, но быстрее, чем функции-константы (которые вообще не возрастают). +# +# * **Линейные функции f(n) = n** +# +# Такие функции измеряют трудоемкость просмотра каждого элемента в массиве элементов один раз (или два раза, или десять раз) например, для определения наибольшего или наименьшего элемента или для вычисления среднего значения. +# +# * **Суперлинейные функции f(n) = n * log n** +# +# Этот важный класс функций возникает в таких алгоритмах, как quicksort и mergesort. Эти функции возрастают лишь немного быстрее, чем линейные, но достаточно быстро, чтобы подняться к более высокому классу доминирования. +# +# * **Квадратичные функции f(n) = n^2** +# +# Эти функции измеряют трудоемкость просмотра большинства или всех пар элементов в универсальном множестве из n элементов. Они возникают в таких алгоритмах, как сортировка вставками или сортировка методом выбора. +# +# * **Кубические функции f(n) = n^3** +# +# Эти функции перечисляют все триады элементов в универсальном множестве из n элементов. Они также возникают в определенных алгоритмах динамического программирования. +# +# * **Показательные функции f(n) = с^n**, константа с > 1 +# +# Эти функции возникают при перечислении всех подмножеств множества из n элементов. Экспоненциальные алгоритмы быстро становятся бесполезными с увеличением количества элементов n. +# +# * **Факториальные функции f(n) = n!** +# +# Факториальные функции определяют все перестановки n элементов. +# diff --git a/Python/venv.ipynb b/Python/venv.ipynb new file mode 100644 index 00000000..de85ef3e --- /dev/null +++ b/Python/venv.ipynb @@ -0,0 +1,123 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f32faa3f", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Issue #7 Виртуальное окружение.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/4\n", + "\"\"\"" + ] + }, + { + "attachments": { + "image-2.png": { + "image/png": "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" + }, + "image-3.png": { + "image/png": "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" + }, + "image-4.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlMAAAH2CAIAAADbGp5/AAAAAXNSR0IArs4c6QAAIABJREFUeAHtfV1u5Dys7FnZYObh4NztJWtJVpFkCeklBP38IQ8XJEWKlCi37W73nwsYDBxZosgixZLUtvU///e//+///vf//f3zD/+AABAAAkAACOwBgf8B8+3BzbARCAABIAAEDAEwHxa7QAAIAAEgsC8EwHz78rdNeXABBIAAENgtAmC+wnyvnz+/v//Rv8PbzqLh5ev43/Hz5SJWvx/WiXo7/P73e/x4nf1784yOFsu8CAKPLmTHA+HMSTCNo8PbmULQ/EoI3BfzzUhnU7ic2fzvn3807MF8s+mnzfIvH8ff7/c1zRey1KyOFsps1ebmPBkK6Yy6XkbSLUptR0lInxnJZzZfPxAEHJlByv9lNlPB/D0dIfMdN79mAnLvl/cDz317p/9pO8pqns98FSWbidaJiNMqLcxU+vfXeSSEcRKEpP9+Zv9gvjAkwHx9Ophf8vr5YyN2fqsVNTfviPNFlik4ux2+Fy1PVxh4JnWd2Xw988V8SrmY5pGBErQwjLsK0dv37+/31+fPaYTn14xa1b4myikAfr5eWMnpjmrNYOasXoIC1FyHD1GghF8a6mlh7TGo5K3Q69BvcYTzC/WumgzclEmoCjzC3SswH3m0TCXqdM8V8hqrmcXormMIJqWll6/jz9eLzY9okTFo/u+vTNbC9HNUSD7WLszfTs+qvN19uIsEOklMGuiCqqzbDGFxnwybCUDeDj5ZHD7Y7z9fb36R5Jrr3mb1XV1tp3oa2rWjLsWXgFku04TTxfsh37YisceP17eG+RioqryI6gud7TWWXOGzDQRzk10QMt0Q88i/HTgqCs5TCXR+zSyWmMzq5kTzJ/VrOp/syGpK7Nmosc0P52KNeRl0JStKYdTBUEpJLi10xGMqRWyVTV1NrVDJUn1UVdU6U+54vDqbM5+50MPt5hcUFpp2KePYNdeXYCqwqiiJpDJ/caLy5iqQW1Fy8T1aYdNF/qc34TGvU+gMEBq0CheBWTZ+aVTUGehoN1i9w9DRMKYmJISyfBmKr5/fZR4dvEBNQnO++1t41NRLnBJb/WuWF/Gut72VGb3J2n6SCfxPU5hlh1XMF5UptlSQIyBPMBC8vRoGZfMtW0wXQMQR1HZe5p1R0/vdoppC3dRwXlA1WhdzfKYq1Zq+oxpgacwnhVUO66B/piSXFtYY1ra1pHB5NTne+vfXN+HBO2NTWrF6TEa8CvPVGa6A9XbwJQ70+QPeotb7rG0ep1G85f1N9X3vlM40tbX5l8Pd3W3D5fFcTgOygU6nAh+e9mStrDX9MP4J6FUEEsmvvHhiKk0moT4zpsynvSekaLfIm7xUUteHjmIXQcN4K45hZnojeEqLnPK0i5gmKgJRSFdOPbax9MQDITiCBk7Jp1Mbbja+CKuUZlJUT9QMfrd04QLAq0qVZcZTY4w77VTqa4aOnPwaGJOFnox54ch2UZMyA9OZqKSprtCvIxvlZRtjClJJwhL5FKUek6q/OegJLjZnvgI6+6msJwq+tgVanxeoyaWEeBpModAzWdvccSq5iknOQr84jwtt36MPTZ6ukqq2GHpkryfQuQHjsxLVdGu+eisHpIFa/1SP6EBqXO92CCPyQc9wSyVX91GCeDscvg8kLXBJaMiLKssI9VZQiedAMSSSsGl06NLxKEJa6ELXPBw0iStulnRSQELhDQZC0L9OH8s8Rm0JaZf57+RQIu+45iM8+478Ax06zxigZF7WyVPshVp5PccqWc3Q0SDA/JhyCVAGQpkc8DZJohXJtwmZajssbJVvZ10WWnxRulYnGjizY1v1iWLvuPk1mE9B0c20MawrB7wOkrY5uVN9WWad3Zov1qnx2vpMlW/LH8bT7IUwOGUSoGu+F7LdwWVpesD6AZAEeXaKlhPzyTrSBmQDdfwz6OlvqUCDnanu7fv4+fJ++H5/+ThqMCxZR5o0uVCeFl9LuLqn/mwObjyqEd7IGf2p0D3tQCADKzhkZp05eYYe4UYed34cVUuYLxmeIZbcxLeUT/2m28zMBippfIaOtFB9zYpNFoZoSfcztXlbs6PDsE1CrTz+CUSyKog+Ghg74YvHunVN5qPI4MRHF723sji2JrpbQrHoI6wLrOAwf9dEpYUlmNLYYo9a8xB2j+XsAXTVNGI7AXCclNXk2kpWRbZupgq6KlKiqsxXEiLJDzEQkSfhljrrrUQrYr4v/vnw9fPncCibn6JkbUijfSAzSwSEg2Y9f11sV+sUCoooqz9ZKMFj0NFF15DqkOYhkq3JAwyEVnn2tXmzxljZhtGQc45oJSyq6eSwL7zf/diXKP3xUyX1XXkIznTOPGJ5gGRyTd+RcY/d1RcMKK7SQhPYe59vRRiLqmmhk09InqQ9HR0aci7YWjCrkg6rhyzcmvkIRJsju0Eeym0dIOmp1JfUw66lkuPHe3nZbtS2ZLfQnIMglsRnO0uCCzKpPpXHwlLzId2sYRosUtip0F/LUKEMZb8llM3e0Ny8SaOrAUe5wTPf1wsP6SKTn1zvQXbIW94x+Xah5qjHK1u7qZVT3rzZy3SivGedpY1pjte1LSWyFgEJvNrWCQyVQ7l6Qe0SE0TIowyENBfXfbzIc2JU5fiABoWf3Zpfs03WQaZDuOQBVxJqaqiEQlGpyQy+pl4b86Uxnxa6jvKwsTWZq1lZzRfarJEj0w8Ew7NFKYScw8QPiqe63pr5tgCLfGwRptlni46eT+Zs6CjRuI1i+tMGXgNL3BtMRlRTf/WfV+totYZXbjjbmxs65comr+huAiVE1Ao8n6QJmO9JHDlvBjCRBSIO/c+fngh3nUkjULeEYrY3b6nkzeEaopTsY+8aqJt76qoKgPmuCvc8ftpOpWEW6BWLu51u/YfscC8ILPBm79/dlCQoldie2vrbbgzeVnK3/+l31O8lsK8B0SMy3zVw2U1eAJhAAAgAgd0hAObbnctB6kAACACBnSMA5gPzAQEgAASAwL4QAPPty987n+jBfCAABIDA3z//7oj5+KVLfoesvs4itMS/yu7x5+jrsDI9AnCpN3j0Bb6lmi928YyOFsu8k4yw44GQPI3SOGWG35fG3oL6t+29gQJ/noPAHTGfmJG9rZykMITgOV6PbS/HfM1bgAseFUtcHJWM6WlWRwtlttpyc37yLbw8Sl27N6zbVlHPM+5eYSD4x3eDjZ3alYwDIBQ55XMH7Ww14iCghbaxQunxfOarXps1mWu9WZv7L/lZKM5IO2dGXQoLCi+PwEMwX2L2jBBMWlkE48IhcDHmSz826Dq6mEc274gTYkYGnJq3P5m2fC5rmk6YLS4zEMje0ZcKyGsp4O59OEr3Y6aRE/LY+/Se6ERHZzKfj2RSKfOgD8LGm6F3Z11tcgJtfgt21rG63dxii2ECmRMIXIH5KJ70O1j2WpgvDGOmmerWyaZmgVoib6LUb0E5mVp5wvJd3spO6eSPtGnakjmvuEmuzXeSsBzI7bvt7osY9KV5nEzbfNIshc4X3nAgON9lSTlhvkiWNCpn/R4xTUiExuHNAm86XTjopOv+8wslD3Q12cais35pjz+nWVmZ7vbNpwj+5Bm2lUF3mXzuy/zNmc8FULXcz6eaaVRavy9sWpWTUFykaiqvnSLa9HuDZXirFygvMFyUcQw3vRtOE+0dYaiGW5SDaMZNQoggS2JNDuTUPBualy+mVq41rZrFUGxVvj1oM/14V9JfLtOs4AvW9ton0xJWIzNNvWgRxXbTauVAqNk/Hy8J8/km7O50e9A014vTzKeLQgtLO0422JvEklfJfV41qVk+gc3B4FppuIaYrwMhQ1vtqriRj2ZNAmqTXghKtkbgKszXLA5otmizuXbo9mO7SXaCSDfgw8FsdlbA1vA9mnzKJkYMgpLQ4fETJ9NqJuKtTp3vc9rlRFZDzuXK+QFAge3CnhrefiDIbICW9TUqdC7iTWPldfUv80sBQbBykxvfqr8mFplihRCfJRVMoiRdaNLwtMoLx64vrenSTuPNwuJldjjto97Akqy6ftOaKLwhApszX5mE8s5kmdJOhrKFpgelL6xpSAZqGYE6OK/yDILX8EGuQ2Yh5vv91oXgfzrXFgLgnCg5jrDVRMDTXtm79guUdqqh2UTdpJtpjZvKGp16jC4OeoZbKrkAXg7ke9yTaS89BWwQtoEQymunGrcEeHBoxn9cWQOjWeeR/F6sTiZYGvnR10lUyvweJWtE6aE/zU8eRat4uGvoiLegfRS560rMLIcAGfU+xIcjGcw3xkdDLsTG9QuvwXxqlW6mTQZTSHMKX19YB4DUiTK1xxuDe39qhMwS13w4mdaiRXnah1Y8s0m4f3qdNPb+PQ6EfoiN9C81acTV+VA79dGRa0Kola/fVeCaIT5dR5VTdeArhiwnVV53aJOavKvpZslMn0xy0aLfbzCfefDJLq7JfBTWPK+kWJSsweMhTDZHQWy7T+IAqhYmViS8qfNkrrqEOT6zWEYwv9SdvWbAZ13XVkllnUe7PFXOZC9sQXkz+Cv63evploPJ/OahT6a9n4FQNSFfi3fC+NJpAd9iJ5KPdAy6YMia8zB3pJLT3uhn2qoby2lzSNHW7R/ELcfaPK1ZObuaRsbK+s+fIut6VzQyQ6haCl1WORtZU8JR/1IIbM18PDyaHQn58VkKD2+aHGPNkhbTQokMd6sEvStxT2pcCqmnkJNCRIW60yUVKEk182KuEJrbPIOGesw7lk3UufaEy4895UsPf1OrKNP53VZUJt8unC+4uSQazlyJnpMynSifcZxWjWnu0QltS7m1RWDmybS2O3eDgeBs1JlosahlPl/TE1gt1/hhDNvmjI8lAduATZiABPZ+p3DSdKErOZ4PFZnZKcfOaxQ2bU3na52lke3WkVfSCg9v1ru63snpIxn8l7jYI3bL662Z77RtmhxP18yiDa0WIRAyyxSelLzqFhNPln3K853GvcENY/1qHXnrrneNgTAVkBvG1fVcDAPvBwEw367ifjbz0Ty3Mh/Pmuuf9xO+z6QJmO+ZvAlb7hwBMB+YL0cg7naC9nKULji8wXwXBHNLUd3mre7Ebtnp5uG3N+Vvz3x7Qxz2AgEgAASAwG0RAPNhMgUEgAAQAAL7QgDMty9/33aehd6BABAAAveAAJgPzAcEgAAQAAL7QuAmzEdPGIYXgPDI8i0RuKQ71j6mwU8NLHn/aUZHi2VefSp6SeRvory9e9f2bu/Anf5uy74SbgvULQf+rpG/BvN1SeqhB/zzhcvl3NG8BbhgVC9kqVkdLZTZasvN+bG9kN/b17QXxMN5A6Hq4184GWTSWjko39r4T75R4r854t77nv6SNYXNQDj13t1ilcLkpirp5sFpoeo57Q5xTXlpnT9OFrprPEX62wvyAxibJvjzeRAA8z2PL9eO3osx3+QXLi6J8+YdcQ7tcrd+XmvtybRnMF+gGXrhxH2jpPX7UPnOBfzWZnOS6mxsg0pBB1IgvgaTdOSjzpgyLQxnBoWO+LMph4E7plFyd6l3R70dSsl0AXUeHoFtma+ZP9IMSz9YpaEmU7w4ThBqWyGAk2ltSTEVcu+HfslCQ53i+fjx6r93RZ5imS0VhcKzB0L4fg1Ja7urmWikfOQMVpuXRMUoDbmFzJfhGT+DQPj0HcU6xaK0kD+hmc1CRu4QKAJire3EzfWbRA0CbWVFBuXPhMC2zCdIjae6NGyUAuvQfSZ878wWmlPbJ/N12msT7eAOvRtm3BM5N9yiFEbkQUJo+l/SUH5GKGeW0PwZT6bNTpHNkU9jRpEM7shqMtT9sbrj9N3k/UXMp7FUDCFRfrMxfs40dNTMHvhP+9xrsUsKJXh6i4y9GlFJOHW5xTfhWJ2xgdwJGUOa+QXN7wuBGzJfcxTqfeHypLFLGapOn0tmkbTVuINYUGtagpbPBKerpUTyaz3DOpmAR6pjyXUdE6TFmuEW5UpupbOr0NFEw3grxh7vFtqiiiiHlyzaBX/XeOoHpCjN5ccqoRQKsA3yw+b6PeW6WEmidKB8UlMVIyicOfSnsVf1SK9VcETAMy7drOvYkQ8wuv49fviDEewHyOaAhXnuCGFgCtQLYT7Bys3MagUFByXPisDNmI9H1+QYRvBdHoGQrTiT4mRaPi+p5HomdUqIjt35z7Ac8SuGJT5KmS8ZCCUjy/MXRRPL+MJ/w52STPn3SSUjIXmGo2gpM4BEpRBLy5nPH4zw/a4zGGV3d7RsZtG0O8YWqXXNOq/pYhKuZ2WCvdl1M+Y7frZHoe4N+lvYG7KVpA85J6h3B2+vlYffsjzbbo2GOsoNmutpDi6zeKsWcuXsM9lVoKYw2gqjvHn8fHk/fL+XI9rL3dhFsD3eMmlyEVcMkhYvcTJtpzyp1COfBAbp4KaJg0UVN8yUn0zlEzwxidIYz4F60x1ZYJj5uu+aWTTlDr+abDyrf/Z4ulWvKYCLJ0bgGszXRXwZ8Hb84+vkyHxi9K9ums9WRl2ZO07Pgmsr3o9yiyR3gp3m+sp8ZQeV5Ot6gr0fk6zX022EJlo90sm06wcCw6Wbz+4A4ewYWJqy6C6lvy7bhl2K77Ty9GA73lpYh+rAR+WUuxgP5uKu9/rcUJVMfXmtvBX+ugwfnWbJn77heHyR8rrH6yI56jBu3qOBkgdD4BrMF04fpTHpQ41D0M9nEXwbIiBoNyu53B2UYuz3nvIgUmhu6ZVyjabaJhl55vt6kZ8Jyybezk6mlbzgAFw6EMoGHaPn+UPmEL7EH5Hq/dLWdMqIo+mXNl/oVplJTFJNI+MQA+2az8ss+oeOqpKuZi0sL5OUaAzljKpnvjhFmKSu2le/3Jxs+GApHrakCFyH+RArd4JAyFZpQJRCyiBu2k5/jvJg3IxKUuSlbL9aR5dSGHKAABC4UwTAfHfqmClaWs8us5kvTttpRu+JcL0Cu0IbxgIBIHCnCID57tQxN2Y+fiHB7Xa69R9oDwg8DAL8voTbtB9umT6MRbvKVxsaC+bbENxt2AsKAwEgAASAwFkIgPnOgg/cBgSAABAAAg+HAJgPzAcEgAAQAAL7QgDMty9/P9zUDAoDASAABC6OwJ0yn74HBlq6AgL0wOelXmla6zh+EiG8lHbC8LUdnRA7HmCzH4u96LMSVzdzNT7ScD5K82ueVunRUDpt0TgO0fYyCFyD+VbE5ckmJysgdGYjcDnma94CXMABC5lvfUerh80FMvWKoD3Z5GSFqTAgGMv3Cuyd9Pwd86wmv+tSmsvRHNLWiZpAe4QnledPYIoO2fQogjAzlvxjn/7R5ZnNx3ouCPsJfHBrWwTAfNviO5V37mWE0Bi+yJpPP7S4OaRX68i5b5SpFxgbE/SshiebnKzgTGh6lMMauZBe3yxfKnCfByMO4MDIa2ZemI9SXjPrXdTm+uNDaEsA83uozVm7GQKh99rp3Ob8AbnyNRlDqYEXf941AtsyXzMrpKkchQuFnXtXzL6BFMo1F7tCnu4NZMZPHGUTw2wA3LVvNlD42U6m5Vj6+Xqx+btN3q3EPs+YR4gwhwVVXa9QEixRqtn/v8ObibWOzj2Z1nrRZVPUsx5guelAILvIdlpX1Y/1ECztUNKa/GlNHaQ2jkjJAUpWRy6ymuPeiyb+Q2UxjbAm2RG4NL900FW6imZS+ah5o/kclLom9zLNhWIFgW2ZT3L31LTUTieRV6dLXNKUSgbV6CxTq2D0MKppFXChKaCZ3VNeKImj5lk3q+VdJqEESkDqowbPcItp4won00aLzBC70KzHOqcRUj5PWipooif9a2ZkSyV7SqGXP4v5BKs+aCuGNx4IymeeWgr3e44vxlowpMxn0NW1VJL3MzxHvRsjugpeeANsQ9iuZnUc+51Nc+Et7miaVx+ZFU4NPVapQQkEc+8I3Jb5+IgZiSeKvxo9TSjXiNS0m1awGA1Z2OIVFzz5Dcsa/Ujx8bM5H1VTIYFW8wUB69xkgEudRvI1TqaNFhW/U8qusZQcJeEORaIMaMsaDkJv78jAdQE2DtobD4QKguR0JgPGsP1Waq0pGOqyWOdDFCohDLwjwgAMNavjjh+vXe8VN6OcyXRB0synco6VdW0SypkS/xlPm69j8yyDn0LJROHibhG4JfO9H9wgGYVyGQa6OzrBfIOadwv9LRQL6Yanqw9+Mu2I+Wrik72pOruvu4scSzWrWnKkXOkis5QH6C7LfNcYCGF0uGlBITAtadZ5cVSS1TmTETj2Q0ZlPmue9J7hmfWeH0Jrktk7jRMDdYWuOY1wbFQK50794jU0r1HhKDDTc/r431sMdqdwasW+C2/HfG57h8IiD2XKQRaUPt00sT596inCThEI6eYpTqYNFpUIocSkqVyPixtFSBdIZY1b03dJEFlHC3NH1hcfTa7zuesPBELM7+vSMHTbvG6F1NaMtuvYDCgF0or1m02C0jzrnSjKVpZ6MUgXJdeTNJv6xMRSBkLfkQuY0LxVm7vom1t3aX0U3h8C12C+JJKScKwTbapfCK8WcqzXIzc7mcOamvQxA2pyus0qKFvJ9KJOhBMHNQDWVk0aIsA1Y2qu3+5k2pBqKSqIRcw0v1WbR4hqGKzjCHTcSeM27UhakWTd7jM5SWEXtO2Ej2O16slq1J9gCxmTa2p3nczavBky/UBg+Y7n1EyljerirKZZKlbIKtmjVJv3XQ/wpCZ977W5xpW4WABxKBWVIiYs088t9Dhfm9zUsOcEHZs7M2v6PqVnrZk2R+HtEbgG80mUl4kbhSDHjU7fqFzisuwh0J/2wLSENbf91rNMBTUnhJuPa94e5Tp0bzwkHGh1MU2FurCWCpQNm7k2VwjNLdcT8k1m0QylvFJ+Lsp8FGW6p38tMZl8u3B4UvO+pmRGF3IUA1nv9VkqJ7OpLOCMOlrwhEuI/NsPBOZmPwzLwqV6RKMiqRlsqcvE2lYnr6PRN8KzStDenQSNK3JWki5q2+L6zqKqlTX/DVxbGgosUyu52lei542HuUMMmgwQuA7zwRN3gkBIN02uD382az76s1kcmEXtQxBBziDsVtW5WkdmGi6AABB4TgTAfM/p1wG1zGa++FMZr5aa3b9d4QZjgQAQeCoEwHxP5c4B4ZmNs5kPJ9NecrVq+OPisRDotnllF7TZ20eoPCACYL7HGorQFggAASAABM5FAMx3LoKnllmQDwSAABAAAveFAJjvvvwBHgUCQAAIAIGtEQDzgfmAABAAAkBgXwiA+fbl72wmRY+9XOqdJH2Bbymq/CjB1OtTrcAZHS2WmYHT9os6j4bAgqe6Hs00BOd6BB6A+WbkuPX2I9blleTLMF/zFuCCJ74WstSsjhbKbLXl5vwsn70pT9FCXdu7z1cNvCUDobedCKB5tX8Q/LNrChQNRGkhYdvhOV3z9DRopp7nM1/V3IaJ+yoCoSoRkhb6L0LUQBra3kfUTDP7hiiZQgDMN4XOIDU8WRMaWjakzzHZvrxzjpA5bTfviBNTzVOVFDmNDs5HnaP5OXXmMh+/i9mczkr5tzyLT3l8wt2za6bH1aaF/vNmNnYGNTPlU9CW6Fm/8pOKmiz0o4OgM5LrMTwRlhRU8jmIge01zAwld1hY+CZfrTCpPKoNEdia+ThTvJXuaU5kr8K4rwdJDMnAtnnT4c1/bmrmdHVoJ+KDEZAhZ3NYeTm9Hdv6uWerJuDboFVfuI/8snD3jRX6HPkHLzJ+vt78Ion6il+Wcl622KDv26XnzYp/a0cdGZR4syiq8XZCZoiccHiCy0ck9vjx6r+hZauZqrwpaWQz+dnPCw+E7HjVmnNJt2KFs6uOjvk1Q/NKCVWUO/JihKdWtuaZ8qEjddMCPSUkLJjtgwxJKIZPssmik5nYzmEg6NjRKcmlhWqjrHr7DyGZ7Wqat3eBmVlzLwrXEYEbMR/FUxsEZVtAA0s/XDv8sqILKXh9JgIy2gvyOmumQp580CC0mazeDRN2G/k9+OEWZ/MrnEwbOqXIljRX0Ih3ve1mcoobM+unncmuudLS0AWZb7OBQLbbhqFXmF2jk5vO/Pk1Qx5J07cVDvCsEqymc5wpX6s5bRfomfs9PaY4KfQduU+xpySXFtaR0ogqdrW21/quOyqcdlyKEgrHCNyE+fK8Q6nWwp2yTMk43bzeDYCxYSGAUK0gQMjXTTweivo732OeTKtnXWmQ1OVgWdzUpViwPZJijCiKvbpcs7DULupJFEtiLO19w4GQMJ/YRcMqoBSskOw8p6YbUwaRF1ULB3ha5VpTZQbltdDq08UCPVPkg8fTYNBCz0x0LTmK7sr3XFyopIV+HVmHnhrV277WzGBREKJ9odAjcBPm88FUHVYzS3TVqNybget5CIQswLPIBz+Z9uXjSLOlt8Ph+0Ak5w43l229k8xXsrzswfJki0p0nacnR5ZUK5GZT95rJGe+CMhnWbU2HwV8Ut4rr2OHurB5ZLNcaAzUJqT2/JraitO9g4vLQ2HTXfwz1PQyTXktDKiO9EwASZHXJ5Ua9grNdfZT+vqPFso603LKkHy3qS5+HBbanooeHtJC5ySvcUdonkKHQkbgJswXYtFclQxsVnFUbg1xMRuBiHxY8700OYhmo5oX/HDVvmj6YuWtj5QbtJwWGfJ0nzWh7iot8Y9P9c+gp6+pAo0nmOrevo+fL++H7/dChOWubzi5EWrS5CIuiSRTOzQMln4Kr+A0AkfbsMFMa9vZWKSNyq2hvyDbjTzIBPfjgnrH1y/X82vy2KRevOS8MMMzr1ksDcpzzVbVBXoGkDUkQvROFgZXpvuZ2ryt6cNb9Pc16bqD7gwzQ++tnBTDfRdeg/lKspOpE2c39no72RkNbKpsY3jf3jo7oH0WsMFPheKjuvci6X4K7drqpifTEvN9fX5/vRB3Hg7fPt34RLOE+fwDdeG64N8yByHp++VqTaGD6yoDIY4a6l0HkdMkeUljfk15NMkRKkcLD+22kOJKpzV2ndYUhKPynNMpIM2EMpO9ufAbAAAgAElEQVTILWqDliyyOQpJJk3IO6VQJA8LK6MkWimAJr9ECMtsC12nE7YXCcWKsTtaM6ueUQLKcwS2Zj63q3D8eHfTfPZ9WVVY2rUFQXQeu1/WHzp+YoXcNtTpEHBI1hUbFSryUoEyl1/z6XmeobnlMs0mzgvKDTqbKbN+53Q7ZzjKXHUybcmAnG7YkAUyO4jECiehDzm1Tts2JCcSukLJsJy+txwITnMZMmXWWMvV16ynaBVmljNrsoG6K0Dr4OOHf2mvrIy73jVs0ua169hc08gcPVtKIJlGQharWSgKl5d9bz0H26lUI8EV1nXbyUJTI7H9tVXbRlMVGxw3rG8NcTGFwPbMBw/dEQI0iiwLaOLO4oMSoluU05/tRF6bx72sDY29WkcZIBvahe6AABC4NgJgvmsjroRxk35nMx/tyFXm4wly/fOmJtwEN3QKBIDAUyEA5nsqd57ipNnMh5NpscgDAtdDoNv/xI87G4MP5tsV88FYIAAEgAAQ+AfmQxAAASAABIDAvhAA8+3L36e2Q4EGEAACQOD5EQDzPb+PwXZAAAgAASDgEQDzgfnosZdLvSqkL/AtRZV/4Q9va52QMKOjxTL9wNjr9YJnoDxErTvqF79GL8Oc8K8XjmsgcHEEwHwYgZdjvuYtwAVPZy1kqVkdLZTZasvN+RG78AYkde0/JnLN+BlYNEMl/12CYE5r9YD5pIseDW0emY/09L2418bpPXG9VRGOE6/WzEHzEfJtc//NaH2PftQW5XtBAMy3F0+PJ00XY770q4bjftcjv3lHnOg1QXs9mRhucjItv2HZnDfL2C5UiUybWIelzHf6JNXAfNRFePsz85ePOseUmZlZc+8Ud501t++lNV+O3SIyIfNRENiY+TgQ7VxHORyg/Em3wtfLHgWyR9YzPfG1zUH6DnudkrOnJF1SZXVcyG6cVjSl4mRa/khjXWGcOxCGR7bSeig5LNeRgS7LNG6nP4UjzGeub1wsx6vaos0Hg9swb4zlQzPiqk5OIajCyQr5cibveBejVPPZzJehFJm+kayYTMCFW8+JwMbM5z7SGs5Lo7GhWVLjG1G4PQKSqgryOhemQk5MlO8sQ+ldnEyrCdpy6JrvdrpljT8+aeFAaBP3UKVxtmqVb2r6CLHA8HWqITVC+LsHx0857sMmRnzB37pMqKtRI/7ZmJk0n0waobmXTGjzYUOTzbcfhh5PXN8Gga2Zz58+Y3sm6Yi6jf07i3JCvm7icVKQX0GOnziZViOQ6CSeLcALkbqh55Pp7BxK6bh88nj9QAg5XfmGYvi0SkJpxEY1ABLlQ4Q4nQs4xHbyIBKhpHMCr0k55K/e0oPolBELCJVBy3eu3fNNjZn0p+4PKYbqrMQEzjkmTZARn5LC00veKbE7yxVPDsXmzFePsKlnp/mgf3J872y0hLwmm8/u93+/CudEKUnKFhacZSj3cRqy1WGSeTURK1touikJqEmCFAMxyQY9wy2VXIB9mJNpZenMfHByIASUOgppcrqk/gaWjA80FAnb4rukozHy4iNjO2pbdVNH83Dudju1az1dSOKqrMCSE18b5subj20MzZt1XlTbSUYi2hcC2zPfnzKW3JZFGF0IvisiEJHndKlrPpxMayNfeVpyq+RK5fu6+DixeDJpdnGBgeBzuk1BlqoUZhItf4QI8TXp2v9CESlkNvM1U5wCjksOVOLNbEaHV6m5ZX+G5qSnm9ItmCKY43DxhAhcgfkojo+fb1/HOkPkUVT/tJDFxcYI+LxGK2+e+1OhLAJGe1mZVrVVXdZbGtX8ogmRuEQ2tcpWG+WjuqM4d80Xsy1r9Sgn05aEfuZACDnd0O53OwVeWx36mvGn986zPkIqS/GAdfxBAuvODd/VdWS221l7YcWa7dbeqL6kSMiaV+FqZmxOFs07w/YJ83sPDkoEgWswX9nHL/v7JbxktCT7Zhq+8NAGCHAW0J9MdLvScRgv0GVq3ywpjCPrCkMdSq7U66JzxnxybLo239/JtMoWDVbzBkJwHGHYsJoCXvBvmS80b4gnhhnVtArqWSI5dRxfSO+2XXl4C4u2drfT92706Qpr8LhC6ZFOu/WF1jxlKV/T6ekkaMynzVG4IwSuw3w7AjTmkXsznFKD5bUpVSl1ukU5/TlKOnFvcMOJy9U6ujevQR8gAAQujACY78KATtHJhqww04rZzBen7bwucUR4e0Nm2otqQAAIAIEEATBfAsp9s9c5Cs9mPpxMC3a/XwS63VfZGq27pueMEbTdBQJgvl24+Xm5HO4DAkAACCxGAMy3GDKwCBAAAkAACDw0AmA+MB8QAAJAAAjsCwEw3778/dDTNCgPBIAAELgIAmC+k8zHP6c3r0/d74//J83pK9BjL5d6z0lfXe97mS5ZDPKMjhbLvMiIel4hwHM6huffXfCg2fOG03y4Nql5b8w3GF3tm7mbYDFIpgOVnof8Lsd8zVuACyBaCPKsjhbKbLXl5vzQYHgD8iqh2H/EuWTAq/R+xYEw8NF8M9OaaWH0r/9Qg/nXf1WgftpbpLWRQKOmvN1fnymtMaNHfaWZapL5WuWrzDo97VVyJUWr4wd/XEmVtC8DZDVfDZy291T/8gWf+DmFFJBR89uX3xPz8TtkFzh707y48GIw4G/vpI3nfRdjvvAhj4XgL7Jx8454/FtCdLpxztr6ZNqdDIQLmJm6Iy0cj2LydflKQxZXdrCGnClYahJxFsIjZrIvHFnMuAp916zhW19evuh9qAFGNZXwqCOWn6vkovRf2vuMwnnQZY5zwg2Q1MB7KdyY+RijeSfTZqdKcvakidiMszeFt2zWpiFSP1lCtyhYJW5sJkUvaFur+okmDutabtO6OWeuWuUts78P9NnXie32xWoWIrDIS+sGkUwbZczT2FCUmnfb3TdW5qCkG8gJyJmPnI21o26yUnLKcplhQL4fati4fvVLys2nwvgLlpoHTQ6jZ5GAgdCOhXPHe/2wdXQHuX5GunBureGUMZ851H2q1JGlqcHL9PqdI1LDvN/aLlFq46uOo1b5GDaZTBJidKtGVYu0RJSv6ml5qNn23qotUGSOywCp68hcjgf2BtcbM5/7rG34KjF5tHcD2V/QN7AM0xjf6rkKGU069CPIIkRyuoWFxo3k7prHdUpFEyW79vK1IfdFmlOocXf1uC835SH5qRwv80bX3nabGJrCNIpM82oRuaCMrgCF+Ygvwq0BSq+f318v4jLrtHgwNC8fWkx8FKJIoiXkFxLbebz2qFHX9h7dwengU04xdQeZDkMxklyBpSkMGaoau/uBQFDoHIi8MAS5xEn1VFozLYyBWiVIuUssc5nPNeGjvkqQaE4Ihzm33VGnFH5JKPbK+47675KTqBBX0leNLmf46cK+d9e8tyI4zuvJY39ys7dz5WRHfdcXKdma+ern3nWxVRxvGbYxIwDqT7z04GZIUdjZECIvfk8wX5oc5zIf96KVKUty/NWJ24xjQm/l+0AMoqeghJNpaxxS8NheFs8PgsfnHAOb+NelHtutmiLgnQyEc8wsOcRlBh2Vc3wk9EPT5ZAN5PcwFwAWGDXDSI8SJ81ptyXv59N6FRWGoQVGprwnNp5LWYrjHFhVqikxLOO0x9OFWe9JGKvAuESZAKQqNiXNxF7tYnPmq0fYnDyQkzEKI8HF9Ek6qZ6rWKcRlhaSVzIJVG6hSV5RlbQyM18ZA7YN6Di4KnMPjg+2ky06P+ANTD9cqWbZriHr6i2e2JKlYe6isJTA1T89SrTaa4Byy7UAMk+KQz6ymiq5dPSUJ9M+30AIfndzxGaPxzvXX/eDyN+1a7tw43RGJqVQD8FM3bn4N3fYN9ybZQ1ZR0ZVHuIKRWZiexiGJfJHype+Buf3mkoKUcif8wtHvauEHsbQ0QCQvtX9lGzPfJrF3E5CcHyDhQfUkqz+sBQmaE1DTbKeYEJHJcJUH2muheczXxjPjW5382cAJK75cDKtRU6cIEteu/rJtDsZCOvMTDNDWmjzp4kx6JOAVfOFdO0mf3VLVoiBaSMp7GhJhYdhKB3NUd6lUJ6Oe5UKRfk1oo/nuq5VHULNOb1rwyKW1LYFKI2ROjm25UHT5K7+vALzkZPmH8gZAPUzDj8r8eV6PWK+uiVSdjCSsBOXjLqW0CxuUzW0O17zdTPEu/KxU8bbTqHP4FChoEQDQKJZp7GurQ0kuait6rJefWGhH1Fyg00mwraSaxbWo9lJotXTnkw7ikbDduQaxdz7yzlLpueEvA+GsLEx6nqLgTDqqzVTAsZSrUXaaHmn47SgNNE8+6lMNieENUlDn9apa4JO875iy10Y0dah5FUt10Pki7aN8tzKA5WpRO72dSw85hdO9G7S7CKKzQBJDPcxeePrazBfeTnJpbniJN1St/zr13YaWApQFg3mhuFepUQ8n+T5Xp/trDMg8l9VjP0nWmlqCCod3mxAan6x9YFr2+wE3lEEpEpSYZkfyJDmcd5MAxMfKW4RQ/aXOqtBiWoWp+Nk2hLYDpM6EVGUeP+8SfeKrQ9+f62Y68CR8Lu7gRBCkeydNnOCulJAmsK2eehd6coX2gqGpmuZO2plHTvy2oNWbswJSYDaaqdhzlH8WJWvvbg0NVCJbTSxRdT8QtOw9h5DqFRwKgkyxdJaXgExmfd3cR3mSxFE4fURCEPOp8v2mgaM27+lPy0XNGob9zflF//zah1dXHMIBAJA4L4QAPPdlz9aBrrwXGk289GeWGU+XpfUPzdWclcegbFAAAjcAIGHY75upV83J28A36NxwGzmw8m0F55zXDw4MRAuDikEegSePMAejvm8b3ANBIAAEAACQGAxAmC+xZA92joPBgIBIAAEgEBAAMwX4ACrAQEgAASAwNMjAOYD8wEBIAAEgMC+EADzXdDf/Jvw1Hs8F+zrgqLosZdLvYKTv0x2+mmRxdDN6GixzKef517LQCB/qeG54JG0zLlnNr+UFfco556Yj1+6lJdG3fuY5LzyJqm+Os0+bkeXfyPYf4U2C4hzPTFIu61KW3S9gczLMV/zFuBpwjNHLIRuVkcLZbbacnN+cthFo356dNP5DQZC64v0KFQOHsFqjjvSmmlh7N1/0sEiIc82CxxXo8u/PtSN7qXUVcWyqnnzXPlotWpSBdrkeEnz1HGjlG7Z4BoX98N89gF7+RRCeW+awq4QHvmgoM9vmzVn2PqP2qnbtkJwwHxbdbexORdjvqu5YPOOOIVZmnP4cyqpB4du4XEMhA7VbLyzU+a7I62ZFna9GyVQVJS8lEXgfMdxv3oyrUtxfdehpovDvmZ6NFLePFM+E8hfdFLCo/QrI2J2c8nk/LUmNzVx9rqUbiBf62Jj5osvRNOnv8r70eQS/SZQ/4q0ouxCzb5KR99CYxxp6uEAne8P4S2bufSTI7pVvl728/VCyrCqpKe1UuULMddyW5jOOZ3VKl/J3zI4g0V6StELjyu5JR6xamK+jPkJx7lvrMyxXX2XQEdDrkXeDfvaUTcFKUN9ucww8q94Mu0Enu70tV0PhHy8W0J4PfG1LfIsxUN3XG1a6MIshISOjgnm8/Vp7FBiGTlOGbToNswDEs82Ei1VurDRcZQFbd58kCo7mTVdK4as56C5N1+uM8dlgNzkDNuNmS9+DZZCTbHTlVyPlxvwPqbJDe6YUItm5YzZ/uDDRPT8LYn+wdd7JRRqxtfpz+zzjDj6ebfkTs6w9RYxDuQOKmTTaIB5G4uPKFjrdG/kOHMu5Qh2Vn9+L06mlcRqWNnFIOFqAsVA6MZ7JRUPjmaDgKdlW18zLUybW6FrfirbnHCc5oR0oeZToh+wNk7/ZeOIp4Ptccq+uY13mgfYMDegEpnOXqqmf6bNTU5/ITm20JsKKQJjSu/bbleyNfP577HW3QDCwn0cqzGPwkImMgITZ16uXyf7dR6ncckydR05nENRVFX55Qzo/ZxhSyOhbuIxvMp8H5724lnPfsiNHJdIfq1HHgbHibubpB//DNImbtFoZF/r4i90NNEw3vK5Rn/J0xCyaNEuagpo4nb6T9cjBsKaw5wJQF3fhC/U+2Sq2cD7InVcWuhbuWuKRtnjsbFDypStIPs5pkaRxUxhizSDlal8WUS67qqcwYzcV9AEK13EoG2a83y027jSJl4BjVXlb0KVrgX/adu9HLkOjptM6X3b7Uo2Z756hE09mZY8R8HBodPMPhhWXdQ36zzyrt7q54A16DlSM48KjjXoY5MQ1tR8mH8zCWSURgyHpg5IrUxJWU4/qWNGg2k7B0fJwSIZCWImq+QHocOQYK+3csepsaU7/dPbjpNpS/RiIEwMhEISkhzqYG9nuhpgFG/+uo5opQd/167t4mTzIJAGRZOvyvBx2WZOBgvUaBstie1hwNb0Emoy9VKJg4v/bDJYTcXVqGaY68pBt8Q4RagvdJbpUoprXmUq8lpCatuUZTKlO8mtkIvf2p75lD8Ga2SaSlgwcdDUJFs3NAREH69TzBcZSB1g2Gk69uCmEZYWUqtMApXX0HQjSisb87kA7XQzJbe5CBZJypDhcfzEybQWD2HhWPKFTtT8rMWmSvOcVcDHQNAp4LKB4BOoTb9OuiOtmRbO8WYY4Dp4fSFdu2limsGSQk9aKpaDKgxY7ajLmcS7SdC2zBfzpwTtpEwbESW5WaK2gNfmoabdrV0Y8xEluwyfqdQ03+jPKzAf8UFzMq0zhlwrgLZBQxFAd3W+UGsmgPpw4QnRRBwrFXlvOeEyK5lc85Gq5kvXdYgDdap2x8wnFrkZooPC67PRNZmpyNj4qbbXqWgzhXQ2qsK1VeEGXye3nXosvbOP/E+GATqdLVVHC2KJVo99Mq2CWUIdA8EB0g6B0aBr13wSWtnwbGtKxGqslq4nmsenFnx9iWrS0Kd1kk/DRHOFDhnuQodh/PHFDyJtbjV1jOTjiAavJha9ph61ufbuu6ia5DLNHTn4tXnrLGvYLtZTQLxK17q+BvOVPWL1SomGdpecoPczOB8uUq4zDg4mX/n4EWW6OUWGo1JR9JZE/JOfYRug83j6axm9zbyYK4TmNsx0QDo8NZso1GVCyqmhbJ7QSykUElFm2WahQh2xdSWddOQHEnsw0XNSph+i7tppVeNWDVTrtD6HblttTqHrxfaX5AeVGN78dECtHJ3lRs1zDoRqeMkPDas17pCB3NSRPNDUTAvb5qF3jUlfaNlmfgYrj4Dl5oSURR1pp3EglAixE56VZaW8RKPX07bWfKEpz8Jbma5mDW9X2NK8DpBigq/JUVqcUss1kpuG1/jzOsx3DUs0DaGvCQQo5mwgTSFG499tRtGfdZDEhnGbJYzbCU1W3LpaRyt0QxMgAAQeCQEw3yN5K1LOCs1nMx9t+Vbm47Va/fNsNVZojiZAAAgAgYsh8MTM120+hH2AiyH4UDQwm/ncw7e8JwPae9yAwUB4XN89hOYPGWBPzHwPETRQEggAASAABK6NAJjv2og/1BoR4AABIAAEnhABMN8TOhXkCgSAABAAAhMIgPnAfEAACAABILAvBG7CfAues0hJu74WVt8y2ZfbUljWFpI7LvVijb7At9Qd/CN5+g7W4DWJGR0tlrkWwKXGWn0MBINiu4tzQb56VGwHBSRXBB6S+SQWs/eaq2GI19kIXI75mrcAB6SVKbaQpWZ1tFBmqy035+eBwxuQ1LV9lePMeLtMUp43EKiv8ur0nPliNLPONXtAWtzC140zXy8GzX9UIfjCfUZZOspqng9yjQSbIKaApIWZSvpV9FlgLnRc4o7FgF/Ea3cuBMyHsKChZUP6nHgdfJHy8ghv3hHn/ZhkxQpOoxc7mfb8pExazWE+yr+F8CiPn3J3a+YCwPlN0ObU6HOCKrQlv+gXFaY7qjXPBJmaK1wEnURFCkhaWPUPKnkr9DojrYWOu/xYq/pn6j3u3W2Zr9uSojCSD1Yd3mwmZe+K0d1uWpoWzh3wj+uYbTRPT3xtx7a+w24OEqfI+HTucK+6s7buGys4mVaWI7q6uvFAqDlXB87kxjJRaTzH9UROrzkxO4y03vV5OQvF+P0EPijAkoO0tRg72ZHVpIg9kW0qGi68pTCqZJOMFJC00A1kU8njUNnU1dQKCx2XSMjBV/k7vrst81mgqEsosPRTjTWTypTKzW6o2kShSOuEw50nEZCBXeaYCrihTYNQp7f8IV1J3DT86lRXlw5tX8EdlC+oCXVBBFnGfHL6pY690Lx8zLONkN7vsVX5dKGt1eJdb7uZ3FrBXbC27SGf7nzt9vOPPEVQktNQD4VRk6Ln9QaCV5hdo5ObzHzLtq7VqZzeyiF7K520dxki7w4LtkADGp+uuVOpBkPaUa3pO6p+T0MxKaxyWA39MwUkLdR4GJ2mFEyulWVcaHdUftJxOpRaISjPENiW+chbnBF0zks5Rdd8JaA1KdBH999NRXY5f+q6LbSj67WhGxjWHBc5AjTyjRjk0/XijuMnTqbVQGKmN4Kn/Mu5VWN4lMK0eYr8bQeCJFCxy01E0hSZmkljTT6BVL+sPWXvTOZrQvE17Nz6FZKwF+091CaMc9dRXzPEfJo0Jgs9M/FshoOBmnSApIU6v0mUb0/J7iNnieNSb6JwhMDGzFcO4Xw7HL4PRIFEbznzlWGpu538EAEff+NK4pMFabyO7EQ5IxCygOwmuZFZ1oJWs2R/WwTwyORlHDnFVodU309O3Z+aRjWLNV5266TozaBnuNV0lAWYzZ9CQ15HWt6st4JKPM2iEjff4j+DgY0OfcLqSzI9txoIvUXNcqEx0GvrTfPXtQ5Ti3NcOrIIXr8U61WK7pBQJMeZbjpXiPKpdx94bUdRT645iKWgkv4ImhYW9Hj3ItEqBWRY2CrvI60qr7OK+Y7r26JkEoGNmY+p7v3t+/j58n74fufxPx7wLt2I0jYMMhtq8sruxgGjkbT3miELSDYXd+BkWhcwytM+CM89mZa3NG41EGgcuWlNTmk0RmxaUxc06TJrKfMl4y4JRTuMSfbJbZriXNM+zjNmPqsZOtKkQas3I6HJwpA60v1Mbd7WtG0D09/XpGvvlAQit8Eud8eOsy5wMROBazDf1+f31wsF4uEgm59pLFJhFytpYYkwH0Yzrd19NY+8DX4qlCxgO3t16p0OSCqsrZLKOkT9mk+OaSzpTCbXLoFGb3o9LYW5BUHV6lFOpk31TM2kwksPBJZZFmF0bUm/bKv49Zlhq04Mo4YdVzlJ/Ng1J292hUFOWPNZKPLQpn5/jnlzqll7l93REzVTkJ2cGoppYeWz3KgGEEEvLeSHnkR5EnWS9nSUKZLRceYmXKxCYGvmc0OOo4GHXBqL8WTFOiNjCbqlbs39nLRLEzVY42BDeQ9m5DDORziZVni9xJij5xJOLSVQxuyCsCm8+UCorq+0V3YXB68nVjNr2zZfC21U7vE1+XeKeqsZfaFmUIkZwpWEmkp7oZA81Z7KawRJNbVVnUUx98gvKfVw16zQdVQjwRVWAjtZaGpwbGhOE+Xt8YUuZVWxDpMGTPy5GIGtmW+xQp3jIeGCCNAosiwwBTVlNLf5TH+67bIwyYp7g+HWBTWX96NHOly2I0i7AgIToXi1iLqCmejiThEA892pY6ZoaT27TKSbiAP9tF6Zj+fC9c9tdIsKrLcRch4CgWEo0pZ7XV09hC1Q8vEQAPM9ns/OIJ5huullxocdQHu7ipMrGJuEYgm54QbpFbS6VRfd/qfshWIGsNkMGMx3q1hHv0AACAABIHAbBMB8t8G9X2OhBAgAASAABK6DAJgPzAcEgAAQAAL7QgDMty9/X2c+hV6AABAAAveMAJgPzEfPGlzqVSF9dX0pqvwL/5JHG2Z0tFjmPQ/Um+qWPI1yU32WRhfqA4EWATBfi8j+hvTlmK95C3DBc1kLWWpWRwtlttpyc37ELrwBSV0PXv1uJVwytNxL1vT+dVAp6XegfFJTvowzbdEk83lA5Lp7R3tKed/c1Duz0OTUizOD4ZKu3F+GuUf0wHz36JXrjo2LMV/6VcMtbNm8I868GbswB1zsZNoFsbfA5KHyaXdzLJpgvqnm9mbeWPm0+ZmFnZn8cupWh+VWcu36xa07RmBj5uNBaDtp/L5OOavBf34sSzEIoy0QyI4D9V/g5A9H6Tvsdd3AzpLvp1BWUt81L/m5T2/QJ+0/uObP15tfJLnmurdZFwT17aVUTwOkdtTteXLSfOOPVLVvRE3LNOF08X7IF1WkajyylXmdgarKi6hYePZAGJNH0HxC+XQKMrCokSmoWjxUv082r24aKZ82P7OwM/PkGbaNsfhzFwhszHzlQEVOmvGzICVAk1M/doF7Nz6vY7UQT/kGmE7JqZBnJ5TawjRFsrlbQ1BWalN80Tzc4tNVcDJtwIowWT8QRuTRBRLzTX+sbjr7Js+qSjoR6QTaB3UlbCxa3EkC9QufNYx9POTKp72fWZiayYWFUMcVMsOrObj7ZAhsz3zltElaAXRrOz66BbF4PQRk8q7jmROWnrqAk2kVFmZ6Iy2aHzAr1PVllujn5AUmg5UDgdra72eDyQfpMFA+VW+2RSFsjNImm9cF31/JAJ3yafMzC1MzpRDMNwHODm9dg/nKl++74TraU9qhG65lckhhchyoO5egrAVZGapZsr9Nw5mhecuaNjxtdUj1GzLQPzWRaR6UvNwlwZIca4QEPS3VJh1lJ77e48m0ZXLjUHXTnXYgBJTq1qIGSS6k3KW2rknzp+s0uEz9RUKS3jN3+Cb+mrsY04wq75vYtV34iJpf6K3rrscq6Yyna6KAo8ITInAN5uP096Zbagoi9jlvMNJCCsPJtIPUpjwtDhLyOPdkWgr7Sw2EMBVooyhTvq1TlKmLSJ2LdLsyMlpD2EjvNgHyQrQ5bZvrtY531WGieSpzfuGoR/EymG8Q7a2DdlJte+azn/cog+iqYmIqqiNkJw64rpk+hdmvelQoCzjKMvgrPiMAACAASURBVPJ7z2kH1VY4mXbG+Xy8LJbV2JkDgZo7XuE/9fBSpTRdPZND9XrxIbR1JPqwqUfc1dD1y7Kp02LLgrKlqNi8iD2zsCpfMjuYr/qrA2eHtzZmPn3SQZDl6duP217TpwRtcMIl2yJAKcwm6bpd6TgMJ9MW/B1QfXC2SZkfemyrxcJzB4LTx6aPomrHfGF8ea2Smjrfby3ScoeG0ZUs2kKu9M0bYiYJY+VFvm9eeuz2z5fWNDmhdw7+qWd5GsPx59MisDHz1fh7WgRDCrh3eykHWQqb0pzyV/Nzka7XWxvj9lp794J+v1pHF9QZooAAELhHBMB89+iVKU46i1pmM5/tUXN3NM33RHiWDrsCHMYCASBwjwiA+e7RK7dnPn4cw/ZFQXubeWRX4bepsby9rI/q1ND1+72YsQEBRQDMt+lohHAgAASAABC4OwTAfHfnEiwvgAAQAAJAYFMEwHxgPiAABIAAENgXAmC+ffl702kUhAMBIAAEHgKB2zAfPyvI79a0vz/zz9T1hZvZzyLq75YPAfqdKUkg67t9584D9HNlS+U0fj/dfEZHi2Ve3y9PNxC2GLAXksmvVPKTL6P3c05H3fUjBD1uhMBtmE+MSV6JlVNywHxXJfLLMV/zFuACKxay1KyOFspsteXm/KxgeAOS39T230w5f2RuMBBm2e4/DBZsbKEQSpgjc8RSVF6et7TJriDZI9z2PpL57285VIskn9KflD9VJ750b3q2+oAgnwGBe2O+BtOJoG9q4s/VCBDIF1nz5YfRbJA4Nu+Ik3KWKDkgL30ybcZ8jTeXDAR+F3PZQaxk7+RKaK7MXE/3ETViIA42OS6RzSThE73nMuVjbJmPGuj4TzLQfZlhEJOZnpm0QfPz50CQcDUEtmY+itr6bk2cdjUDnv6U13HqbEuCniebdOt07F4NuMfsKD2dlUBW5hOoBWeDXTwouck7tHGH+8YKTqaV5UiM5KsMhHUHsTrfJWl9vsxswEZapWFeN3WEVyjSlMNcgJVqmczh6cF9c+6CmdsO8cgH72k9wYLPg8DWzOeQ6g5naJhPwjEWShyX+aCbkTmxyUDF3RECKZ5UyMxHCUgpkA4WcKcUlcQUvRN6Cbco0VATEkLzlZJYXz+/v16klXVahITm7vOh8tVH06o5zyi2KrtVmkObbyt729veYypkbfvDXS0ztt+Z5ClCJTmxKC3kW1cZCIRMSzAF6misqjSj8gyZGcgeLg6MbgpbmS+LEC/TwjL3Ud+cdJb5tP3fekphOa2n1kTOeXwErsZ8ySG0XdqiwIqFFPSWyOQ8uRMTt8d3SZKYLmZUh+fxQ6kFJ9NqXiOGU9aXb9kwK9RnanyKXOyaKw2EGSwVftaqo2xs0QyZIcDKWBa4BFU3DbI4p+lRxruaCoJMSQLl3AnlsFSCNme3zlnzndLTFMbFEyBwJeZrz97k0RVCU8dbLIxBT4On2WHTbKXNn8AlW5oQ8eR04D6l739roZpuzVdv8TKO9j/9Oiwcc+qOFVW20M20kv50A1wz1/SMJ4REwzoPdjItLVl6jgkGaiTHwug4GwgBzzA0qHlGJ4PoIvnBoaqGr9/KTHoPehYTmnWeKc9dUB0/qINMicAgU47Ekv/rJNhkJs2XMJ//ScVkZlB4WHD9iAhchfm67R1BqgyMGFixMAZ9k/Viw0dE/+o6J3jqmu+lyUHGcC3JFdjbrdGQN9VTnvnkSTyrFh09tdb3NVWgzXh4CfX2ffx8eT98vxciLHd9QzHTWCfeMmlyoTwtlkoGvMTJtDQ/cGRv3k+ViYWJ414n45+aL2C+Bv8GEIfnCZlBz2ICAVhnTn6SRBX8Lf5xtIuQIFOacyw5meyjqQCbs+Yb62mewsXTILA980niyEZpHNtudNXs4IOerm1UPI0DrmuIx9OoqwJbd43GXlOFa6t2Aj5Y80liKtxD8nVNybERg8Hr6ZJyohUx3xf/fPj6+XM4BGqZJTOLTMJBg9BfF9uV1xWK9Ce9rjBRPo35tNAD4pDPlBetyPaGpQTzprBIIG1tWlD2EruaicxWAa+nOY4KVZmqPEkLtFfeUugixMuszb1f9NpZ0QTYHOaTX5eL1bUj9XLxC/58DgS2Zj4KoPALMyWUlYWgvbNjLiCvePpBLhVoNk3ZxB4KqE+iu0LlhkgwnCCUG3SJVlZRnOxEwjc9dj8ZDJaITb5dOBxYYclWnOzYqGAmWaEd9TKdKJ/anAQ1s9ZU67SkIznig6bQCRRUVSUPsuk5XaiO8wrbddeREVjLfKGmIUNGTdYk3UzmLOYLPyiq8oyPCzCReTJCtHmQadOUrLkGpN9TbdVO0HMd2V1cPA8CWzPf8yClae6hLaJkF3LcKAVQ7nO/G9GfbnMptIp7g+HWZbG6WkeXVRvSgAAQuDsEwHx355ItKXY288XdIZ5KOyLckN525Q4YCwSAwG0QAPPdBvct6W3CotnMh5Npwe5Pi0C30Vr3nyfGDm49FQJgvqdy540IFRgCASAABB4JATDfI3kLxAYEgAAQAALnIwDmA/MBASAABIDAvhAA8+3L3+fPlSABCAABIPDoCID5wHxAAAgAASCwLwTAfPvy96PP1KA/EAACQOB8BMB8YD4gAASAABDYFwJgvn35+/y5EiQAASAABB4dATAfmA8IAAEgAAT2hQCYb1/+fvSZGvQHAkAACJyPAJgPzAcEgAAQAAL7QgDMty9/nz9XggQgAASAwKMjAOYD8wEBIAAEgMC+EADz7cvfjz5Tg/5AAAgAgfMRAPOB+YAAEAACQGBfCID59uXv8+dKkAAEgAAQeHQEwHxgPiAABIAAENgXAmC+ffn70Wdq0B8IAAEgcD4CYD4wHxAAAkAACOwLATDfvvx9/lwJEoAAEAACj44AmA/MBwSAABAAAvtCAMy3L38/+kwN+gMBIAAEzkcAzAfmAwJAAAgAgX0hAObbl7/PnytBAhAAAkDg0REA84H5gAAQAAJAYF8IgPn25e9Hn6lBfyAABIDA+QiA+cB8QAAIAAEgsC8EwHz78vf5cyVIAAJAAAg8OgJgPjAfEAACQAAI7AsBMN++/P3oMzXoDwSAABA4HwEwH5gPCAABIAAE9oUAmG9f/j5/rgQJQAAIAIFHRwDMB+YDAkAACACBfSEA5tuXvx99pgb9gQAQAALnIwDmA/MBASAABIDAvhAA8+3L3+fPlSABCAABIPDoCID5wHxAAAgAASCwLwTAfPvy96PP1KA/EAACQOB8BB6e+d4P/x0/X04D8fb9e/x4/VN47v3w3+8v/3OFmZC3g1T7/e/wlnDkqd7fDr/f79qpyi8yZ6ndtk10ULG4BQSAABAAArMQWMd8lQ+IPw5vN0y+p7iHUXj5OP7+fL1URGa1qqzz8nXMmI/E9sRWe/n759/r549nXAcUY3hT6JwyQWeUAwEgAASeG4H1zKdrIGKFG5LfDA4jDZsF1oxWngxy5nv9/GnEprEy7Kvj47Q5CoEAEAACQOCyCJzPfP/+vn3/8tJHUjytcniHUKnRLRD91iK1KluOyh9MolJYF0OnCn9bVmsBivuccrdlIzWhtG3+/JMy39uhriNfvo4/Xy9maVwIDpeGVF9R8kTL140OzZ91Pdo1xC0gAASAABCYRODCzGebn7LL95c5IxCbUBrl8bD9+PfPP/rtrRAeMY20OlVIrVR+zgFpha4wkJDrVGQmzEcGtvQsFlXllYOT5nTrxJpvWqXcWO0Rd4EAEAACQGCIwPnMRwm6spSt6nihY8vBkpGlMNChaRYfBuGF2t8/WWFcQnUcZgLlImedvpVjMr+YGwlpxIY/naiiTNsdmUDr3WnOdnJ6lRoz8ScQAAJAAAjMRWA983V7ldnyq9lpLMwXVjOOFPV5S9nwPH7wqigtrNuJLam0K9ysL15ftqxjhPr27RZzgmMgNlK4sStuhzrGKm5oS14+jjZFaBV2nptSyVWbkIBbQAAIAAEg0CGwnvn6H6gSEmp+neI/ZQu0bW5Z3qs4ozDp1EuInGSbgVmrQm/vh/63t5b5uuahQstzKdEGJUc0NqHSqAnKgQAQAAJA4AQCGzMfbVfanh7lcVlpETe07wPQ3XSx1RXWZRzLMfm5qR1LUbW0kFdyP9lqLBAbr0TropPZNFTomC/cLa86dD9zGiuHC1pcpirlxoa2s8gVcoAAEAACu0Nga+b7x7/V6Y5lfSSEX3SbeLYz8mW7s0prR5Z5eDv9akGz7mQ+yJkv8LQPhUBdHbH9a1axbYVu5cqE3T7gMyAtP3XwKuEaCAABIAAEViKwjvlWdjZI7ltLI95qftUbM19KSJ75Fj9sMuhrptWLu7sRyDPNQTUgAASAwO0R2APzJe8PpGzUvcxg7vHMZ4WzLmh5N+dhlsHO5FilWb2DBYEAEAACQKBHYB/M1z2NSYyiT5C+yquEv/9lFEWbjVKzfSRnwFUR4vhWxqwmhdKKhmewZtQETAkEgAAQAAIFgd0w3xLWAWcAASAABIDAEyMA5sMkCAgAASAABPaFAJhvX/5+4kkcTAMCQAAIzEQAzAfmAwJAAAgAgX0hAObbl79nTohQDQgAASDwxAiA+cB8QAAIAAEgsC8EwHz78vcTT+JgGhAAAkBgJgJgPjAfEAACQAAI7AsBMN++/D1zQoRqQAAIAIEnRgDMB+YDAkAACACBfSEA5tuXv594EgfTgAAQAAIzEQDzgfmAABAAAkBgXwiA+fbl75kTIlQDAkAACDwxAmA+MB8QAAJAAAjsCwEw3778/cSTOJgGBIAAEJiJAJgPzAcEgAAQAAL7QgDMty9/z5wQoRoQAAJA4IkRAPOB+YAAEAACQGBfCID59uXvJ57EwTQgAASAwEwEwHxgPiAABIAAENgXAmC+ffl75oQI1YAAEAACT4wAmA/MBwSAABAAAvtCAMy3L38/8SQOpgEBIAAEZiIA5gPzAQEgAASAwL4QAPPty98zJ0SoBgSAABB4YgTAfGA+IAAEgAAQ2BcCYL59+fuJJ3EwDQgAASAwEwEwH5gPCAABIAAE9oUAmG9f/p45IUI1IAAEgMATIwDmA/MBASAABIDAvhAA8+3L3088iYNpQAAIAIGZCID5wHxAAAgAASCwLwTAfPvy98wJEaoBASAABJ4YATAfmA8IAAEgAAT2hQCYb1/+fuJJHEwDAkAACMxEAMwH5gMCQAAIAIF9IQDm25e/Z06IUA0IAAEg8MQIgPnAfEAACAABILAvBO6I+V4/f35//6N/h7cz5xrvh/+Ony8q5O3w+9/v8eP1zy1dG1W6pSYKyxwd7gI6Vfjl6+jdOkd/1AECQAAIJAisYr6370JRTFSOY5IONG3NvUX8tz/mez8w5Qvx/57I71uQ6EDmZZmPpamNy70M5ps7iJYOOtQHAntDYDXzfb/LEurl43gqUy/CdAPmu4t8MaCWotv03QbARZWbtqM/t5DZ9UXMd3gTk4nGFpIfmO8uIrlzK7QCAo+HwNnM9+ffX1oCChG6ST3vLnb5lJLX3z8vX8efrxerrCTKVNoxn1XzO5acN2X1ULcxXaGScbaDOurddUSSf75eRu50HZXeRzJdTVUpTRwdUP/+xikFLwq/qzlx5STN7W5glxYl8VdZYh4/X6xVXcfzmruW1yX4yMyZ0FE11c2HjUOperPVk8PGVsPSo0RO2jxVydUMRtmC24ciS6jVRsGAciAABB4PgUswH+Xon5iYOMUc3ih7htxB5VJTqYVK/H5pbOLvFpl///x7/fxWWqoViBu0r4ZIepl977U5U05N0N2vg1nvrFshy1kqNfzXKFzu0pSCCbjOLSjC+spls5TNJ2OZPzI9hU4SUu9lig4XhS5nvtl6GrAkx2ImbZ56sxbS3KtIiAb6AQzm82jgGgg8FQKXYL4/b4ffH7fyY4CIP76pkNOxJlauyXnHqKVJPeHPmPF5GeRn5dRRqc/dlQ3YjhuCzLx3n5RrWmz4qf9TJVOT1qJJlRpRhbp0JRdFfXjhQ+az1VLsNxLY0DR1UBvcaqCUZ2aS9832oXxWw9eka2MvQ0O7S+VI4UfaMJrpOzJRb4eyM8G2vH3r/OBHdyxa200rXAABIPBkCFyC+SjVMvNZ8v0jm3XfxFVU+HY4fB+IAin7yJovJvf6MKfmvpCeCuiW0+nCdqh4qWe3eInW5PEgM2c+yo9lycjm6Joyy4Z976nMSZWaMGoUdnedYrr67Cv3JSQh0dNTQjAtl2ATi9I1KdM5zml4Ajrq3fZUK+3N1ZM7IglxzZo0z1RqqrnHfW3aUVVSqJ0jAlwoBwJA4KERuADzEa/I8q6ZUxPJ8UT77fv4+fJ++H5nIlzGfLnMulworEZ5rS4Hmzw+g/loCzFJykkGDIsVlZxRwqRKTdA0CttdLn+zrTkp7yv3JYx8h1JkaOslXUfKXTVQ8n5mJq+w50NnxKm9n8ZTa1Lv+sOk+TptnnkzukNlej4LorIKvjKugQAQeGAEzmY+2pCUdYBPHCVJCfN98c9yr58/h4NsfuYJVHJNTLUjmbrykIl8WU2WQpIQd9KizKz302nRfEwqlfRde89kum3AXqUmsWbU5Z4BoY7qQoek+eV1t7vLwlM9eXPYTRFMjV5m5o7MzHXQ1SnFfD2pd1mW0TSlIJA1z1Wi5vZLsBnuLqp8LiTJk/UtJHABBIDAgyGwmvlshVQzsqwzyvSff94rD7NIkmKe4MyVJVBejtjSgS6KBE5AsktWSiR9iwLfX/YQDXOwNHz9/LGOOplp72GV0BCnS47kXaExFmu95zL5t0/Wkx/2mdhM8yvO0nudUlCnXMGgpu6KXYxJSpyZno3+dVGojx0VbcOfFfzczET5Smx+PDiWchVm60m9K4aCAAGSNh+o5HArc6NQoiEnOoP5vO9wDQSeCoFVzOfSVsMKj/pns0qgP41mnsrfl3fQHUJ3hyo935CBRUDgkREA8zGxxYdIeRlhvySB+SYRuEPo7lClR84Rl58tAQ0gcGsEwHwlrcf9MdDeJNvFqL1D6O5QJfAHEAAC94MAmG9Bir8ft0ETIAAEgAAQWI0AmA/MBwSAABAAAvtCAMy3L3+vniKhIRAAAkDgaRAA84H5gAAQAAJAYF8IgPn25e+nmbLBECAABIDAagTAfGA+IAAEgAAQ2BcCYL59+Xv1FAkNgQAQAAJPgwCYD8wHBIAAEAAC+0IAzLcvfz/NlA2GAAEgAARWIwDmA/MBASAABIDAvhAA8+3L36unSGgIBIAAEHgaBMB8YD4gAASAABDYFwJgvn35+2mmbDAECAABILAaATAfmA8IAAEgAAT2hQCYb1/+Xj1FQkMgAASAwNMgAOYD8wEBIAAEgMC+EADz7cvfTzNlgyFAAAgAgdUIgPnAfEAACAABILAvBMB8+/L36ikSGgIBIAAEngYBMB+YDwgAASAABPaFAJhvX/5+mikbDAECQAAIrEbgcZnv7fD73+/x4/XPDqnr2ra/H/47fr6sDjI0BAJAAAjcFQLrmI8z7+9/v/Lv8HYLky6f/e8wvw9Uurzt0x4cqLHDOQdMBgJA4BkQWM98hzex/+Xr+N/vbcjvwg64w/x+JyrdiRrT9Iy7QAAIAIGZCJzPfP/+vn3//n6///kn+fH180fWgoEaw+rw5ev48/ViC0dqy+oyiUpN3cZMZVoXjnFHMq0XWaH+fL0kfFkFxoVs2vvfP72eo95dTYZoZCaVE4xlGX38fBmpVMvDbMOZWaAbqfTv7x+uHJonmAQzf223c35HzvbTfaUKoBAIAAEgsAkCF2Y+yt2c5ihBcwp+P9iKkFIh/1wkObGQkFV4/fxWWrKaxKa9TGF16qKmVC8zNpc6Lx/H3/+UjHMo+5VN2numZ9571LB0mjUX2ktYuVdpZLv+DseakMm5Stx8FvOZX/7qnEZkzuzINa/uEOXxPxAAAkDgtgicz3yURiUbUrLTtdpfYppvXl7Yko7z+/FDEmglobfv2kofVzHO6GXqAvGf1WEEKb2aTL1Fumnh6fzb08xE7+I27SjtnTWsS72Ebn1zZZRQrVcp9suVdc1dIqkgn6tU6ijOwz9ZiEFd1FjQ0dvBG565eNj1Sd1QAQgAASBwHgLrmc9vzUkWS9I0r7SsJl2kzCdZsqnMa7VEphqstCFUkSZ6KizrQhKeLKp8/u376kuofqJn2jspRtzJe5iV2JLmnqFXMZ9NOIp63830ImIVuvAI1GtSsk5ZCg4NgXGdvKPGxv0+hTsDao3nCj5KgAAQ2BiB9cyna6k6thOeiAlUx3bgCfp9i+iwrh3//qnruUSmIhKzeZBpt4x4fnVhqjpUta2k76svGeiZ926SXavUzNDctSo/nfoSuTYD6c9mKcZ/5oSk0PUC25LouILD/I5i81b4fDVQEwgAASCwAQIbM5/82lR/jRO+8YnemMCte2TFcP6ab2H+JTrxi6f6+5anyVRPb1GlbZfxqQIv+9Lmw33RXiWRGZgvTBqsowmVSAf3E6m3zq6rntRXmTdQoS5epzuiu6e6sL5wAQSAABC4KgJbM98/WXzYhifnTU6L7jnGms1L4feXPr2SrbpCc5KsD3TYMtSIwa/5NH1P4Oskj3lXmIAtMj2pYde7k+aYJmtOKrlyY5eInpppYKrt+rimoMeaT6755jCfe9b08Pb6+aOEx21ndRTM1+YT4OMWEAACQOBKCKxjvjOVCzzhFkZniu2aN2s++vPET30bKrPBgh3aAgEgAASAwAoEnpr54u9SvKiqT22sAAtNgAAQAAJA4AkQeGrmc49WyuakPab/BJ6DCUAACAABILAOgZswX7ctiZ1AIAAEgAAQAALXQgDMBxoGAkAACACBfSEA5tuXv9ftDKAVEAACQOCZEADzgfmAABAAAkBgXwjckPn4zbD45jjNKeiBzKlzFXTeMWh+rW1iVeMm4XJt27O3Km9iODoFAkAACFwAgTXMp58jqd2vyoxp+qZCeyV8kl3S5lWlybZ5tVVW5KJW9J42Gah0edvT3q1woMa2tlvvuAACQAAIXBaBNcxHL8aFtdrb4VJviDfvnl93AXeH+f1OVLoTNS4b+pAGBIDAbhFYw3x8WIH7GEr9hL/7YFX5hlZ+XG39UpdWKw6I756n32J+9x/6qs1HB7Hy8qhsn/73O2Doqo/VZMmS8e2uLkadmWUGMOrd1axHH7hCP4Go27z09TLrVPd+y2cwa3m1PX697IRKWjk0T1dvTs/6uU6H54mOXPPTfaUKoBAIAAEgsAkCq5iPv0NtX2K0BQF9JLPkOMp6UqF8OZPLKWu7XE9/ak6sCT1wD+VZ5Rs+8Ufr+/McmDUlzwofx96lCa0mq6h0pmOG2N1U+exo2bx3b6DJzJrLr5tuMqGL3V4lkRMlV3vLV1LJ5Fwlbs7s5ZA03fyF86YdGbGgI9fct9okiL3auAYCQAAInERgHfPx55ULh9lWZ34YKWVAY7u4mRnTN+fEZs3njivig34CN8TmlF6NI/WWJ87T+benmQnlBVntKO19eAKDecU3t5mE3XWHobeEoQ0z0BjkyS9Wt9J8j+U6eqog03hnqqM8GJKOlONxCwgAASBwNQRWMl/d8LStTsqD5RTWskHHhNfTidkW0rdkwCa36jmr9NWxt29bIIqE2DzlHiosrUi9QJymhl30qvYlVLmxVBdYHe8SwRB3MiyV2JLmnqEDLeUKhAkBI2NzC0VsK+ab2VFjI06mBcEDASBwTwisZT7d8KypmZJd8j3oWqEzO1JXtnyhJoXS3g91STeb+SrxzDiiyPb0KvdkyhNLGY2pCSnvVjk4mdamF7gAAkAACNwcgdXMJxueP24h5RZYjuQy8iiUoLThGKJf88lq7/hz9KsNlh+bZ9wzIOMR6CQw9pIp79ZnsrKZXPNpX6Qe82XafLgv2qskAqPtnoytowyQ4heq3yygVU/zRdWT+sLJtC6kO6wMNFwAASDwGAisZ76y6ReogrKtPYsoC6OMPEI1qm9PW6TMF84cj4e1yv7qmHtss7HdcswTmVOMVcqUF5YSM3EybV2Ud5TswHSrZNAGEAACQODmCJzBfDl5bEH49hDNQuHNmo/+PPFT3839AQWAABAAAkBgawQegPlo3WaLwkV0G1eQvGuX/BK5NcSQDwSAABAAAneFwF0zX9mrDBuqy5Z9cbcTtLcMvbuKVCgDBIAAELgUAnfNfJcyEnKAABAAAkAACBgCYD4sg4AAEAACQGBfCID59uVvm/LgAggAASCwWwTAfGA+IAAEgAAQ2BcCj8t8/Dr2GQ+/PPJk59q2p+81PjKA+xrk8BQQAAINAiuZLz4zWb/m1Ui/yJ+DtHv57D/o6JZZcqDS5W2f9tRAjVsiM60w7gIBIAAEJhBYz3z27coJ6Re5dbW0e7WO5sNyJyrdiRrzcUNNIAAEgMAEApdjPv6IpdEhLwrl/Tn3FSv/Qjq9Zu7PMaBqzXEH8sVIq2bfOavlVeDobFheHmlHOJmWQ4ExqdCNlm7OcfXzYw7PstU8Qt41P93XSAeUAwEgAAQuj8DlmE8+LS2fB3MfTyEKLImPUmGhRqrQfEgsYT5h7NGCg/ivplTJsyKzdlR7Z2I2Zk3nAn1HZVOXe6HuONdnR8vmvUcNi/Oy5jiZ9vKRnboYhUAACACBv3/+rWc+vxQzRuFc/+FWb8kJpXLwkK0O1Q0XYL6oxpucDaSFlQ61xzbb5sxnD9E0XwHl76gpt+XK093s5CZTwDfvACH1epWkrTZkE9w8g+6ynna6U1J/zhfgorFFjQUdJX5/ndMv6gABIAAEtkdgPfOlmVoSbl2K8UrLc+Tv8SMSktFPTh5zs78e4xcTPcksypAmzSrTui4XPc30JUot9UgKlj+lvJhf4WowoQUlbSEqQwetcgVwMu32A0MCCf8DASDwlAhcmPk4U7/VXc24dFAEA0+khX5NMyv758yHk2kLj3o8FfBAsW1hdFxxQ5fNXgAAGXJJREFUQbPm4z/zxWVs3goHbwEBIAAEborARZnPMiMlvvqTW10CqqmUiNttQKLDsjAiObpWk/WNbTmqhL/NuidlvoX5l7SKHWWk69ZnJF/0DFye0YxZlzbHybSTNOycDhIFAkAACJyPwHrm83uYxFhMV7Zlx8+GOPLTpytt04/Jzz/bKT9Qccnx4z15eoVv0d4gsYjvfWK/sTyi0vU+AM5J5qdaMubDybQKPkOUr/mIqxyY9dFQMBwQAAJA4PYIrGS+AXPc3p6gWLPmqyvRO9MTaxogAASAABC4IgJPzXy2+8qAZlusoEAgAASAABDYHQJPzXz8VoDbF8XJtLuL77AHcMUZJfoFAkDgnhF4cua7Z+ihGxAAAkAACNwEATAflkFAAAgAASCwLwTAfPvy902mV+gUCAABIHBXCID5wHxAAAgAASCwLwTAfPvy911Nu6AMEAACQOAmCKxnvveDfGry7XDqe5g3MQydAgEgAASAABBIETiH+eQTLfGr/HhwHAgAASAABIDAfSOwivnku5r6STB5Yc4+S5YSLAqBABAAAkAACNwJAquYj8hcl3pv381Xnu/EMKgBBIAAEAACQCBF4Fzmy84lwDMjQAAIAAEgAATuF4E1zNccgMC7nfgw2P36OJ3yoBAIAAEgsFsE1jAfgaWbnOk5PrtFE4YDASAABIDA/SNwBvPpCXZ2Jt/9WwsNgQAQAAJAAAisZD79eY9OHwXzIYyAABAAAkDggRBYyXwPZCFUBQJAAAgAASDgEQDz4ckUIAAEgAAQ2BcCYL59+dvPenANBIAAENgnAmA+MB8QAAJAAAjsCwEw3778vc/5HawGAkAACHgEwHxgPiAABIAAENgXAmC+ffnbz3pwDQSAABDYJwJ7YL63w+9/d/NZ7Yk3IG+r5217x/wDCAABIHA9BNYz36yTaZPzjDb8wufgU2pn5fSBzLke6pqD+VZDN7fhPuewsBoIAIH5CJzDfPNPpqV0f4UD/DqauUCuPFNm13yC+S6g7XzH33/NDjrgAwSAABC4DAKrmC9ZyU0TW898vA6Ts22PH69Tp/dSWzn8NuxYOh2Ony/0NbV4Uu4vf1a0lvOflO6poVt31j/bjmpbk1yEuJomtjNh0JzaHt7M/KJJrRwEuo68zl1ff/+4mlXJn6+X2FE1lqNH/0x7F+KxW/qNOhNoG8gvX8eso8MHO+7n6+3jWHebZ+lpnVafBlhOhr6AXKqRtIqJxlLFs1fppHxUAAJA4LERWMV8i0+mpeTi1nz+T84747z2+vn99SIQu1aUsmXFGdAfrRJc7pMzdeu3Ru1W3tGff71MOqTJZVJnV1BGFlVdc7a3KO8sYjIzZaRt8+fEKi1TKe2IeEs57F8jv/mznEXFltKt44fwq9rL8ulu1hE5iDpiId/vdI4x+Wu2noRkB10C7wATUqk3szHQHDTfm4Pu5iuGmkAACNwFAucyX5pNugRBmUgzZrfqevk41gn4FCjaV5TmFkCjXKkNi3D3p6xX2k5dhT7/6mH00q+e1tSZXGR2KuVJWZr7fv/+YWaahUyikhDSZPZvbW96J5ay5Tj7qF0uSyEzX9uRwqLmE/P9Jf6Lq21l07Y5Y6ttW++MoHblOchkoFeAeklUmtyBWKEMmgABIHB3CKxhvrIasD1AunAZzVGRJqPIVZoWy91p5qO7tkMli62wcNEuCNlRrmxy+l/r8eXjGJJ701Ems9Gn7uPlru1UypOyWNHqyRbJjl+dN/TwZiqNmC+3nWU2vXea10MZvePyjtTFKoSZb5GeY296jw+uhyBb6BY8M5XAfANU8whHZSDwiAisYT6ys01t06OiYz7PlPprUwYfkZwlfU3NIa/5VppnW2W0oZUXCa+fPyo87YjqtzIpV07TvPWSNU9XSEpmnZ4mKqjnTabrXKWAkpPc2156cXVSzbvFOjtuIfP10I307JBXlFrzk/KhTG2reObQGey4AAJA4DkROIP5+BcgfbdhGh3KRMox5Zc2/bO51cihDFW2wmR6bj87ZfRDudvWcC4hNjldNhKPn29fR0vEeUdlyzHIJIX1l6FG2+TPTqWppNzrqZl6GqVUpWFHzPfe9qJ203tL+YSnEgZdm0pZR+3ESHY7F+uZeFPCIHikh910E7bu/WUVUpV6gSgBAkDgqRBYyXyaJSlx2I80mqZ7gCzR2C3KoeXJPSazUVvqqNT8/qoP6clvYEWCkmhJx04s5zXr6NdnQFbAdT3qSFK8kxl7cUvSgQlOB+ouIOZhVDPZKK1ZC52qWUeul6JS2pHg39ge2lKP3FfGfIX8ejQsBopFGfPxk0qhL3bchJ6ucjVfCm3KYhEVL4QgeS/6vYSNk6Y2MpKh3MVSFOgmUhn+qAwEgMAjIbCS+TD4gcCtEAiP3oCQgAAQAALLEQDzPdI85VZkcy/98ssSyRbo8ri/F4ugORAAArdAAMwH5gMCQAAIAIF9IQDm25e/sdYBAkAACAABMB+YDwgAASAABPaFAJhvX/7GXA8IAAEgAATAfGA+IAAEgAAQ2BcCYL59+RtzPSAABIAAEFjPfPr1lvIZfkAJBIAAEAACQOAhEDiH+eafTIt1FRAAAkAACACBe0FgFfPJC8X+q2CnP+J1LwY/xHwESgIBIAAEgMB2CKxiPnrlXg820y80bqciJAMBIAAEgAAQuCAC5zKffnMZSzogAASAABAAAo+BwBrms+M960kC2ZlBF+RniAICQAAIAAEgcCkE1jAf9a2bnIOzbB6D9i8FIuQAASAABIDAAyFwBvPpKW52NtsDmQ1VgQAQAAJAYLcIrGQ+/XmPTvUE8+02emA4EAACQOAREVjJfI9oKnQGAkAACAABIPD3zz8wH36SBAJAAAgAgX0hAObbl78x3QMCQAAIAAEwH5gPCAABIAAE9oUAmG9f/sZcDwgAASAABMB8YD4gAASAABDYFwJgvn35G3M9IAAEgAAQeGjm069m0xe0N/93o6/VvB1+//s9frxub+AVMEQXQAAIAIF7QGA98930ZFrmAz0m6Tqv0m/NfAP5ZzHfQObMWUICcvPJ1uPny9+Xj+Pvf7/8QR8KaP2sHV3LLaPt5HCr73didPoeQvkGbKnsSrzwWFMakg5uWnCeyTORQTUgAAQeG4FzmO9WJ9NSRqZ89/ZdE67LfT4PXvB665S6hfwzZBL3NKTy98+/RCDR28/xKBwWmO/18+f4+fZ1lDixcdJI9n8y4RGJ0oVOaNTdXKgqlZr0LSFjVoqBt8Nv0531iwsgAASAQEFgFfMlk/ckS16QdYIoTrVfL5RkNQ9OutNpq/XdaqYuMn6+Xqxc87hfZNTTdzntyoozpN1ejUHNqBKlb12/lgteQtVyv6Lyx2KQnHzZVNua5CLEqWRi+3mDgRxvDZjv+4tIjtdedc1HHR3e/jH/+WUZlasjAlOSl6nf78h8Wr9KZpylZqNnUycqH6IIt4AAENgxAquYj/DS39hukGskd/8cDpptJ/xH3NAsAjSTUisWRQRQZBKhurUFbe4pPVjSf/385mqhZppV85qJSpTKTX4jijhMdWDYbTFEpCK38o4ymc4ij8OIsxvoMiWZgd7/vB1kEmDxUMo7bnPw/v1TTVCrafIRmI/kFAZ1OEgElprGoyMMVXhvJkqAABDYIwLnMp8l3ysnF1vTWNbLFMiSe10nsb85QYdUa+nYcjcza5pV55uvNTOVxvJ7blA5wrstMzX1O511viJzBWMp+bP7vwe5+Z2PNiQVpfK7r8p0ejY7kAGBTkO6qxMR+fGv2JjWlDWlbng2He1xPGejADgAASDQIrCG+Zr0xxt0tj3YdrDZUHyhH5De4uMVbe6mBYT+XKSKaWouiq1gPmqiT2SE5y+0C1MjqZmpxPW7zF6kOQrhEmUaohzba006osqtzKbazKdGpRWvO1uBuj9JD6q8fBwPb/KESzOTiK0C87XW8Q91rnmtnNakxTepx+zYeNa8gAsgAASAQERgDfMRZ2iWiUmtS/2xs4uyYFm+UEI0Ami7o7yZMF/3U5lLtWRCSbKUUiujq6VEXbbQ7NKxRyCtmanEaqt8L8EpU00rEtzvZ2lH1LaVGS2a7w4DuRXomY+2MX++Pr9/jx/MRm5yECi2kpkPpKIMaxjcYWt0DTlf0x4NPX6+JLpV0FpU59uOmkAACDwfAmcwny4CWmq5TropKx5Ko/Hnn5DjKGU79mL/eZ6wLFzoRBysfEY1xTqWI4RXC0t+rz/Cha7Db3KU0IuemUrUkMozCldlqnB9ZtJYeahSJ/MEXIP4rq0SdvFs+vZ9PJIVNjEqAqmObcwa5mLRSXdY/bSmQffjuqhYDSxCBSAABHaNwErm03RMWenazEeLALeeGBJP8SupqvV1rUY51D9FGRYZtuaTpa3UPLzZGssJpGcaT/GudBRqOgl1+Sg6OK2Yb0xPJU5O5ay/M9wJDB11MvWhHhWrgHRjYABys9FNzT3z0V4lvXffEaSxV/pYUHWH6hPjipQR4uxrsuYyscjmDWA+IAAEgECPwErm6wVdu+S6L/Nd27rrrJvRCxAAAkBglwg8KvPZCgycBASAABAAAkBgEQKPynyLjERlIAAEgAAQAAKGAJiv+4lrl2t/CwhcAAEgAASeHgEwH5gPCAABIAAE9oUAmG9f/n76qRwMBAJAAAicRADMB+YDAkAACACBfSEA5tuXv09OhVABCAABIPD0CKxnvpueTAu6AgJAAAgAASCwEoFzmM8+q2Gf0VqpxNPPL2AgEAACQAAI3A8Cq5iv+bQVfwpLvzsF8gMCQAAIAAEgcNcIrGI+euNNT3prvqCPl+GAABAAAkAACNw3AucyH30r2X06+X4Ws9AECAABIAAEgECKwBrmaz7Yz8cL4Ke+u17ap75HIRAAAkBgnwisYT5CSjc5u/NoQABAAAgAASAABO4agTOYjzc59d2GuzZyn5MaWA0EgAAQAAIpAiuZT3/eiyeI3vdPmqn9KAQCQAAIAIG9IbCS+fYGE+wFAkAACACBp0EAzId9WiAABIAAENgXAmC+ffn7aaZsMAQIAAEgsBoBMB+YDwgAASAABPaFAJhvX/5ePUVCQyAABIDA0yAA5gPzAQEgAASAwL4QAPPty99PM2WDIUAACACB1Qg8EvNt/72Yt8Pvf7/Hj9c9vph4h7bfoUqYJwEBIPAMCKxnPv16y9vhVw7qy+BIzjNa/4XPCzLfQNRZqXYgM4Plpsw60PMs21fPvCYb3p1KA+juzsWTqEJbIAAE/p3DfPNPpqVPvZx/gN8F884FRVmW2UKmCb/gxaPoeUGTLyUK0F0KScgBArdFYBXzJSu5aWLrmY+n83yk7andRWrLx0HQ/0qfrrCekeQK/Y6l0/b4+ULfXZN+7X+WUMtNIDV0K9T6Z9tRbRtl/v3japrYfMHnahblX76OP18vBpTTJJHQN+dp3aa2z7eo1MwsqqiawmRphdT1IsRjtw5vMnU1iGynetDR4YNj6efr7eNYt7UddGM9rdMaPE6xGWOYelGF2bral4W3ubhXCZN0IAAELonAKuajzLvoZFoayUpa/4QP9E8e5OMkQici6V2bcbvCKvn18/vrRaCphXSmRLYZa6KanEUJTrtjG5NslXf0518vM9Wz6VH+zGQyMkV5Z1FCe/+y5nyexqVtn29RVjO1iHgrowRyZXQHIUzEww6iW8ePQSxlHfEk4PAmQr7fKYBp02K2nqRP7+LUm1khqdSb2RgoDTOVLjnmM/UgHwjsC4FzmS8dut3Qiom7mea/fBz90spn9nhL846SrtTU85J8p6pV7NdJVlGts7VhKXd/yjJiqn4n87SeXme71k7zXGnVRhe+uU4vgtqdnr2xVKJyZKaS7WxnyKtWie3CUpPZvwXZ6UAqESXYap5jg2c2tlT691cKeanddqTaqvnEfHUC52JppOdWzNcGfwLdLh+5CkGrcYVCIHAZBNYwX5l6284eXbjs4whG4zUykOagcjfSmzZh8+KtkrOo0DaI+EKyYVNOK4OwnvCSNf21IDapVjLp+x9OqSHnOgV0jdjKbPSp22ttp6RYU5lkLmG+pPkGtje9TFiU1RxaRJU5fl4+jgYyR1HjjhZhd06kj6W8I406FcLMt0jPDdZ8hc45nss0JVMJzOcHL66BwPkIrGE+6rXNI1k2rxTYMZ9nymYJWFuVKTwRD/8rOcsSpZbzXUr0tsTRjBnIw4Ol6a9VWxtaeZHw+vmjwtOOqH4rM9fTJPuLVGZQvlNsWfPL2D7forzmyKIe5GJdY3WLsMRhF0sLma+ftI307FwcItB7pL8eylTXaAzk0PUCUQIEgMB6BM5gPl7r6LsN0xrQsFfmkN8I7c/mViOH0oFsW1ESLNxGTdyvcdKk1izrJ1aPW/Wpjbfy4vJCElCTamXT7/j59nU0IXlHZXswyEz1bAycUJ6at1t2earNVdrA9vkWpTWHFvHEwoNcUGrckTAfL+s1tEg+X2cdtXM12e1crGfdbjVfEFe5bVgrDxemm/z+2gewVUhVSsMGhUAACKxEYCXzaUqiUWrZWWevvSo2qu0WJevymJzuFubN+cEEea7BLbw4O6gESXyc6EXm95d7UMWVG92Wp2ycAkGgPUbBKrGqTkknMHQkSw0nM/bilqS9pZnMgC1VcDo0ErLmBLUrv4ztwcZJi7KaExY1IOfuyJivTKR62C0sC3QZ8/EjUaGvIXEWGnOVqzuk0OZGFuTxQgiSOfK9eNNJ0yd32LOhXHk9Sgu0iltAAAgsQ2Al8zWZF38CgT0jEB69AScBASBw9wiA+ZbNFPac32F7goDsSYRdbkQUEAAC944AmO/ePZRk27ufT0FnIAAEgMA9IwDmA/MBASAABIDAvhAA8+3L3/c8C4NuQAAIAIHrIADmA/MBASAABIDAvhAA8+3L39eZT6EXIAAEgMA9IwDmA/MBASAABIDAvhBYz3z69Zbyzft7pnfoBgSAABAAAkDAEDiH+bLv9+OBeyAABIAAEAAC943AKuazL4rp98P0i5r7Wi/b9AEXQAAIAAEg8EAIrGI+InM9RUw/h/hANkNVIAAEgAAQ2DMC5zLf9JeU94wsbAcCQAAIAIH7RGAN8y0/mRa7oEAACAABIAAE7gWBNcxHHK6bnIODY+7FvPucbkArIAAEgAAQuCECZzAfn0+m7zaA6oAAEAACQAAIPAYCK5lPf96jIzTtCNAbEji6BgJAAAgAASAwE4GVzDdTOqoBASAABIAAELg3BMB8j7E2v7e4gT5AAAgAgcdFAMwH5gMCQAAIAIF9IQDm25e/H3eOBs2BABAAApdCAMwH5gMCQAAIAIF9IQDm25e/LzVjghwgAASAwOMiAOYD8wEBIAAEgMC+EHho5tOvZl/lOIwzv1ZzZvMZc6u3w+9/v8eP16ugMUOffQ0kAAIEgMADIbCe+fTrLTc5mZazvJ6RdJ1X6c+krjOb+5AaiDqL+QYywV5AAAgAgSdE4Bzmu9XJtJTij58v9O1Q/oKaZ4Xtrs/khjObe7suKMrEbiHThOMCCAABIHBXCKxivtueTPvycfxl0n37Jv47ubnntNX6bslYtgdfvo4/Xy9W/v1exNLn2X51canNXeGJ3UVXU9iaxLrCytyu0MuMytNH41SZcsESarkJpIZmBX9hvPzZdlTbmuQixNU0sSfRRgUgAASAwN0jsIr5yCr9jU0PbTjNQBfDQjLyz+Hwo1Q0XowTAcja1OpQc23IoiitF5lfL4WZpAKdx6RJ31ZFr5/fXC3UTM1Pm7vCqkkuM1GerDBNmk71Y6piKbG47QPbrbyjTGaqZ9Mj/gQCQAAIPCIC5zKfpdQrG28rFeUwIzZ/UamlqtcshmgF+S3M1/IE39LFX843U+bnzXXGIPOAbN6gMjPludU85vuncoShG/onlFyF3rrTelZILzan8b7DNRAAAkBgKwTWMB+tBmxnrFy4jbUr5UHen3z7OP7WZVmXi8O6p9xtyGYF81ETh4AuCtveU+Zr2trTmE05ycyUX8J8f02Bl4+j7aAmHVFstWzaVDM9r+TcrcK99RHMAQJAYJcIrGE+Sh/KH23GvB6IZVFCCxdL623vtGyylVxlvu4HsCVrPiIkW2j6ZVObVY14WKsCVCzUJqnMTHkvqjU2rOFYcpHw+mnbwmlHI+a7/mwGhAcEgAAQuAYCZzAfr3X03YZr6Ko8wX2VdQwld/spLlRgYiBm8jxHhT77U3OmsUAzymdUU4iT5UjNWsiLqonea03XPFW41vQyM+XJdirPyJ7K4wKUOe/t62gclneUyUz1vLqXO3bvXYwSIAAEgMBSBFYynybZQBhL+15Zn36om7HZqEmT+aPU17UaEYB/NjJf88nSVmoe3mzl5AR+f3VkE4wyVV1zfZomqDSS6crrQjNIIKpjljKLwvYvW+ro0AlslHdCSn1X4pa5wUAFGYVAAAgAgQdCYCXz3d7C677Md3t7wTFAAAgAASBwIQQelflsBQZOAgJAAAgAASCwCIFHZb5FRqIyEAACQAAIAAFDAMyHpzaAABAAAkBgXwiA+fblb5vy4AIIAAEgsFsEwHxgPiAABIAAENgXAmC+ffl7t1M8GA4EgAAQMATAfGA+IAAEgAAQ2BcCYL59+dumPLgAAkAACOwWATAfmA8IAAEgAAT2hQCYb1/+3u0UD4YDASAABAwBMB+YDwgAASAABPaFwP8HlpdPi7gK7koAAAAASUVORK5CYII=" + }, + "image-5.png": { + "image/png": "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" + }, + "image.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "1890e044", + "metadata": {}, + "source": [ + "1. Что делает команда python -m venv venv?\n", + "Создает виртуальное окружение и папку venv для хранения соответствующих ресурсов.\n", + "\n", + "1.1 Что делает каждая команда в списке ниже?\n", + "pip list - выводит список всех установленных пакетов в текущем окружении (виртуальном или глобальном) вместе с их версиями.\n", + "\n", + "pip freeze > requirements.txt - сохранить список зависимостей с версиями из текущего окружения в файл requirements.txt\n", + "\n", + "pip install -r requirements.txt - установить зависимости из файла requirements.txt\n", + "\n", + "2. Что делает каждая команда в списке ниже?\n", + "conda env list - выводит список всех виртуальных окружений Conda, доступных на вашем компьютере.\n", + "\n", + "conda create -n env_name python=3.5 - создает новое виртуальное окружение с именем env_name и устанавливает в него Python 3.5.\n", + "\n", + "conda env update -n env_name -f file.yml - Conda добавит или обновит пакеты в указанном окружении в соответствии с содержимым файла file.yml.\n", + "\n", + "source activate env_name - устаревший способ активации окружения Conda, который больше не рекомендуется использовать. Рекомендовано conda activate env_name.\n", + "\n", + "source deactivate - устаревший способ деактивации окружения Conda, который больше не рекомендуется использовать. Рекомендовано conda deactivate.\n", + "\n", + "conda clean -a - полностью очищает кэш Conda, удаляя все ненужные файлы.\n", + "\n", + "3. вставьте скрин вашего терминала, где вы активировали сначала venv, потом conda, назовите окружение \"SENATOROV\"\n", + "![image-3.png](attachment:image-3.png)\n", + "![image-4.png](attachment:image-4.png)\n", + "![image-5.png](attachment:image-5.png)\n", + "\n", + "4. Как установить необходимые пакеты внутрь виртуального окружения для conda/venv?\n", + "conda install package-name\n", + "\n", + "5. Что делают эти команды?\n", + "pip freeze > requirements.txt\n", + "conda env export > environment.yml\n", + "Сохраняют список зависимостей в файлы requirements.txt и environment.yml\n", + "\n", + "5.1. вставьте скрин, где будет видна папка VENV в вашем репозитории а также файлы зависимостей requirements.txt и environment.yml, файлы должны содержать зависимости\n", + "![image.png](attachment:image.png)\n", + "\n", + "6. Что делают эти команды?\n", + "pip install -r requirements.txt - устанавливает зависимости из файла requirements.txt в текущее виртуальное окружение.\n", + "conda env create -f environment.yml - создает новое виртуальное окружение Conda на основе конфигурации из файла environment.yml.\n", + "\n", + "7. Что делают эти команды?\n", + "pip list - выводит список установленных зависимостей в текущем окружении (виртуальном или глобальном).\n", + "pip show package name - выводит подробную информацию об установленном пакете.\n", + "conda list - выводит список установленных зависимостей в текущем окружении.\n", + "\n", + "8. Где по умолчанию больше пакетов venv/pip или conda? и почему дата сайнинисты используют conda?\n", + "По умолчанию пакетов больше в pip.\n", + "Дата-сайнтисты используют Conda из-за:\n", + "- Наличия предустановленных пакетов для data science\n", + "- Кроссплатформенности\n", + "- Управления не-Python зависимостями\n", + "- Более надежное разрешение зависимостей\n", + "\n", + "9. вставьте скрин где будет видно, Выбор интерпретатора Python (conda) в VS Code/cursor\n", + "![image-2.png](attachment:image-2.png)\n", + "\n", + "10. добавьте в .gitignore папку SENATOROV\n", + "Добавил\n", + "\n", + "11. Зачем нужно виртуально окружение?\n", + "Виртуальное окружение — это изолированное пространство для Python-проекта, в котором устанавливаются фиксированные версии Python и библиотек, независимо от других проектов и системы в целом.\n", + "\n", + "12. С этого момента надо работать в виртуальном окружении conda, ты научился(-ась) выгружать зависимости и работать с окружением?\n", + "Да.\n", + "\n", + "13. Удалите папку VENV, она больше не нужна, мы же не разрабы, нам нужна только conda\n", + "Удалил" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/venv.py b/Python/venv.py new file mode 100644 index 00000000..8150b98e --- /dev/null +++ b/Python/venv.py @@ -0,0 +1,75 @@ +"""Issue #7 Виртуальное окружение. + +https://github.com/SENATOROVAI/intro-cs/issues/4 +""" + +# 1. Что делает команда python -m venv venv? +# Создает виртуальное окружение и папку venv для хранения соответствующих ресурсов. +# +# 1.1 Что делает каждая команда в списке ниже? +# pip list - выводит список всех установленных пакетов в текущем окружении (виртуальном или глобальном) вместе с их версиями. +# +# pip freeze > requirements.txt - сохранить список зависимостей с версиями из текущего окружения в файл requirements.txt +# +# pip install -r requirements.txt - установить зависимости из файла requirements.txt +# +# 2. Что делает каждая команда в списке ниже? +# conda env list - выводит список всех виртуальных окружений Conda, доступных на вашем компьютере. +# +# conda create -n env_name python=3.5 - создает новое виртуальное окружение с именем env_name и устанавливает в него Python 3.5. +# +# conda env update -n env_name -f file.yml - Conda добавит или обновит пакеты в указанном окружении в соответствии с содержимым файла file.yml. +# +# source activate env_name - устаревший способ активации окружения Conda, который больше не рекомендуется использовать. Рекомендовано conda activate env_name. +# +# source deactivate - устаревший способ деактивации окружения Conda, который больше не рекомендуется использовать. Рекомендовано conda deactivate. +# +# conda clean -a - полностью очищает кэш Conda, удаляя все ненужные файлы. +# +# 3. вставьте скрин вашего терминала, где вы активировали сначала venv, потом conda, назовите окружение "SENATOROV" +# ![image-3.png](attachment:image-3.png) +# ![image-4.png](attachment:image-4.png) +# ![image-5.png](attachment:image-5.png) +# +# 4. Как установить необходимые пакеты внутрь виртуального окружения для conda/venv? +# conda install package-name +# +# 5. Что делают эти команды? +# pip freeze > requirements.txt +# conda env export > environment.yml +# Сохраняют список зависимостей в файлы requirements.txt и environment.yml +# +# 5.1. вставьте скрин, где будет видна папка VENV в вашем репозитории а также файлы зависимостей requirements.txt и environment.yml, файлы должны содержать зависимости +# ![image.png](attachment:image.png) +# +# 6. Что делают эти команды? +# pip install -r requirements.txt - устанавливает зависимости из файла requirements.txt в текущее виртуальное окружение. +# conda env create -f environment.yml - создает новое виртуальное окружение Conda на основе конфигурации из файла environment.yml. +# +# 7. Что делают эти команды? +# pip list - выводит список установленных зависимостей в текущем окружении (виртуальном или глобальном). +# pip show package name - выводит подробную информацию об установленном пакете. +# conda list - выводит список установленных зависимостей в текущем окружении. +# +# 8. Где по умолчанию больше пакетов venv/pip или conda? и почему дата сайнинисты используют conda? +# По умолчанию пакетов больше в pip. +# Дата-сайнтисты используют Conda из-за: +# - Наличия предустановленных пакетов для data science +# - Кроссплатформенности +# - Управления не-Python зависимостями +# - Более надежное разрешение зависимостей +# +# 9. вставьте скрин где будет видно, Выбор интерпретатора Python (conda) в VS Code/cursor +# ![image-2.png](attachment:image-2.png) +# +# 10. добавьте в .gitignore папку SENATOROV +# Добавил +# +# 11. Зачем нужно виртуально окружение? +# Виртуальное окружение — это изолированное пространство для Python-проекта, в котором устанавливаются фиксированные версии Python и библиотек, независимо от других проектов и системы в целом. +# +# 12. С этого момента надо работать в виртуальном окружении conda, ты научился(-ась) выгружать зависимости и работать с окружением? +# Да. +# +# 13. Удалите папку VENV, она больше не нужна, мы же не разрабы, нам нужна только conda +# Удалил diff --git a/Python/yandex-python-handbook/chapter_2_1.ipynb b/Python/yandex-python-handbook/chapter_2_1.ipynb new file mode 100644 index 00000000..5dc7cfc7 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_1.ipynb @@ -0,0 +1,551 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 2.1 Ввод и вывод данных.\n", + "\n", + "Операции с числами, строками.\n", + "Форматирование. из хендбука Яндекс Основы Python.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Привет, Яндекс!\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "name = input(\"Как Вас зовут?\")\n", + "print(f\"Привет, {name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "string = input()\n", + "for _ in range(3):\n", + " print(string)" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "money = int(input())\n", + "\n", + "price = 38 * 5 // 2\n", + "\n", + "rest = money - price\n", + "\n", + "print(rest)" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "price = int(input())\n", + "weight = int(input())\n", + "money = int(input())\n", + "\n", + "print(money - price * weight)" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "title = input()\n", + "price = int(input())\n", + "weight = int(input())\n", + "money = int(input())\n", + "\n", + "total_price = price * weight\n", + "rest = money - total_price\n", + "\n", + "print(\"Чек\")\n", + "print(f\"{title} - {weight}кг - {price}руб/кг\")\n", + "print(f\"Итого: {total_price}руб\")\n", + "print(f\"Внесено: {money}руб\")\n", + "print(f\"Сдача: {rest}руб\")" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "for _ in range(num):\n", + " print(\"Купи слона!\")" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "title = input()\n", + "\n", + "for _ in range(num):\n", + " print(f'Я больше никогда не буду писать \"{title}\"!')" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "children = int(input())\n", + "minutes = int(input())\n", + "\n", + "child_eat_per_minute = 2 / 2 / 2\n", + "\n", + "result = int(children * minutes * child_eat_per_minute)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "name = input()\n", + "locker_number = int(input())\n", + "\n", + "group_number = locker_number // 100\n", + "child_number = locker_number % 10\n", + "bed_number = (locker_number // 10) % 10\n", + "\n", + "\n", + "print(\"Группа №{group_number}.\")\n", + "print(\"{child_number}. {name}.\")\n", + "print(\"Шкафчик: {locker_number}.\")\n", + "print(\"Кроватка: {bed_number}.\")" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "number = int(input())\n", + "\n", + "first = number // 1000\n", + "second = (number // 100) % 10\n", + "third = (number // 10) % 10\n", + "fourth = number % 10\n", + "\n", + "result = second * 1000 + first * 100 + fourth * 10 + third\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "first_value = input().zfill(3)\n", + "second_value = input().zfill(3)\n", + "\n", + "result_str: str = \"\"\n", + "\n", + "for index in range(2, -1, -1):\n", + " current_sum = (int(first_value[index]) + int(second_value[index])) % 10\n", + " result_str = f\"{current_sum}{result}\"\n", + "\n", + "print(result_str.lstrip(\"0\"))" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "children = int(input())\n", + "candies = int(input())\n", + "\n", + "for_each = candies // children\n", + "rest = candies % children\n", + "\n", + "print(for_each)\n", + "print(rest)" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "red = int(input())\n", + "green = int(input())\n", + "blue = int(input())\n", + "\n", + "print(red + blue + 1)" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "hours = int(input())\n", + "minutes = int(input())\n", + "time = int(input())\n", + "\n", + "time_hours = time // 60\n", + "time_minutes = time % 60\n", + "\n", + "result_minutes = (minutes + time_minutes) % 60\n", + "result_hours = (hours + time_hours + (minutes + time_minutes) // 60) % 24\n", + "\n", + "print(f\"{str(result_hours).zfill(2)}:{str(result_minutes).zfill(2)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "start = int(input())\n", + "finish = int(input())\n", + "speed = int(input())\n", + "\n", + "result_time: float = (finish - start) / speed\n", + "\n", + "print(f\"{result_time:.2f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "rest = int(input())\n", + "last = int(input(), 2)\n", + "\n", + "print(rest + last)" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "price = int(input(), 2)\n", + "money = int(input())\n", + "\n", + "print(money - price)" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "title = input()\n", + "price = int(input())\n", + "weight = int(input())\n", + "money = int(input())\n", + "\n", + "total_price = weight * price\n", + "change = money - total_price\n", + "\n", + "weight_with_price_display = f\"{weight}кг * {price}руб/кг\"\n", + "total_price_display = f\"{total_price}руб\"\n", + "money_display = f\"{money}руб\"\n", + "change_display = f\"{change}руб\"\n", + "\n", + "LENGTH = 35\n", + "\n", + "line1 = \"Чек\".center(LENGTH, \"=\")\n", + "line2 = f\"{'Товар:': <10}{title: >25}\"\n", + "line3 = f\"{'Цена:': <10}{weight_with_price_display: >25}\"\n", + "line4 = f\"{'Итого:': <10}{total_price_display: >25}\"\n", + "line5 = f\"{'Внесено:': <10}{money_display: >25}\"\n", + "line6 = f\"{'Сдача:': <10}{change_display: >25}\"\n", + "line7 = \"=\" * LENGTH\n", + "\n", + "print(line1)\n", + "print(line2)\n", + "print(line3)\n", + "print(line4)\n", + "print(line5)\n", + "print(line6)\n", + "print(line7)" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "weight = int(input())\n", + "price = int(input())\n", + "price1 = int(input())\n", + "price2 = int(input())\n", + "\n", + "# Составим систему уравнений с двумя неизвестными weight1 и weight2\n", + "# weight1 * price1 + weight2 * price2 = weight * price\n", + "# weight1 + weight2 = weight\n", + "#\n", + "# weight1 = weight - weight2\n", + "#\n", + "# (weight - weight2) * price1 + weight2 * price2 = weight * price\n", + "# weight * price1 - weight2 * price1 + weight2 * price2 = weight * price\n", + "# weight2 * (price2 - price1) = weight * price - weight * price1\n", + "# weight2 = (weight * price - weight * price1) / (price2 - price1)\n", + "\n", + "weight2 = (weight * price - weight * price1) / (price2 - price1)\n", + "\n", + "weight1 = weight - weight2\n", + "\n", + "\n", + "print(int(weight1), int(weight2))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_2_1.py b/Python/yandex-python-handbook/chapter_2_1.py new file mode 100644 index 00000000..250a42a6 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_1.py @@ -0,0 +1,268 @@ +"""Задания к главе 2.1 Ввод и вывод данных. + +Операции с числами, строками. Форматирование. из хендбука Яндекс Основы Python. +""" + +# A + +print("Привет, Яндекс!") + +# B + +name = input("Как Вас зовут?") +print(f"Привет, {name}") + +# C + +string = input() +for _ in range(3): + print(string) + +# D + +# + +money = int(input()) + +price = 38 * 5 // 2 + +rest = money - price + +print(rest) +# - + +# E + +# + +price = int(input()) +weight = int(input()) +money = int(input()) + +print(money - price * weight) +# - + +# F + +# + +title = input() +price = int(input()) +weight = int(input()) +money = int(input()) + +total_price = price * weight +rest = money - total_price + +print("Чек") +print(f"{title} - {weight}кг - {price}руб/кг") +print(f"Итого: {total_price}руб") +print(f"Внесено: {money}руб") +print(f"Сдача: {rest}руб") +# - + +# G + +# + +num = int(input()) + +for _ in range(num): + print("Купи слона!") +# - + +# H + +# + +num = int(input()) + +title = input() + +for _ in range(num): + print(f'Я больше никогда не буду писать "{title}"!') +# - + +# I + +# + +children = int(input()) +minutes = int(input()) + +child_eat_per_minute = 2 / 2 / 2 + +result = int(children * minutes * child_eat_per_minute) +print(result) +# - + +# J + +# + +name = input() +locker_number = int(input()) + +group_number = locker_number // 100 +child_number = locker_number % 10 +bed_number = (locker_number // 10) % 10 + + +print("Группа №{group_number}.") +print("{child_number}. {name}.") +print("Шкафчик: {locker_number}.") +print("Кроватка: {bed_number}.") +# - + +# K + +# + +number = int(input()) + +first = number // 1000 +second = (number // 100) % 10 +third = (number // 10) % 10 +fourth = number % 10 + +result = second * 1000 + first * 100 + fourth * 10 + third + +print(result) +# - + +# L + +# + +first_value = input().zfill(3) +second_value = input().zfill(3) + +result_str: str = "" + +for index in range(2, -1, -1): + current_sum = (int(first_value[index]) + int(second_value[index])) % 10 + result_str = f"{current_sum}{result}" + +print(result_str.lstrip("0")) +# - + +# M + +# + +children = int(input()) +candies = int(input()) + +for_each = candies // children +rest = candies % children + +print(for_each) +print(rest) +# - + +# N + +# + +red = int(input()) +green = int(input()) +blue = int(input()) + +print(red + blue + 1) +# - + +# O + +# + +hours = int(input()) +minutes = int(input()) +time = int(input()) + +time_hours = time // 60 +time_minutes = time % 60 + +result_minutes = (minutes + time_minutes) % 60 +result_hours = (hours + time_hours + (minutes + time_minutes) // 60) % 24 + +print(f"{str(result_hours).zfill(2)}:{str(result_minutes).zfill(2)}") +# - + +# P + +# + +start = int(input()) +finish = int(input()) +speed = int(input()) + +result_time: float = (finish - start) / speed + +print(f"{result_time:.2f}") +# - + +# Q + +# + +rest = int(input()) +last = int(input(), 2) + +print(rest + last) +# - + +# R + +# + +price = int(input(), 2) +money = int(input()) + +print(money - price) +# - + +# S + +# + +title = input() +price = int(input()) +weight = int(input()) +money = int(input()) + +total_price = weight * price +change = money - total_price + +weight_with_price_display = f"{weight}кг * {price}руб/кг" +total_price_display = f"{total_price}руб" +money_display = f"{money}руб" +change_display = f"{change}руб" + +LENGTH = 35 + +line1 = "Чек".center(LENGTH, "=") +line2 = f"{'Товар:': <10}{title: >25}" +line3 = f"{'Цена:': <10}{weight_with_price_display: >25}" +line4 = f"{'Итого:': <10}{total_price_display: >25}" +line5 = f"{'Внесено:': <10}{money_display: >25}" +line6 = f"{'Сдача:': <10}{change_display: >25}" +line7 = "=" * LENGTH + +print(line1) +print(line2) +print(line3) +print(line4) +print(line5) +print(line6) +print(line7) +# - + +# T + +# + +weight = int(input()) +price = int(input()) +price1 = int(input()) +price2 = int(input()) + +# Составим систему уравнений с двумя неизвестными weight1 и weight2 +# weight1 * price1 + weight2 * price2 = weight * price +# weight1 + weight2 = weight +# +# weight1 = weight - weight2 +# +# (weight - weight2) * price1 + weight2 * price2 = weight * price +# weight * price1 - weight2 * price1 + weight2 * price2 = weight * price +# weight2 * (price2 - price1) = weight * price - weight * price1 +# weight2 = (weight * price - weight * price1) / (price2 - price1) + +weight2 = (weight * price - weight * price1) / (price2 - price1) + +weight1 = weight - weight2 + + +print(int(weight1), int(weight2)) diff --git a/Python/yandex-python-handbook/chapter_2_2.ipynb b/Python/yandex-python-handbook/chapter_2_2.ipynb new file mode 100644 index 00000000..44497a18 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_2.ipynb @@ -0,0 +1,636 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "82e62497", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nЗадания к главе 2.2 \"Условный оператор\".\\n\\nХендбук Яндекс \"Основы Python\".\\n'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\"\"\n", + "Задания к главе 2.2 \"Условный оператор\".\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Здравствуйте, !\n", + "Всё наладится!\n" + ] + } + ], + "source": [ + "from typing import TypedDict\n", + "\n", + "name = input(\"Как Вас зовут?\\n\")\n", + "print(f\"Здравствуйте, {name}!\")\n", + "answer = input(\"Как дела?\\n\")\n", + "\n", + "if answer == \"хорошо\":\n", + " print(\"Я за Вас рада!\")\n", + "else:\n", + " print(\"Всё наладится!\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "dc34bc16", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "invalid literal for int() with base 10: ''", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m petya = \u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2\u001b[39m vasya = \u001b[38;5;28mint\u001b[39m(\u001b[38;5;28minput\u001b[39m())\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m petya > vasya:\n", + "\u001b[31mValueError\u001b[39m: invalid literal for int() with base 10: ''" + ] + } + ], + "source": [ + "petya = int(input())\n", + "vasya = int(input())\n", + "\n", + "if petya > vasya:\n", + " print(\"Петя\")\n", + "else:\n", + " print(\"Вася\")" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "petya = int(input())\n", + "vasya = int(input())\n", + "tolya = int(input())\n", + "\n", + "if petya > vasya and petya > tolya:\n", + " print(\"Петя\")\n", + "elif vasya > tolya:\n", + " print(\"Вася\")\n", + "else:\n", + " print(\"Толя\")" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "class Item(TypedDict):\n", + " \"\"\"Type array items.\"\"\"\n", + "\n", + " name: str\n", + " value: int\n", + "\n", + "\n", + "petya = int(input())\n", + "vasya = int(input())\n", + "tolya = int(input())\n", + "\n", + "first_speed = max(petya, vasya, tolya)\n", + "third_speed = min(petya, vasya, tolya)\n", + "\n", + "array: list[Item] = [\n", + " {\"name\": \"Петя\", \"value\": petya},\n", + " {\"name\": \"Вася\", \"value\": vasya},\n", + " {\"name\": \"Толя\", \"value\": tolya},\n", + "]\n", + "\n", + "array.sort(key=lambda obj: obj[\"value\"], reverse=True)\n", + "\n", + "for count, obj in enumerate(array):\n", + " print(f\"{count + 1}. {obj['name']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "added_petya = int(input())\n", + "added_vasya = int(input())\n", + "\n", + "if added_petya + 6 > added_vasya + 12:\n", + " print(\"Петя\")\n", + "else:\n", + " print(\"Вася\")" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "year = int(input())\n", + "\n", + "if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "value = input()\n", + "reversed_value = value[::-1]\n", + "\n", + "if reversed_value == value:\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "string = input()\n", + "\n", + "if \"зайка\" in string:\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "first_value, second_value, third_value = input(), input(), input()\n", + "\n", + "my_array = [first_value, second_value, third_value]\n", + "my_array.sort()\n", + "\n", + "print(my_array[0])" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "value = input()\n", + "\n", + "sum1 = int(value[1]) + int(value[2])\n", + "sum2 = int(value[0]) + int(value[1])\n", + "\n", + "if sum1 >= sum2:\n", + " print(str(sum1) + str(sum2))\n", + "else:\n", + " print(str(sum2) + str(sum1))" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "value = input()\n", + "\n", + "digits = list(map(int, value))\n", + "digits.sort()\n", + "\n", + "if digits[0] + digits[2] == digits[1] * 2:\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "line1 = int(input())\n", + "line2 = int(input())\n", + "line3 = int(input())\n", + "\n", + "if all([line1 + line2 > line3, line1 + line3 > line2, line2 + line3 > line1]):\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "line_1 = input()\n", + "line_2 = input()\n", + "line_3 = input()\n", + "\n", + "for index, _ in enumerate(line_1):\n", + " if line_1[index] == line_2[index] == line_3[index]:\n", + " print(line_1[index])" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "integers_list = list(map(int, input()))\n", + "integers_list.sort()\n", + "\n", + "first = \"0\"\n", + "second = \"0\"\n", + "\n", + "if integers_list[0] != 0:\n", + " first = str(integers_list[0]) + str(integers_list[1])\n", + "elif integers_list[1] != 0:\n", + " first = str(integers_list[1]) + str(integers_list[0])\n", + "elif integers_list[2] != 0:\n", + " first = str(integers_list[2]) + str(integers_list[0])\n", + "\n", + "if integers_list[2] != 0:\n", + " second = str(integers_list[2]) + str(integers_list[1])\n", + "\n", + "print(first, second)" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "value1 = list(map(int, input()))\n", + "value2 = list(map(int, input()))\n", + "combined = value1 + value2\n", + "\n", + "combined.sort(reverse=True)\n", + "\n", + "\n", + "result = f\"{combined[0]}{(combined[1] + combined[2]) % 10}{combined[3]}\"\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "vasya_p = \"Вася\"\n", + "tolya_p = \"Толя\"\n", + "petya_p = \"Петя\"\n", + "\n", + "\n", + "p_s = int(input())\n", + "v_s = int(input())\n", + "t_s = int(input())\n", + "\n", + "if v_s > t_s > p_s:\n", + " first = vasya_p\n", + " second = tolya_p\n", + " third = petya_p\n", + "elif v_s > p_s > t_s:\n", + " first = vasya_p\n", + " second = petya_p\n", + " third = tolya_p\n", + "elif p_s > v_s > t_s:\n", + " first = petya_p\n", + " second = vasya_p\n", + " third = tolya_p\n", + "elif p_s > t_s > v_s:\n", + " first = petya_p\n", + " second = tolya_p\n", + " third = vasya_p\n", + "elif t_s > p_s > v_s:\n", + " first = tolya_p\n", + " second = petya_p\n", + " third = vasya_p\n", + "else:\n", + " first = tolya_p\n", + " second = vasya_p\n", + " third = petya_p\n", + "\n", + "\n", + "print(f'{\"\": ^8}{first: ^8}{\"\": ^8}')\n", + "print(f'{second: ^8}{\"\": ^8}{\"\": ^8}')\n", + "print(f'{\"\": ^8}{\"\": ^8}{third: ^8}')\n", + "print(f'{\"II\": ^8}{\"I\": ^8}{\"III\": ^8}')" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "a_coef = float(input())\n", + "b_coef = float(input())\n", + "c_coef = float(input())\n", + "\n", + "\n", + "if a_coef == b_coef == c_coef == 0:\n", + " print(\"Infinite solutions\")\n", + "elif a_coef == b_coef == 0:\n", + " print(\"No solution\")\n", + "elif a_coef == 0:\n", + " result_q = -c_coef / b_coef\n", + "\n", + " print(round(result_q, 2))\n", + "else:\n", + " discriminant = b_coef**2 - 4 * a_coef * c_coef\n", + "\n", + " if discriminant < 0:\n", + " print(\"No solution\")\n", + " elif discriminant == 0:\n", + " result_q = -b_coef / (2 * a_coef)\n", + "\n", + " print(round(result_q, 2))\n", + " else:\n", + " result1 = (-b_coef + discriminant**0.5) / (2 * a_coef)\n", + " result2 = (-b_coef - discriminant**0.5) / (2 * a_coef)\n", + "\n", + " min_result = min(round(result1, 2), round(result2, 2))\n", + " max_result = max(round(result1, 2), round(result2, 2))\n", + "\n", + " print(min_result, max_result)" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "a_length = float(input())\n", + "b_length = float(input())\n", + "c_length = float(input())\n", + "\n", + "lengths = [a_length, b_length, c_length]\n", + "lengths.sort(reverse=True)\n", + "\n", + "longest = lengths[0]\n", + "other1, other2 = lengths[1:]\n", + "\n", + "if longest**2 == other1**2 + other2**2:\n", + " print(\"100%\")\n", + "elif longest**2 > other1**2 + other2**2:\n", + " print(\"велика\")\n", + "else:\n", + " print(\"крайне мала\")" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "a_str = input()\n", + "b_str = input()\n", + "c_str = input()\n", + "\n", + "results = []\n", + "\n", + "for string in [a_str, b_str, c_str]:\n", + " if \"зайка\" in string:\n", + " results.append(string)\n", + "\n", + "results.sort()\n", + "result = results[0]\n", + "\n", + "print(result, len(result))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_2_2.py b/Python/yandex-python-handbook/chapter_2_2.py new file mode 100644 index 00000000..fea3e572 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_2.py @@ -0,0 +1,343 @@ +"""Задания к главе 2.2 "Условный оператор". + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from typing import TypedDict + +name = input("Как Вас зовут?\n") +print(f"Здравствуйте, {name}!") +answer = input("Как дела?\n") + +if answer == "хорошо": + print("Я за Вас рада!") +else: + print("Всё наладится!") +# - + +# B + +# + +petya = int(input()) +vasya = int(input()) + +if petya > vasya: + print("Петя") +else: + print("Вася") +# - + +# C + +# + +petya = int(input()) +vasya = int(input()) +tolya = int(input()) + +if petya > vasya and petya > tolya: + print("Петя") +elif vasya > tolya: + print("Вася") +else: + print("Толя") + + +# - + +# D + + +# + +class Item(TypedDict): + """Type array items.""" + + name: str + value: int + + +petya = int(input()) +vasya = int(input()) +tolya = int(input()) + +first_speed = max(petya, vasya, tolya) +third_speed = min(petya, vasya, tolya) + +array: list[Item] = [ + {"name": "Петя", "value": petya}, + {"name": "Вася", "value": vasya}, + {"name": "Толя", "value": tolya}, +] + +array.sort(key=lambda obj: obj["value"], reverse=True) + +for count, obj in enumerate(array): + print(f"{count + 1}. {obj['name']}") +# - + +# E + +# + +added_petya = int(input()) +added_vasya = int(input()) + +if added_petya + 6 > added_vasya + 12: + print("Петя") +else: + print("Вася") +# - + +# F + +# + +year = int(input()) + +if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0): + print("YES") +else: + print("NO") +# - + +# G + +# + +value = input() +reversed_value = value[::-1] + +if reversed_value == value: + print("YES") +else: + print("NO") +# - + +# H + +# + +string = input() + +if "зайка" in string: + print("YES") +else: + print("NO") +# - + +# I + +# + +first_value, second_value, third_value = input(), input(), input() + +my_array = [first_value, second_value, third_value] +my_array.sort() + +print(my_array[0]) +# - + +# J + +# + +value = input() + +sum1 = int(value[1]) + int(value[2]) +sum2 = int(value[0]) + int(value[1]) + +if sum1 >= sum2: + print(str(sum1) + str(sum2)) +else: + print(str(sum2) + str(sum1)) +# - + +# K + +# + +value = input() + +digits = list(map(int, value)) +digits.sort() + +if digits[0] + digits[2] == digits[1] * 2: + print("YES") +else: + print("NO") +# - + +# L + +# + +line1 = int(input()) +line2 = int(input()) +line3 = int(input()) + +if all([line1 + line2 > line3, line1 + line3 > line2, line2 + line3 > line1]): + print("YES") +else: + print("NO") +# - + +# M + +# + +line_1 = input() +line_2 = input() +line_3 = input() + +for index, _ in enumerate(line_1): + if line_1[index] == line_2[index] == line_3[index]: + print(line_1[index]) +# - + +# N + +# + +integers_list = list(map(int, input())) +integers_list.sort() + +first = "0" +second = "0" + +if integers_list[0] != 0: + first = str(integers_list[0]) + str(integers_list[1]) +elif integers_list[1] != 0: + first = str(integers_list[1]) + str(integers_list[0]) +elif integers_list[2] != 0: + first = str(integers_list[2]) + str(integers_list[0]) + +if integers_list[2] != 0: + second = str(integers_list[2]) + str(integers_list[1]) + +print(first, second) +# - + +# O + +# + +value1 = list(map(int, input())) +value2 = list(map(int, input())) +combined = value1 + value2 + +combined.sort(reverse=True) + + +result = f"{combined[0]}{(combined[1] + combined[2]) % 10}{combined[3]}" + +print(result) +# - + +# P + +# + +vasya_p = "Вася" +tolya_p = "Толя" +petya_p = "Петя" + + +p_s = int(input()) +v_s = int(input()) +t_s = int(input()) + +if v_s > t_s > p_s: + first = vasya_p + second = tolya_p + third = petya_p +elif v_s > p_s > t_s: + first = vasya_p + second = petya_p + third = tolya_p +elif p_s > v_s > t_s: + first = petya_p + second = vasya_p + third = tolya_p +elif p_s > t_s > v_s: + first = petya_p + second = tolya_p + third = vasya_p +elif t_s > p_s > v_s: + first = tolya_p + second = petya_p + third = vasya_p +else: + first = tolya_p + second = vasya_p + third = petya_p + + +print(f'{"": ^8}{first: ^8}{"": ^8}') +print(f'{second: ^8}{"": ^8}{"": ^8}') +print(f'{"": ^8}{"": ^8}{third: ^8}') +print(f'{"II": ^8}{"I": ^8}{"III": ^8}') +# - + +# Q + +# + +a_coef = float(input()) +b_coef = float(input()) +c_coef = float(input()) + + +if a_coef == b_coef == c_coef == 0: + print("Infinite solutions") +elif a_coef == b_coef == 0: + print("No solution") +elif a_coef == 0: + result_q = -c_coef / b_coef + + print(round(result_q, 2)) +else: + discriminant = b_coef**2 - 4 * a_coef * c_coef + + if discriminant < 0: + print("No solution") + elif discriminant == 0: + result_q = -b_coef / (2 * a_coef) + + print(round(result_q, 2)) + else: + result1 = (-b_coef + discriminant**0.5) / (2 * a_coef) + result2 = (-b_coef - discriminant**0.5) / (2 * a_coef) + + min_result = min(round(result1, 2), round(result2, 2)) + max_result = max(round(result1, 2), round(result2, 2)) + + print(min_result, max_result) +# - + +# R + +# + +a_length = float(input()) +b_length = float(input()) +c_length = float(input()) + +lengths = [a_length, b_length, c_length] +lengths.sort(reverse=True) + +longest = lengths[0] +other1, other2 = lengths[1:] + +if longest**2 == other1**2 + other2**2: + print("100%") +elif longest**2 > other1**2 + other2**2: + print("велика") +else: + print("крайне мала") +# - + +# T + +# + +a_str = input() +b_str = input() +c_str = input() + +results = [] + +for string in [a_str, b_str, c_str]: + if "зайка" in string: + results.append(string) + +results.sort() +result = results[0] + +print(result, len(result)) diff --git a/Python/yandex-python-handbook/chapter_2_3.ipynb b/Python/yandex-python-handbook/chapter_2_3.ipynb new file mode 100644 index 00000000..e053d0ab --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_3.ipynb @@ -0,0 +1,560 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "863a352a", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Задания к главе 2.3 \"Циклы\" из хендбука Яндекс \"Основы Python\".\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "while value := input() != \"Три!\":\n", + " print(\"Режим ожидания...\")\n", + "\n", + "print(\"Ёлочка, гори!\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "counter = 0\n", + "\n", + "while (value_b := input()) != \"Приехали!\":\n", + " if \"зайка\" in value_b:\n", + " counter += 1\n", + "\n", + "print(counter)" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "start = int(input())\n", + "finish = int(input())\n", + "\n", + "result = []\n", + "\n", + "for index in range(start, finish + 1):\n", + " result.append(index)\n", + "\n", + "\n", + "print(\" \".join(map(str, result)))" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "95.0\n" + ] + } + ], + "source": [ + "start = int(input())\n", + "finish = int(input())\n", + "\n", + "one = 1 if finish > start else -1\n", + "\n", + "for i in range(start, finish + one, one):\n", + " print(i, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "total = 0.0\n", + "\n", + "while (price := float(input())) != 0:\n", + " total += price * 0.9 if price >= 500 else price\n", + "\n", + "print(total)" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "a_variable = int(input())\n", + "b_variable = int(input())\n", + "\n", + "while b_variable:\n", + " a_variable, b_variable = b_variable, a_variable % b_variable\n", + "\n", + "print(a_variable)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "a_coef = int(input())\n", + "b_coef = int(input())\n", + "\n", + "x_coef = a_coef\n", + "y_coef = b_coef\n", + "\n", + "while y_coef:\n", + " x_coef, y_coef = y_coef, x_coef % y_coef\n", + "\n", + "print(int(a_coef * b_coef / x_coef))" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "text_h = input()\n", + "num_h = int(input())\n", + "\n", + "for i in range(0, num_h, 1):\n", + " print(text_h)" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "input_x = int(input())\n", + "\n", + "result_i = 1\n", + "\n", + "for i in range(1, input_x + 1):\n", + " result_i *= i\n", + "\n", + "print(result_i)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "x_coord = y_coord = 0\n", + "\n", + "while (direction := input()) != \"СТОП\":\n", + " num = int(input())\n", + "\n", + " match direction:\n", + " case \"СЕВЕР\":\n", + " y_coord += num\n", + " case \"ЮГ\":\n", + " y_coord -= num\n", + " case \"ВОСТОК\":\n", + " x_coord += num\n", + " case \"ЗАПАД\":\n", + " x_coord -= num\n", + "\n", + "print(y_coord)\n", + "print(x_coord)" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "string = input()\n", + "\n", + "i = 0\n", + "\n", + "total_sum = 0\n", + "\n", + "while i < len(string):\n", + " total_sum += int(string[i])\n", + " i += 1\n", + "\n", + "print(total_sum)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "string = input()\n", + "\n", + "res = 0\n", + "\n", + "for digit in string:\n", + " res = max(res, int(digit)) if res is not None else int(digit)\n", + "\n", + "print(res)" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "names = [input() for _ in range(0, num)]\n", + "\n", + "result_m = names[0]\n", + "\n", + "for name in names[1::]:\n", + " result_m = name if name < result_m else result_m\n", + "\n", + "print(result_m)" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "if num < 2:\n", + " print(\"NO\")\n", + "else:\n", + " for i in range(2, round(num / 2) + 1):\n", + " if num % i == 0:\n", + " print(\"NO\")\n", + " break\n", + " else:\n", + " print(\"YES\")" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "my_list = [input() for _ in range(num)]\n", + "\n", + "print(len(list(filter(lambda string: \"зайка\" in string, my_list))))" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "num_p = input()\n", + "\n", + "left_p = 0\n", + "right_p = len(num_p) - 1\n", + "\n", + "while left_p < right_p:\n", + " if num_p[left_p] != num_p[right_p]:\n", + " print(\"NO\")\n", + "\n", + " break\n", + " left_p += 1\n", + " right_p -= 1\n", + "else:\n", + " print(\"YES\")" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "num_q = input()\n", + "\n", + "print(\"\".join(filter(lambda digit: int(digit) % 2 != 0, num_q)))" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "num = int(input())\n", + "\n", + "rest = num\n", + "\n", + "result_int: list[int] = []\n", + "\n", + "divider = 1\n", + "\n", + "while rest > 1:\n", + " divider += 1\n", + "\n", + " if rest % divider != 0:\n", + " continue\n", + "\n", + " result_int.append(divider)\n", + "\n", + " rest //= divider\n", + "\n", + " divider -= 1\n", + "\n", + "result_str = \" * \".join(map(str, result_int))\n", + "\n", + "print(result_str if num > 1 else num)" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "left = 1\n", + "right = 1001\n", + "\n", + "print((left + right) // 2)\n", + "\n", + "while (response := input()) != \"Угадал!\":\n", + " if response == \"Больше\":\n", + " left = (left + right) // 2 + 1\n", + " else:\n", + " right = (left + right) // 2 - 1\n", + "\n", + " print((left + right) // 2)" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_2_3.py b/Python/yandex-python-handbook/chapter_2_3.py new file mode 100644 index 00000000..fbe92126 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_3.py @@ -0,0 +1,276 @@ +"""Задания к главе 2.3 "Циклы" из хендбука Яндекс "Основы Python".""" + +# A + +# + +while value := input() != "Три!": + print("Режим ожидания...") + +print("Ёлочка, гори!") +# - + +# B + +# + +counter = 0 + +while (value_b := input()) != "Приехали!": + if "зайка" in value_b: + counter += 1 + +print(counter) +# - + +# C + +# + +start = int(input()) +finish = int(input()) + +result = [] + +for index in range(start, finish + 1): + result.append(index) + + +print(" ".join(map(str, result))) +# - + +# D + +# + +start = int(input()) +finish = int(input()) + +one = 1 if finish > start else -1 + +for i in range(start, finish + one, one): + print(i, end=" ") +# - + +# E + +# + +total = 0.0 + +while (price := float(input())) != 0: + total += price * 0.9 if price >= 500 else price + +print(total) +# - + +# F + +# + +a_variable = int(input()) +b_variable = int(input()) + +while b_variable: + a_variable, b_variable = b_variable, a_variable % b_variable + +print(a_variable) +# - + +# G + +# + +a_coef = int(input()) +b_coef = int(input()) + +x_coef = a_coef +y_coef = b_coef + +while y_coef: + x_coef, y_coef = y_coef, x_coef % y_coef + +print(int(a_coef * b_coef / x_coef)) +# - + +# H + +# + +text_h = input() +num_h = int(input()) + +for i in range(0, num_h, 1): + print(text_h) +# - + +# I + +# + +input_x = int(input()) + +result_i = 1 + +for i in range(1, input_x + 1): + result_i *= i + +print(result_i) +# - + +# J + +# + +x_coord = y_coord = 0 + +while (direction := input()) != "СТОП": + num = int(input()) + + match direction: + case "СЕВЕР": + y_coord += num + case "ЮГ": + y_coord -= num + case "ВОСТОК": + x_coord += num + case "ЗАПАД": + x_coord -= num + +print(y_coord) +print(x_coord) +# - + +# K + +# + +string = input() + +i = 0 + +total_sum = 0 + +while i < len(string): + total_sum += int(string[i]) + i += 1 + +print(total_sum) +# - + +# L + +# + +string = input() + +res = 0 + +for digit in string: + res = max(res, int(digit)) if res is not None else int(digit) + +print(res) +# - + +# M + +# + +num = int(input()) + +names = [input() for _ in range(0, num)] + +result_m = names[0] + +for name in names[1::]: + result_m = name if name < result_m else result_m + +print(result_m) +# - + +# N + +# + +num = int(input()) + +if num < 2: + print("NO") +else: + for i in range(2, round(num / 2) + 1): + if num % i == 0: + print("NO") + break + else: + print("YES") +# - + +# O + +# + +num = int(input()) + +my_list = [input() for _ in range(num)] + +print(len(list(filter(lambda string: "зайка" in string, my_list)))) +# - + +# P + +# + +num_p = input() + +left_p = 0 +right_p = len(num_p) - 1 + +while left_p < right_p: + if num_p[left_p] != num_p[right_p]: + print("NO") + + break + left_p += 1 + right_p -= 1 +else: + print("YES") +# - + +# Q + +# + +num_q = input() + +print("".join(filter(lambda digit: int(digit) % 2 != 0, num_q))) +# - + +# R + +# + +num = int(input()) + +rest = num + +result_int: list[int] = [] + +divider = 1 + +while rest > 1: + divider += 1 + + if rest % divider != 0: + continue + + result_int.append(divider) + + rest //= divider + + divider -= 1 + +result_str = " * ".join(map(str, result_int)) + +print(result_str if num > 1 else num) +# - + +# S + +# + +left = 1 +right = 1001 + +print((left + right) // 2) + +while (response := input()) != "Угадал!": + if response == "Больше": + left = (left + right) // 2 + 1 + else: + right = (left + right) // 2 - 1 + + print((left + right) // 2) +# - + +# T diff --git a/Python/yandex-python-handbook/chapter_2_4.ipynb b/Python/yandex-python-handbook/chapter_2_4.ipynb new file mode 100644 index 00000000..8158fd9a --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_4.ipynb @@ -0,0 +1,688 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fc271a33", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Задания к главе 2.4 \"Вложенные циклы\" из хендбука Яндекс \"Основы Python\".\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "num_a = int(input())\n", + "\n", + "for index1 in range(1, num_a + 1):\n", + " for index2 in range(1, num_a + 1):\n", + " print(index1 * index2, end=\"\\n\" if index2 == num_a else \" \")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "num_b = int(input())\n", + "\n", + "for index1 in range(1, num_b + 1):\n", + " for index2 in range(1, num_b + 1):\n", + " print(f\"{index2} * {index1} = {index2 * index1}\")" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "max_num = int(input())\n", + "\n", + "current = 1\n", + "\n", + "current_row = 1\n", + "current_column = 1\n", + "\n", + "while current <= max_num:\n", + " while current <= max_num and current_column <= current_row:\n", + " print(current, end=\" \")\n", + "\n", + " current += 1\n", + " current_column += 1\n", + "\n", + " current_column = 1\n", + " current_row += 1\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "inputs_amount = int(input())\n", + "\n", + "inputs_list = [input() for _ in range(inputs_amount)]\n", + "\n", + "sums_list = map(lambda item: sum(map(int, item)), inputs_list)\n", + "\n", + "print(sum(sums_list))" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "lands_amount = int(input())\n", + "\n", + "bunnies_counter = 0\n", + "\n", + "for _ in range(lands_amount):\n", + " is_bunny_found = False\n", + "\n", + " while (description := input()) != \"ВСЁ\":\n", + " if description == \"зайка\" and not is_bunny_found:\n", + " bunnies_counter += 1\n", + " is_bunny_found = True\n", + "\n", + "print(bunnies_counter)" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "nums_amount = int(input())\n", + "\n", + "nums_list = [int(input()) for _ in range(nums_amount)]\n", + "\n", + "divider = nums_list[0]\n", + "\n", + "for index in range(1, len(nums_list), 1):\n", + " next_var = nums_list[index]\n", + "\n", + " while next_var:\n", + " divider, next_var = next_var, divider % next_var\n", + "\n", + "print(divider)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "players_amount = int(input())\n", + "\n", + "seconds = 3\n", + "\n", + "for index in range(players_amount):\n", + " current_time = seconds\n", + "\n", + " while current_time:\n", + " print(f\"До старта {current_time} секунд(ы)\")\n", + " current_time -= 1\n", + "\n", + " print(f\"Старт {index + 1}!!!\")\n", + "\n", + " seconds += 1" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "children_amount = int(input())\n", + "\n", + "winner_name: str\n", + "max_sum: int = 0\n", + "\n", + "for _ in range(children_amount):\n", + " name = input()\n", + " guessed_num = input()\n", + "\n", + " current_sum = sum(map(int, guessed_num))\n", + "\n", + " if current_sum >= max_sum:\n", + " max_sum = current_sum\n", + " winner_name = name\n", + "\n", + "print(winner_name)" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "children_amount = int(input())\n", + "\n", + "largest_num = \"\"\n", + "\n", + "for _ in range(children_amount):\n", + " largest_num += max(input())\n", + "\n", + "print(largest_num)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "slices = int(input())\n", + "\n", + "print(\"А Б В\")\n", + "for index1 in range(1, slices + 1):\n", + " for index2 in range(1, slices + 1):\n", + " for index3 in range(1, slices + 1):\n", + " total_sum = index1 + index2 + index3\n", + " if total_sum == slices:\n", + " print(f\"{index1} {index2} {index3}\")\n", + " break\n", + "\n", + " if total_sum > slices:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "nums_amount = int(input())\n", + "\n", + "primes = 0\n", + "\n", + "for _ in range(nums_amount):\n", + " is_prime = True\n", + "\n", + " num = int(input())\n", + "\n", + " if num < 2:\n", + " continue\n", + "\n", + " for divider in range(2, round(num / 2) + 1):\n", + " if num % divider == 0:\n", + " is_prime = False\n", + " break\n", + "\n", + " if is_prime:\n", + " primes += 1\n", + "\n", + "print(primes)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "height = int(input())\n", + "width = int(input())\n", + "\n", + "cell_value = 1\n", + "\n", + "chars_amount = len(str(height * width))\n", + "\n", + "for row_index in range(height):\n", + " for column_index in range(width):\n", + " end = \" \" if column_index < width - 1 else \"\\n\"\n", + " display_value = str(cell_value).rjust(chars_amount)\n", + "\n", + " print(display_value, end=end)\n", + "\n", + " cell_value += 1" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "height_m = int(input())\n", + "width_m = int(input())\n", + "\n", + "\n", + "chars_amount_m = len(str(height_m * width_m))\n", + "\n", + "first_num_in_row = 1\n", + "\n", + "for row_index in range(height_m):\n", + " for column_index in range(width_m):\n", + " end = \" \" if column_index < width_m - 1 else \"\\n\"\n", + "\n", + " value = first_num_in_row + height_m * column_index\n", + "\n", + " print(str(value).rjust(chars_amount_m), end=end)\n", + "\n", + " first_num_in_row += 1" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "height_n = int(input())\n", + "width_n = int(input())\n", + "\n", + "\n", + "length = len(str(height_n * width_n))\n", + "\n", + "is_odd = True\n", + "\n", + "for row_index in range(1, height_n + 1):\n", + "\n", + " current_range = range(1, width_n + 1) if is_odd else range(width_n, 0, -1)\n", + "\n", + " for column_index in current_range:\n", + " value = (row_index - 1) * width_n + column_index\n", + "\n", + " print(str(value).rjust(length), end=\" \")\n", + "\n", + " print()\n", + "\n", + " is_odd = not is_odd" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "height_o = int(input())\n", + "width_o = int(input())\n", + "\n", + "\n", + "length_o = len(str(height_o * width_o))\n", + "\n", + "\n", + "for row_index in range(0, height_o):\n", + "\n", + " for column_index in range(0, width_o):\n", + "\n", + " if column_index % 2 != 0:\n", + " value = (column_index + 1) * height_o - row_index\n", + " else:\n", + " value = column_index * height_o + row_index + 1\n", + "\n", + " print(str(value).rjust(length_o), end=\" \")\n", + "\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "size = int(input())\n", + "width_p = int(input())\n", + "\n", + "\n", + "for row_index in range(1, size + 1):\n", + " temp: list[str] = []\n", + "\n", + " for column_index in range(1, size + 1):\n", + " product = str(row_index * column_index)\n", + "\n", + " additional_space = \" \" if width_p % 2 else \"\"\n", + "\n", + " string = (product + additional_space).center(width_p)\n", + "\n", + " temp.append(string)\n", + "\n", + " print(\"|\".join(temp))\n", + "\n", + " if row_index < size:\n", + " print(f\"{'-' * (size * (width_p + 1) - 1)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "nums_amount = int(input())\n", + "\n", + "counter = 0\n", + "\n", + "for _ in range(nums_amount):\n", + " current_value = input()\n", + "\n", + " if current_value == current_value[::-1]:\n", + " counter += 1\n", + "\n", + "print(counter)" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "limit = int(input())\n", + "\n", + "counter = 1\n", + "\n", + "level = 1\n", + "\n", + "result: list[list[str]] = []\n", + "\n", + "while counter <= limit:\n", + " current_list: list[str] = []\n", + "\n", + " for _ in range(level):\n", + " current_list.append(str(counter))\n", + " counter += 1\n", + "\n", + " if counter > limit:\n", + " break\n", + "\n", + " result.append(current_list)\n", + " level += 1\n", + "\n", + "formatted_result = list(map(\" \".join, result))\n", + "max_length = len(formatted_result[-1])\n", + "\n", + "for string in formatted_result:\n", + " print(f\"{string:^{max_length}}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "square_size = int(input())\n", + "\n", + "cell_width = len(str(round(square_size / 2)))\n", + "\n", + "for row_index in range(1, square_size + 1):\n", + " for column_index in range(1, square_size + 1):\n", + " value = min(\n", + " row_index,\n", + " column_index,\n", + " square_size + 1 - row_index,\n", + " square_size + 1 - column_index,\n", + " )\n", + "\n", + " formatted_value = str(value).rjust(cell_width)\n", + "\n", + " print(formatted_value, end=\" \")\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "value_t = int(input())\n", + "\n", + "\n", + "def convert_to_base(number: int, base: int) -> str:\n", + " \"\"\"Преобразует натуральное число в заданную систему счисления.\n", + "\n", + " Args:\n", + " number (int): натуральное число\n", + " base (int): основание системы счисления от 2 до 10\n", + "\n", + " Returns:\n", + " int: Строковое представление числа number в системе счисления base\n", + " \"\"\"\n", + " digits = \"0123456789\"\n", + " result_str = \"\"\n", + "\n", + " while number > 0:\n", + " result_str = digits[number % base] + result_str\n", + " number //= base\n", + "\n", + " return result_str\n", + "\n", + "\n", + "max_sum = 0\n", + "max_base: int\n", + "\n", + "for current_base in range(2, 11):\n", + " converted_value = str(convert_to_base(value_t, current_base))\n", + "\n", + " current_sum = sum(map(int, converted_value))\n", + "\n", + " if current_sum > max_sum:\n", + " max_sum = current_sum\n", + " max_base = current_base\n", + "\n", + "print(max_base)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_2_4.py b/Python/yandex-python-handbook/chapter_2_4.py new file mode 100644 index 00000000..f4a35fbe --- /dev/null +++ b/Python/yandex-python-handbook/chapter_2_4.py @@ -0,0 +1,413 @@ +"""Задания к главе 2.4 "Вложенные циклы" из хендбука Яндекс "Основы Python".""" + +# A + +# + +num_a = int(input()) + +for index1 in range(1, num_a + 1): + for index2 in range(1, num_a + 1): + print(index1 * index2, end="\n" if index2 == num_a else " ") +# - + +# B + +# + +num_b = int(input()) + +for index1 in range(1, num_b + 1): + for index2 in range(1, num_b + 1): + print(f"{index2} * {index1} = {index2 * index1}") +# - + +# C + +# + +max_num = int(input()) + +current = 1 + +current_row = 1 +current_column = 1 + +while current <= max_num: + while current <= max_num and current_column <= current_row: + print(current, end=" ") + + current += 1 + current_column += 1 + + current_column = 1 + current_row += 1 + print() +# - + +# D + +# + +inputs_amount = int(input()) + +inputs_list = [input() for _ in range(inputs_amount)] + +sums_list = map(lambda item: sum(map(int, item)), inputs_list) + +print(sum(sums_list)) +# - + +# E + +# + +lands_amount = int(input()) + +bunnies_counter = 0 + +for _ in range(lands_amount): + is_bunny_found = False + + while (description := input()) != "ВСЁ": + if description == "зайка" and not is_bunny_found: + bunnies_counter += 1 + is_bunny_found = True + +print(bunnies_counter) +# - + +# F + +# + +nums_amount = int(input()) + +nums_list = [int(input()) for _ in range(nums_amount)] + +divider = nums_list[0] + +for index in range(1, len(nums_list), 1): + next_var = nums_list[index] + + while next_var: + divider, next_var = next_var, divider % next_var + +print(divider) +# - + +# G + +# + +players_amount = int(input()) + +seconds = 3 + +for index in range(players_amount): + current_time = seconds + + while current_time: + print(f"До старта {current_time} секунд(ы)") + current_time -= 1 + + print(f"Старт {index + 1}!!!") + + seconds += 1 +# - + +# H + +# + +children_amount = int(input()) + +winner_name: str +max_sum: int = 0 + +for _ in range(children_amount): + name = input() + guessed_num = input() + + current_sum = sum(map(int, guessed_num)) + + if current_sum >= max_sum: + max_sum = current_sum + winner_name = name + +print(winner_name) +# - + +# I + +# + +children_amount = int(input()) + +largest_num = "" + +for _ in range(children_amount): + largest_num += max(input()) + +print(largest_num) +# - + +# J + +# + +slices = int(input()) + +print("А Б В") +for index1 in range(1, slices + 1): + for index2 in range(1, slices + 1): + for index3 in range(1, slices + 1): + total_sum = index1 + index2 + index3 + if total_sum == slices: + print(f"{index1} {index2} {index3}") + break + + if total_sum > slices: + break +# - + +# K + +# + +nums_amount = int(input()) + +primes = 0 + +for _ in range(nums_amount): + is_prime = True + + num = int(input()) + + if num < 2: + continue + + for divider in range(2, round(num / 2) + 1): + if num % divider == 0: + is_prime = False + break + + if is_prime: + primes += 1 + +print(primes) +# - + +# L + +# + +height = int(input()) +width = int(input()) + +cell_value = 1 + +chars_amount = len(str(height * width)) + +for row_index in range(height): + for column_index in range(width): + end = " " if column_index < width - 1 else "\n" + display_value = str(cell_value).rjust(chars_amount) + + print(display_value, end=end) + + cell_value += 1 +# - + +# M + +# + +height_m = int(input()) +width_m = int(input()) + + +chars_amount_m = len(str(height_m * width_m)) + +first_num_in_row = 1 + +for row_index in range(height_m): + for column_index in range(width_m): + end = " " if column_index < width_m - 1 else "\n" + + value = first_num_in_row + height_m * column_index + + print(str(value).rjust(chars_amount_m), end=end) + + first_num_in_row += 1 +# - + +# N + +# + +height_n = int(input()) +width_n = int(input()) + + +length = len(str(height_n * width_n)) + +is_odd = True + +for row_index in range(1, height_n + 1): + + current_range = range(1, width_n + 1) if is_odd else range(width_n, 0, -1) + + for column_index in current_range: + value = (row_index - 1) * width_n + column_index + + print(str(value).rjust(length), end=" ") + + print() + + is_odd = not is_odd +# - + +# O + +# + +height_o = int(input()) +width_o = int(input()) + + +length_o = len(str(height_o * width_o)) + + +for row_index in range(0, height_o): + + for column_index in range(0, width_o): + + if column_index % 2 != 0: + value = (column_index + 1) * height_o - row_index + else: + value = column_index * height_o + row_index + 1 + + print(str(value).rjust(length_o), end=" ") + + print() +# - + +# P + +# + +size = int(input()) +width_p = int(input()) + + +for row_index in range(1, size + 1): + temp: list[str] = [] + + for column_index in range(1, size + 1): + product = str(row_index * column_index) + + additional_space = " " if width_p % 2 else "" + + string = (product + additional_space).center(width_p) + + temp.append(string) + + print("|".join(temp)) + + if row_index < size: + print(f"{'-' * (size * (width_p + 1) - 1)}") +# - + +# Q + +# + +nums_amount = int(input()) + +counter = 0 + +for _ in range(nums_amount): + current_value = input() + + if current_value == current_value[::-1]: + counter += 1 + +print(counter) +# - + +# R + +# + +limit = int(input()) + +counter = 1 + +level = 1 + +result: list[list[str]] = [] + +while counter <= limit: + current_list: list[str] = [] + + for _ in range(level): + current_list.append(str(counter)) + counter += 1 + + if counter > limit: + break + + result.append(current_list) + level += 1 + +formatted_result = list(map(" ".join, result)) +max_length = len(formatted_result[-1]) + +for string in formatted_result: + print(f"{string:^{max_length}}") +# - + +# S + +# + +square_size = int(input()) + +cell_width = len(str(round(square_size / 2))) + +for row_index in range(1, square_size + 1): + for column_index in range(1, square_size + 1): + value = min( + row_index, + column_index, + square_size + 1 - row_index, + square_size + 1 - column_index, + ) + + formatted_value = str(value).rjust(cell_width) + + print(formatted_value, end=" ") + print() +# - + +# T + +# + +value_t = int(input()) + + +def convert_to_base(number: int, base: int) -> str: + """Преобразует натуральное число в заданную систему счисления. + + Args: + number (int): натуральное число + base (int): основание системы счисления от 2 до 10 + + Returns: + int: Строковое представление числа number в системе счисления base + """ + digits = "0123456789" + result_str = "" + + while number > 0: + result_str = digits[number % base] + result_str + number //= base + + return result_str + + +max_sum = 0 +max_base: int + +for current_base in range(2, 11): + converted_value = str(convert_to_base(value_t, current_base)) + + current_sum = sum(map(int, converted_value)) + + if current_sum > max_sum: + max_sum = current_sum + max_base = current_base + +print(max_base) diff --git a/Python/yandex-python-handbook/chapter_3_1.ipynb b/Python/yandex-python-handbook/chapter_3_1.ipynb new file mode 100644 index 00000000..ba44f702 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_1.ipynb @@ -0,0 +1,596 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fc271a33", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 3.1 \"Строки, кортежи, списки\".\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "\n", + "words_amount = int(input())\n", + "\n", + "for _ in range(words_amount):\n", + " word = input()\n", + " if word[0] not in \"абв\":\n", + " print(\"NO\")\n", + " break\n", + "else:\n", + " print(\"YES\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "hor_str = input()\n", + "\n", + "for char in hor_str:\n", + " print(char)" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "max_length = int(input())\n", + "titles_amount = int(input())\n", + "\n", + "for _ in range(titles_amount):\n", + " title = input()\n", + "\n", + " if len(title) > max_length:\n", + " end = max_length - 3\n", + " title = title[0:end:] + \"...\"\n", + "\n", + " print(title)" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "while (input_str := input()) != \"\":\n", + " if input_str.endswith(\"@@@\"):\n", + " continue\n", + "\n", + " if input_str.startswith(\"##\"):\n", + " print(input_str[2::])\n", + " else:\n", + " print(input_str)" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "input_str = input().lower()\n", + "\n", + "if input_str == input_str[::-1]:\n", + " print(\"YES\")\n", + "else:\n", + " print(\"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "lands_amount = int(input())\n", + "\n", + "rabbits_counter = 0\n", + "for _ in range(lands_amount):\n", + " rabbits_counter += (land := input()).count(\"зайка\")\n", + "\n", + "print(rabbits_counter)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "two_numbers = input()\n", + "\n", + "print(sum(map(int, two_numbers.split())))" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "lands_amount = int(input())\n", + "\n", + "for _ in range(lands_amount):\n", + " land = input()\n", + "\n", + " position = land.find(\"зайка\")\n", + "\n", + " print(position + 1 if position >= 0 else \"Заек нет =(\")" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "while line := input():\n", + " hash_position = line.find(\"#\")\n", + "\n", + " match (hash_position):\n", + " case -1:\n", + " print(line)\n", + " case 0:\n", + " continue\n", + " case _:\n", + " print(line[0:hash_position:])" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "letters: list[str] = []\n", + "\n", + "while (line := input()) != \"ФИНИШ\":\n", + " letters.extend(list(\"\".join(line.lower().split(\" \"))))\n", + "\n", + "letters.sort()\n", + "\n", + "max_count = 0\n", + "max_count_char: str\n", + "\n", + "for char in letters:\n", + " current_count = letters.count(char)\n", + "\n", + " if current_count > max_count:\n", + " max_count = current_count\n", + " max_count_char = char\n", + "\n", + "print(max_count_char)" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "titles_amount = int(input())\n", + "\n", + "titles = [input() for _ in range(titles_amount)]\n", + "\n", + "search_request = input().lower()\n", + "\n", + "for title in titles:\n", + " if title.lower().find(search_request) != -1:\n", + " print(title)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "porridge_list = [\n", + " \"Манная\",\n", + " \"Гречневая\",\n", + " \"Пшённая\",\n", + " \"Овсяная\",\n", + " \"Рисовая\",\n", + "]\n", + "\n", + "\n", + "days_amount = int(input())\n", + "\n", + "for day in range(days_amount):\n", + " print(porridge_list[day % len(porridge_list)])" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "nums_amount = int(input())\n", + "\n", + "nums: list[int] = [int(input()) for _ in range(nums_amount)]\n", + "\n", + "power = int(input())\n", + "\n", + "for num in nums:\n", + " print(num**power)" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "nums_iter = map(int, input().split(\" \"))\n", + "\n", + "power = int(input())\n", + "\n", + "for num in nums_iter:\n", + " print(num**power, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "nums_list_ = list(map(int, input().split(\" \")))\n", + "\n", + "divider_ = nums_list_[0]\n", + "\n", + "for index in range(1, len(nums_list_), 1):\n", + " next_var = nums_list_[index]\n", + "\n", + " while next_var:\n", + " divider_, next_var = next_var, divider_ % next_var\n", + "\n", + "print(divider_)" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "input_string = \"\".join(input().lower().split(\" \"))\n", + "\n", + "print(\"YES\" if input_string == input_string[::-1] else \"NO\")" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "input_string = input()\n", + "\n", + "length = len(input_string)\n", + "\n", + "counter = 0\n", + "\n", + "for index, digit in enumerate(input_string):\n", + " counter += 1\n", + "\n", + " if index < length - 1 and input_string[index + 1] == digit:\n", + " continue\n", + "\n", + " print(digit, counter)\n", + " counter = 0" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "input_list = input().split(\" \")\n", + "\n", + "stack_s: list[int] = []\n", + "\n", + "\n", + "for item in input_list:\n", + " if item.isdigit():\n", + " stack_s.append(int(item))\n", + " continue\n", + "\n", + " second = stack_s.pop()\n", + " first = stack_s.pop()\n", + "\n", + " match (item):\n", + " case \"+\":\n", + " stack_s.append(first + second)\n", + " case \"-\":\n", + " stack_s.append(first - second)\n", + " case \"*\":\n", + " stack_s.append(first * second)\n", + "\n", + "print(stack_s[0])" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "input_list = input().split(\" \")\n", + "\n", + "stack: list[int] = []\n", + "\n", + "\n", + "for item in input_list:\n", + " if item.isdigit():\n", + " stack.append(int(item))\n", + " continue\n", + "\n", + " match (item):\n", + " case \"+\":\n", + " second = stack.pop()\n", + " first = stack.pop()\n", + " stack.append(first + second)\n", + "\n", + " case \"-\":\n", + " second = stack.pop()\n", + " first = stack.pop()\n", + " stack.append(first - second)\n", + "\n", + " case \"*\":\n", + " second = stack.pop()\n", + " first = stack.pop()\n", + " stack.append(first * second)\n", + "\n", + " case \"/\":\n", + " second = stack.pop()\n", + " first = stack.pop()\n", + " stack.append(first // second)\n", + "\n", + " case \"~\":\n", + " first = stack.pop()\n", + " stack.append(first * (-1))\n", + "\n", + " case \"!\":\n", + " first = stack.pop()\n", + " stack.append(math.factorial(first))\n", + "\n", + " case \"#\":\n", + " stack.append(stack[-1])\n", + "\n", + " case \"@\":\n", + " stack.append(stack.pop(-3))\n", + "\n", + "print(stack[0])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_3_1.py b/Python/yandex-python-handbook/chapter_3_1.py new file mode 100644 index 00000000..49460093 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_1.py @@ -0,0 +1,301 @@ +# %% +"""Задания к главе 3.1 "Строки, кортежи, списки". + +Хендбук Яндекс "Основы Python". +""" + +# A + +# %% +import math + +words_amount = int(input()) + +for _ in range(words_amount): + word = input() + if word[0] not in "абв": + print("NO") + break +else: + print("YES") + +# B + +# %% +hor_str = input() + +for char in hor_str: + print(char) + +# C + +# %% +max_length = int(input()) +titles_amount = int(input()) + +for _ in range(titles_amount): + title = input() + + if len(title) > max_length: + end = max_length - 3 + title = title[0:end:] + "..." + + print(title) + +# D + +# %% +while (input_str := input()) != "": + if input_str.endswith("@@@"): + continue + + if input_str.startswith("##"): + print(input_str[2::]) + else: + print(input_str) + +# E + +# %% +input_str = input().lower() + +if input_str == input_str[::-1]: + print("YES") +else: + print("NO") + +# F + +# %% +lands_amount = int(input()) + +rabbits_counter = 0 +for _ in range(lands_amount): + rabbits_counter += (land := input()).count("зайка") + +print(rabbits_counter) + +# G + +# %% +two_numbers = input() + +print(sum(map(int, two_numbers.split()))) + +# H + +# %% +lands_amount = int(input()) + +for _ in range(lands_amount): + land = input() + + position = land.find("зайка") + + print(position + 1 if position >= 0 else "Заек нет =(") + +# I + +# %% +while line := input(): + hash_position = line.find("#") + + match (hash_position): + case -1: + print(line) + case 0: + continue + case _: + print(line[0:hash_position:]) + +# J + +# %% +letters: list[str] = [] + +while (line := input()) != "ФИНИШ": + letters.extend(list("".join(line.lower().split(" ")))) + +letters.sort() + +max_count = 0 +max_count_char: str + +for char in letters: + current_count = letters.count(char) + + if current_count > max_count: + max_count = current_count + max_count_char = char + +print(max_count_char) + +# K + +# %% +titles_amount = int(input()) + +titles = [input() for _ in range(titles_amount)] + +search_request = input().lower() + +for title in titles: + if title.lower().find(search_request) != -1: + print(title) + +# L + +# %% +porridge_list = [ + "Манная", + "Гречневая", + "Пшённая", + "Овсяная", + "Рисовая", +] + + +days_amount = int(input()) + +for day in range(days_amount): + print(porridge_list[day % len(porridge_list)]) + +# M + +# %% +nums_amount = int(input()) + +nums: list[int] = [int(input()) for _ in range(nums_amount)] + +power = int(input()) + +for num in nums: + print(num**power) + +# N + +# %% +nums_iter = map(int, input().split(" ")) + +power = int(input()) + +for num in nums_iter: + print(num**power, end=" ") + +# O + +# %% +nums_list_ = list(map(int, input().split(" "))) + +divider_ = nums_list_[0] + +for index in range(1, len(nums_list_), 1): + next_var = nums_list_[index] + + while next_var: + divider_, next_var = next_var, divider_ % next_var + +print(divider_) + +# P + +# Q + +# %% +input_string = "".join(input().lower().split(" ")) + +print("YES" if input_string == input_string[::-1] else "NO") + +# R + +# %% +input_string = input() + +length = len(input_string) + +counter = 0 + +for index, digit in enumerate(input_string): + counter += 1 + + if index < length - 1 and input_string[index + 1] == digit: + continue + + print(digit, counter) + counter = 0 + +# S + +# %% +input_list = input().split(" ") + +stack_s: list[int] = [] + + +for item in input_list: + if item.isdigit(): + stack_s.append(int(item)) + continue + + second = stack_s.pop() + first = stack_s.pop() + + match (item): + case "+": + stack_s.append(first + second) + case "-": + stack_s.append(first - second) + case "*": + stack_s.append(first * second) + +print(stack_s[0]) + +# T + +# %% +input_list = input().split(" ") + +stack: list[int] = [] + + +for item in input_list: + if item.isdigit(): + stack.append(int(item)) + continue + + match (item): + case "+": + second = stack.pop() + first = stack.pop() + stack.append(first + second) + + case "-": + second = stack.pop() + first = stack.pop() + stack.append(first - second) + + case "*": + second = stack.pop() + first = stack.pop() + stack.append(first * second) + + case "/": + second = stack.pop() + first = stack.pop() + stack.append(first // second) + + case "~": + first = stack.pop() + stack.append(first * (-1)) + + case "!": + first = stack.pop() + stack.append(math.factorial(first)) + + case "#": + stack.append(stack[-1]) + + case "@": + stack.append(stack.pop(-3)) + +print(stack[0]) diff --git a/Python/yandex-python-handbook/chapter_3_2.ipynb b/Python/yandex-python-handbook/chapter_3_2.ipynb new file mode 100644 index 00000000..ec948ccd --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_2.ipynb @@ -0,0 +1,707 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fc271a33", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 3.2 \"Множества, словари\".\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import TypedDict\n", + "\n", + "print(\"\".join(set(input())))" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"\".join(set(input()) & set(input())))" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "my_set: set[str] = set()\n", + "\n", + "for _ in range(int(input())):\n", + " items = input().split()\n", + "\n", + " for item in items:\n", + " my_set.add(item)\n", + "\n", + "for item in my_set:\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "semolina_porridge: set[str] = set()\n", + "oatmeal: set[str] = set()\n", + "\n", + "semolina_amount = int(input())\n", + "oatmeal_amount = int(input())\n", + "\n", + "for _ in range(semolina_amount):\n", + " semolina_porridge.add(input())\n", + "\n", + "for _ in range(oatmeal_amount):\n", + " oatmeal.add(input())\n", + "\n", + "print(len(semolina_porridge ^ oatmeal) or \"Таких нет\")" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "semolina_amount = int(input())\n", + "oatmeal_amount = int(input())\n", + "\n", + "child_names: list[str] = []\n", + "\n", + "for _ in range(semolina_amount + oatmeal_amount):\n", + " child_names.append(input())\n", + "\n", + "filtered = filter(lambda name: child_names.count(name) == 1, child_names)\n", + "\n", + "print(len(list(filtered)) or \"Таких нет\")" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "semolina_amount = int(input())\n", + "oatmeal_amount = int(input())\n", + "\n", + "names: list[str] = []\n", + "\n", + "for _ in range(semolina_amount + oatmeal_amount):\n", + " names.append(input())\n", + "\n", + "filtered = filter(lambda name: names.count(name) == 1, names)\n", + "\n", + "names_list = sorted(list(filtered))\n", + "\n", + "print(\"\\n\".join(names_list) if len(names_list) != 0 else \"Таких нет\")" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "MORSE = {\n", + " \"A\": \".-\",\n", + " \"B\": \"-...\",\n", + " \"C\": \"-.-.\",\n", + " \"D\": \"-..\",\n", + " \"E\": \".\",\n", + " \"F\": \"..-.\",\n", + " \"G\": \"--.\",\n", + " \"H\": \"....\",\n", + " \"I\": \"..\",\n", + " \"J\": \".---\",\n", + " \"K\": \"-.-\",\n", + " \"L\": \".-..\",\n", + " \"M\": \"--\",\n", + " \"N\": \"-.\",\n", + " \"O\": \"---\",\n", + " \"P\": \".--.\",\n", + " \"Q\": \"--.-\",\n", + " \"R\": \".-.\",\n", + " \"S\": \"...\",\n", + " \"T\": \"-\",\n", + " \"U\": \"..-\",\n", + " \"V\": \"...-\",\n", + " \"W\": \".--\",\n", + " \"X\": \"-..-\",\n", + " \"Y\": \"-.--\",\n", + " \"Z\": \"--..\",\n", + " \"0\": \"-----\",\n", + " \"1\": \".----\",\n", + " \"2\": \"..---\",\n", + " \"3\": \"...--\",\n", + " \"4\": \"....-\",\n", + " \"5\": \".....\",\n", + " \"6\": \"-....\",\n", + " \"7\": \"--...\",\n", + " \"8\": \"---..\",\n", + " \"9\": \"----.\",\n", + "}\n", + "\n", + "words = input().upper().split()\n", + "\n", + "coded_words = [\" \".join(map(lambda char: MORSE[char], list(w))) for w in words]\n", + "\n", + "print(\"\\n\".join(coded_words))" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "porridge_dict: dict[str, list[str]] = {}\n", + "\n", + "for _ in range(int(input())):\n", + " input_list = input().split()\n", + " name = input_list[0]\n", + "\n", + " for porridge in input_list[1::]:\n", + " if porridge not in porridge_dict:\n", + " porridge_dict[porridge] = []\n", + " porridge_dict[porridge].append(name)\n", + "\n", + "\n", + "target_porridge = input()\n", + "\n", + "if target_porridge in porridge_dict:\n", + " names_list = sorted(porridge_dict[target_porridge])\n", + " print(\"\\n\".join(names_list))\n", + "else:\n", + " print(\"Таких нет\")" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "lands_dict: dict[str, int] = {}\n", + "\n", + "while land := input():\n", + " for word in land.split():\n", + " if word not in lands_dict:\n", + " lands_dict[word] = 0\n", + " lands_dict[word] = (lands_dict[word] or 0) + 1\n", + "\n", + "\n", + "for key, value in lands_dict.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "LETTERS: dict[str, str] = {\n", + " \"А\": \"A\",\n", + " \"Б\": \"B\",\n", + " \"В\": \"V\",\n", + " \"Г\": \"G\",\n", + " \"Д\": \"D\",\n", + " \"Е\": \"E\",\n", + " \"Ё\": \"E\",\n", + " \"Ж\": \"ZH\",\n", + " \"З\": \"Z\",\n", + " \"И\": \"I\",\n", + " \"Й\": \"I\",\n", + " \"К\": \"K\",\n", + " \"Л\": \"L\",\n", + " \"М\": \"M\",\n", + " \"Н\": \"N\",\n", + " \"О\": \"O\",\n", + " \"П\": \"P\",\n", + " \"Р\": \"R\",\n", + " \"С\": \"S\",\n", + " \"Т\": \"T\",\n", + " \"У\": \"U\",\n", + " \"Ф\": \"F\",\n", + " \"Х\": \"KH\",\n", + " \"Ц\": \"TC\",\n", + " \"Ч\": \"CH\",\n", + " \"Ш\": \"SH\",\n", + " \"Щ\": \"SHCH\",\n", + " \"Ы\": \"Y\",\n", + " \"Э\": \"E\",\n", + " \"Ю\": \"IU\",\n", + " \"Я\": \"IA\",\n", + " \"Ь\": \"\",\n", + " \"Ъ\": \"\",\n", + "}\n", + "\n", + "\n", + "input_str = input()\n", + "\n", + "for char in input_str:\n", + " if char.upper() not in LETTERS:\n", + " print(char, end=\"\")\n", + " else:\n", + " is_upper = char.isupper()\n", + " translit = LETTERS[char.upper()]\n", + "\n", + " display_value = translit.capitalize() if is_upper else translit.lower()\n", + "\n", + " print(display_value, end=\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "names_dict: dict[str, int] = {}\n", + "\n", + "for _ in range(int(input())):\n", + " name = input()\n", + "\n", + " if name in names_dict:\n", + " names_dict[name] += 1\n", + " else:\n", + " names_dict[name] = 1\n", + "\n", + "print(sum(filter(lambda value: value > 1, names_dict.values())))" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "names_dict_l: dict[str, int] = {}\n", + "\n", + "for _ in range(int(input())):\n", + " name = input()\n", + "\n", + " if name in names_dict_l:\n", + " names_dict_l[name] += 1\n", + " else:\n", + " names_dict_l[name] = 1\n", + "\n", + "pairs = list(filter(lambda item: item[1] > 1, names_dict_l.items()))\n", + "\n", + "pairs.sort(key=lambda pair: pair[0])\n", + "\n", + "if len(pairs) != 0:\n", + " for name, counter in pairs:\n", + " print(name, \"-\", counter)\n", + "else:\n", + " print(\"Однофамильцев нет\")" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "dishes_amount = int(input())\n", + "\n", + "dishes = {input() for _ in range(dishes_amount)}\n", + "\n", + "\n", + "not_allowed_dishes = set()\n", + "\n", + "for _ in range(int(input())):\n", + " for _ in range(int(input())):\n", + " not_allowed_dishes.add(input())\n", + "\n", + "\n", + "allowed_dishes = sorted(list(dishes - not_allowed_dishes))\n", + "\n", + "result = \"\\n\".join(allowed_dishes)\n", + "\n", + "print(result or \"Готовить нечего\")" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "ingredients = {input() for _ in range(int(input()))}\n", + "\n", + "\n", + "cookbook: dict[str, set[str]] = {}\n", + "\n", + "for _ in range(int(input())):\n", + " dish = input()\n", + " cookbook[dish] = set()\n", + "\n", + " for _ in range(int(input())):\n", + " cookbook[dish].add(input())\n", + "\n", + "\n", + "allowed_dishes_n: list[str] = []\n", + "\n", + "for dish, ingredients_list in cookbook.items():\n", + " if ingredients >= ingredients_list:\n", + " allowed_dishes_n.append(dish)\n", + "\n", + "\n", + "sorted_dishes = sorted(allowed_dishes_n)\n", + "\n", + "print(\"\\n\".join(sorted_dishes) if sorted_dishes else \"Готовить нечего\")" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "class NumberStats(TypedDict):\n", + " \"\"\"\n", + " Статистика числа, содержащая информацию о составе цифр.\n", + "\n", + " Attributes:\n", + " digits (int): Общее количество цифр в числе\n", + " units (int): Количество единиц в числе\n", + " zeros (int): Количество нулей в числе\n", + " \"\"\"\n", + "\n", + " digits: int\n", + " units: int\n", + " zeros: int\n", + "\n", + "\n", + "statistics: list[NumberStats] = []\n", + "\n", + "for number in input().split():\n", + " bin_number = f\"{int(number):b}\"\n", + "\n", + " statistics.append(\n", + " {\n", + " \"digits\": len(bin_number),\n", + " \"units\": bin_number.count(\"1\"),\n", + " \"zeros\": bin_number.count(\"0\"),\n", + " }\n", + " )\n", + "\n", + "\n", + "print(statistics)" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "items_set: set[str] = set()\n", + "\n", + "while land := input():\n", + " items_list = land.split()\n", + "\n", + " for index, item in enumerate(items_list):\n", + " if item == \"зайка\":\n", + " if index > 0:\n", + " items_set.add(items_list[index - 1])\n", + " if index < len(items_list) - 1:\n", + " items_set.add(items_list[index + 1])\n", + "\n", + "for item in items_set:\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "class Person(TypedDict):\n", + " \"\"\"\n", + " Информация о друзьях первой и второй линии.\n", + "\n", + " Attributes:\n", + " first_line set[str]: Друзья первой линии\n", + " second_line set[str]: Друзья второй линии\n", + " \"\"\"\n", + "\n", + " first_line: set[str]\n", + " second_line: set[str]\n", + "\n", + "\n", + "friends: dict[str, Person] = {}\n", + "\n", + "\n", + "def create_person(person_name: str) -> None:\n", + " \"\"\"\n", + " Добавляет имя в словарь friends, если имя в словаре отсутствует.\n", + "\n", + " Args:\n", + " name (str): добавляемое имя\n", + " \"\"\"\n", + " if person_name not in friends:\n", + " new_person: Person = {\"first_line\": set(), \"second_line\": set()}\n", + " friends[person_name] = new_person\n", + "\n", + "\n", + "while names := input().split():\n", + " person, friend = names\n", + "\n", + " create_person(person)\n", + "\n", + " friends[person][\"first_line\"].add(friend)\n", + "\n", + " create_person(friend)\n", + "\n", + " friends[friend][\"first_line\"].add(person)\n", + "\n", + "for name, friends_dict in friends.items():\n", + " nearest_friends = friends_dict[\"first_line\"]\n", + "\n", + " for nearest_friend in nearest_friends:\n", + "\n", + " first_line = friends[nearest_friend][\"first_line\"]\n", + " second_line = friends[nearest_friend][\"second_line\"]\n", + " added_friends = second_line | nearest_friends - first_line\n", + " added_friends.discard(nearest_friend)\n", + "\n", + " friends[nearest_friend][\"second_line\"] = added_friends\n", + "\n", + "\n", + "for name in sorted(friends.keys()):\n", + " print(f\"{name}: {', '.join(sorted(friends[name]['second_line']))}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "toys: list[str] = []\n", + "\n", + "for _ in range(int(input())):\n", + " child_toys = set(input().split(\": \")[1].split(\", \"))\n", + "\n", + " toys.extend(child_toys)\n", + "\n", + "\n", + "all_toys: set[str] = set()\n", + "unique: set[str] = set()\n", + "\n", + "for toy in toys:\n", + " if toy not in all_toys:\n", + " all_toys.add(toy)\n", + " unique.add(toy)\n", + " else:\n", + " unique.discard(toy)\n", + "\n", + "for toy in sorted(unique):\n", + " print(toy)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_3_2.py b/Python/yandex-python-handbook/chapter_3_2.py new file mode 100644 index 00000000..68b7135e --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_2.py @@ -0,0 +1,456 @@ +"""Задания к главе 3.2 "Множества, словари". + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from typing import TypedDict + +print("".join(set(input()))) +# - + +# B + +print("".join(set(input()) & set(input()))) + +# C + +# + +my_set: set[str] = set() + +for _ in range(int(input())): + items = input().split() + + for item in items: + my_set.add(item) + +for item in my_set: + print(item) +# - + +# D + +# + +semolina_porridge: set[str] = set() +oatmeal: set[str] = set() + +semolina_amount = int(input()) +oatmeal_amount = int(input()) + +for _ in range(semolina_amount): + semolina_porridge.add(input()) + +for _ in range(oatmeal_amount): + oatmeal.add(input()) + +print(len(semolina_porridge ^ oatmeal) or "Таких нет") +# - + +# E + +# + +semolina_amount = int(input()) +oatmeal_amount = int(input()) + +child_names: list[str] = [] + +for _ in range(semolina_amount + oatmeal_amount): + child_names.append(input()) + +filtered = filter(lambda name: child_names.count(name) == 1, child_names) + +print(len(list(filtered)) or "Таких нет") +# - + +# F + +# + +semolina_amount = int(input()) +oatmeal_amount = int(input()) + +names: list[str] = [] + +for _ in range(semolina_amount + oatmeal_amount): + names.append(input()) + +filtered = filter(lambda name: names.count(name) == 1, names) + +names_list = sorted(list(filtered)) + +print("\n".join(names_list) if len(names_list) != 0 else "Таких нет") +# - + +# G + +# + +MORSE = { + "A": ".-", + "B": "-...", + "C": "-.-.", + "D": "-..", + "E": ".", + "F": "..-.", + "G": "--.", + "H": "....", + "I": "..", + "J": ".---", + "K": "-.-", + "L": ".-..", + "M": "--", + "N": "-.", + "O": "---", + "P": ".--.", + "Q": "--.-", + "R": ".-.", + "S": "...", + "T": "-", + "U": "..-", + "V": "...-", + "W": ".--", + "X": "-..-", + "Y": "-.--", + "Z": "--..", + "0": "-----", + "1": ".----", + "2": "..---", + "3": "...--", + "4": "....-", + "5": ".....", + "6": "-....", + "7": "--...", + "8": "---..", + "9": "----.", +} + +words = input().upper().split() + +coded_words = [" ".join(map(lambda char: MORSE[char], list(w))) for w in words] + +print("\n".join(coded_words)) +# - + +# H + +# + +porridge_dict: dict[str, list[str]] = {} + +for _ in range(int(input())): + input_list = input().split() + name = input_list[0] + + for porridge in input_list[1::]: + if porridge not in porridge_dict: + porridge_dict[porridge] = [] + porridge_dict[porridge].append(name) + + +target_porridge = input() + +if target_porridge in porridge_dict: + names_list = sorted(porridge_dict[target_porridge]) + print("\n".join(names_list)) +else: + print("Таких нет") +# - + +# I + +# + +lands_dict: dict[str, int] = {} + +while land := input(): + for word in land.split(): + if word not in lands_dict: + lands_dict[word] = 0 + lands_dict[word] = (lands_dict[word] or 0) + 1 + + +for key, value in lands_dict.items(): + print(key, value) +# - + +# J + +# + +LETTERS: dict[str, str] = { + "А": "A", + "Б": "B", + "В": "V", + "Г": "G", + "Д": "D", + "Е": "E", + "Ё": "E", + "Ж": "ZH", + "З": "Z", + "И": "I", + "Й": "I", + "К": "K", + "Л": "L", + "М": "M", + "Н": "N", + "О": "O", + "П": "P", + "Р": "R", + "С": "S", + "Т": "T", + "У": "U", + "Ф": "F", + "Х": "KH", + "Ц": "TC", + "Ч": "CH", + "Ш": "SH", + "Щ": "SHCH", + "Ы": "Y", + "Э": "E", + "Ю": "IU", + "Я": "IA", + "Ь": "", + "Ъ": "", +} + + +input_str = input() + +for char in input_str: + if char.upper() not in LETTERS: + print(char, end="") + else: + is_upper = char.isupper() + translit = LETTERS[char.upper()] + + display_value = translit.capitalize() if is_upper else translit.lower() + + print(display_value, end="") +# - + +# K + +# + +names_dict: dict[str, int] = {} + +for _ in range(int(input())): + name = input() + + if name in names_dict: + names_dict[name] += 1 + else: + names_dict[name] = 1 + +print(sum(filter(lambda value: value > 1, names_dict.values()))) +# - + +# L + +# + +names_dict_l: dict[str, int] = {} + +for _ in range(int(input())): + name = input() + + if name in names_dict_l: + names_dict_l[name] += 1 + else: + names_dict_l[name] = 1 + +pairs = list(filter(lambda item: item[1] > 1, names_dict_l.items())) + +pairs.sort(key=lambda pair: pair[0]) + +if len(pairs) != 0: + for name, counter in pairs: + print(name, "-", counter) +else: + print("Однофамильцев нет") +# - + +# M + +# + +dishes_amount = int(input()) + +dishes = {input() for _ in range(dishes_amount)} + + +not_allowed_dishes = set() + +for _ in range(int(input())): + for _ in range(int(input())): + not_allowed_dishes.add(input()) + + +allowed_dishes = sorted(list(dishes - not_allowed_dishes)) + +result = "\n".join(allowed_dishes) + +print(result or "Готовить нечего") +# - + +# N + +# + +ingredients = {input() for _ in range(int(input()))} + + +cookbook: dict[str, set[str]] = {} + +for _ in range(int(input())): + dish = input() + cookbook[dish] = set() + + for _ in range(int(input())): + cookbook[dish].add(input()) + + +allowed_dishes_n: list[str] = [] + +for dish, ingredients_list in cookbook.items(): + if ingredients >= ingredients_list: + allowed_dishes_n.append(dish) + + +sorted_dishes = sorted(allowed_dishes_n) + +print("\n".join(sorted_dishes) if sorted_dishes else "Готовить нечего") + + +# - + +# O + + +# + +class NumberStats(TypedDict): + """Статистика числа, содержащая информацию о составе цифр. + + Attributes: + digits (int): Общее количество цифр в числе + units (int): Количество единиц в числе + zeros (int): Количество нулей в числе + """ + + digits: int + units: int + zeros: int + + +statistics: list[NumberStats] = [] + +for number in input().split(): + bin_number = f"{int(number):b}" + + statistics.append( + { + "digits": len(bin_number), + "units": bin_number.count("1"), + "zeros": bin_number.count("0"), + } + ) + + +print(statistics) +# - + +# P + +# + +items_set: set[str] = set() + +while land := input(): + items_list = land.split() + + for index, item in enumerate(items_list): + if item == "зайка": + if index > 0: + items_set.add(items_list[index - 1]) + if index < len(items_list) - 1: + items_set.add(items_list[index + 1]) + +for item in items_set: + print(item) + + +# - + +# Q + + +# + +class Person(TypedDict): + """Информация о друзьях первой и второй линии. + + Attributes: + first_line set[str]: Друзья первой линии + second_line set[str]: Друзья второй линии + """ + + first_line: set[str] + second_line: set[str] + + +friends: dict[str, Person] = {} + + +def create_person(person_name: str) -> None: + """Добавляет имя в словарь friends, если имя в словаре отсутствует. + + Args: + name (str): добавляемое имя + """ + if person_name not in friends: + new_person: Person = {"first_line": set(), "second_line": set()} + friends[person_name] = new_person + + +while names := input().split(): + person, friend = names + + create_person(person) + + friends[person]["first_line"].add(friend) + + create_person(friend) + + friends[friend]["first_line"].add(person) + +for name, friends_dict in friends.items(): + nearest_friends = friends_dict["first_line"] + + for nearest_friend in nearest_friends: + + first_line = friends[nearest_friend]["first_line"] + second_line = friends[nearest_friend]["second_line"] + added_friends = second_line | nearest_friends - first_line + added_friends.discard(nearest_friend) + + friends[nearest_friend]["second_line"] = added_friends + + +for name in sorted(friends.keys()): + print(f"{name}: {', '.join(sorted(friends[name]['second_line']))}") +# - + +# S + +# + +toys: list[str] = [] + +for _ in range(int(input())): + child_toys = set(input().split(": ")[1].split(", ")) + + toys.extend(child_toys) + + +all_toys: set[str] = set() +unique: set[str] = set() + +for toy in toys: + if toy not in all_toys: + all_toys.add(toy) + unique.add(toy) + else: + unique.discard(toy) + +for toy in sorted(unique): + print(toy) diff --git a/Python/yandex-python-handbook/chapter_3_3.ipynb b/Python/yandex-python-handbook/chapter_3_3.ipynb new file mode 100644 index 00000000..1ffbb67c --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_3.ipynb @@ -0,0 +1,503 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 3.3.\n", + "\n", + "\"Списочные выражения. Модель памяти для типов языка Python\".\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "start_a = int(input())\n", + "start_b = int(input())\n", + "result = [num**2 for num in range(start_a, start_b + 1)]" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "start_a = int(input())\n", + "finish_b = int(input())\n", + "result_b = [\n", + " number**2\n", + " for number in range(\n", + " start_a,\n", + " finish_b + 1 if start_a < finish_b else finish_b - 1,\n", + " 1 if start_a < finish_b else -1,\n", + " )\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "start_c = int(input())\n", + "finish_c = int(input())\n", + "divider_c = int(input())\n", + "\n", + "range_c = range(start_c, finish_c + 1)\n", + "\n", + "result_c = [num for num in range_c if num % divider_c == 0]" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_d = [1, 2, 3, 4, 5]\n", + "\n", + "result_d = {num for num in numbers_d if num % 2 == 1}" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_e = [1, 2, 3, 4, 5]\n", + "\n", + "result_e = {num for num in numbers_e if num**0.5 // 1 == num**0.5}" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "sentence = \"Мама мыла раму\"\n", + "\n", + "result_f = [len(x) for x in sentence.split()]" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "text = \"2 + 2 = 4\"\n", + "\n", + "\"\".join([char for char in text if char.isdigit()])" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "string = \"Российская Федерация\"\n", + "\n", + "\"\".join([word[0].upper() for word in string.split()])" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_i = [3, 1, 2, 3, 2, 2, 1]\n", + "\n", + "\" - \".join(map(str, sorted(set(numbers_i))))" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "words_j = \"Ехали медведи на велосипеде\"\n", + "\n", + "vowels = \"аяуюоёэеиыaeiouy\"\n", + "\n", + "\n", + "def is_valid(word: str) -> bool:\n", + " \"\"\"\n", + " Функция для проверки количества гласных в слове.\n", + "\n", + " Args:\n", + " word (str): слово для проверки\n", + "\n", + " Returns:\n", + " bool: True, если гласных 3 или больше. Иначе False\n", + " \"\"\"\n", + " return len([letter for letter in word if letter.lower() in vowels]) >= 3\n", + "\n", + "\n", + "result_j = [word for word in words_j.split() if is_valid(word)]" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_k = [1, 2, 1, 3, 1, 2, 2, 4, 1, 2, 5, 1, 2]\n", + "\n", + "result_k = {num for num in numbers_k if numbers_k.count(num) == 1}" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_l = {2, 4, 5, 7, -10, -8, 10, -9, -1}\n", + "\n", + "max({num1 * num2 for num1 in numbers_l for num2 in numbers_l if num1 != num2})" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "data_m = {\"a\": [1, 2, 3], \"b\": [2, 3, 4, 5]}\n", + "\n", + "result_m = min((sum(nums), letter) for letter, nums in data_m.items())[1]" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "data_n = {\"a\": [1, 2, 1], \"b\": [2, 3, 2, 5, 1]}\n", + "\n", + "items_n = data_n.items()\n", + "\n", + "result_n = {letter for letter, nums in items_n if len(nums) != len(set(nums))}" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "text_o = \"Мама мыла раму!\"\n", + "\n", + "result_o = {\n", + " letter: text_o.lower().count(letter)\n", + " for letter in text_o.lower()\n", + " if letter.isalpha()\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "rle = [(\"a\", 2), (\"b\", 3), (\"c\", 1)]\n", + "\n", + "\"\".join(letter * count for letter, count in rle)" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "num_q = 3\n", + "\n", + "range_q = range(1, num_q + 1)\n", + "\n", + "result_q = [[column * row for column in range_q] for row in range_q]" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_r = {1, 2, 3, 4, 5}\n", + "\n", + "result_r = {\n", + " num: [divider for divider in range(1, num + 1) if num % divider == 0]\n", + " for num in numbers_r\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "numbers_s = {1, 2, 3, 4, 5}\n", + "\n", + "\n", + "def is_prime(num: int) -> bool:\n", + " \"\"\"\n", + " Функция для проверки простого числа.\n", + "\n", + " Args:\n", + " num (int): слово для проверки\n", + "\n", + " Returns:\n", + " bool: True, если число простое. Иначе False\n", + " \"\"\"\n", + " if num <= 1:\n", + " return False\n", + "\n", + " dividers_range = range(2, int(num**0.5 + 1))\n", + "\n", + " return len([div for div in dividers_range if not num % div]) == 0\n", + "\n", + "\n", + "result_s = {num for num in numbers_s if is_prime(num)}" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "text_t = \"ехали медведи на велосипеде\"\n", + "\n", + "result_t = {\n", + " tuple(sorted([word1, word2]))\n", + " for word1 in text_t.split()\n", + " for word2 in text_t.split()\n", + " if word1 != word2 and len(set(word1) & set(word2)) >= 3\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_3_3.py b/Python/yandex-python-handbook/chapter_3_3.py new file mode 100644 index 00000000..a6f2b799 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_3.py @@ -0,0 +1,221 @@ +"""Задания к главе 3.3. + +"Списочные выражения. Модель памяти для типов языка Python". + +Хендбук Яндекс "Основы Python". +""" + +# A + +start_a = int(input()) +start_b = int(input()) +result = [num**2 for num in range(start_a, start_b + 1)] + +# B + +start_a = int(input()) +finish_b = int(input()) +result_b = [ + number**2 + for number in range( + start_a, + finish_b + 1 if start_a < finish_b else finish_b - 1, + 1 if start_a < finish_b else -1, + ) +] + +# C + +# + +start_c = int(input()) +finish_c = int(input()) +divider_c = int(input()) + +range_c = range(start_c, finish_c + 1) + +result_c = [num for num in range_c if num % divider_c == 0] +# - + +# D + +# + +numbers_d = [1, 2, 3, 4, 5] + +result_d = {num for num in numbers_d if num % 2 == 1} +# - + +# E + +# + +numbers_e = [1, 2, 3, 4, 5] + +result_e = {num for num in numbers_e if num**0.5 // 1 == num**0.5} +# - + +# F + +# + +sentence = "Мама мыла раму" + +result_f = [len(x) for x in sentence.split()] +# - + +# G + +# + +text = "2 + 2 = 4" + +"".join([char for char in text if char.isdigit()]) +# - + +# H + +# + +string = "Российская Федерация" + +"".join([word[0].upper() for word in string.split()]) +# - + +# I + +# + +numbers_i = [3, 1, 2, 3, 2, 2, 1] + +" - ".join(map(str, sorted(set(numbers_i)))) +# - + +# J + +# + +words_j = "Ехали медведи на велосипеде" + +vowels = "аяуюоёэеиыaeiouy" + + +def is_valid(word: str) -> bool: + """Функция для проверки количества гласных в слове. + + Args: + word (str): слово для проверки + + Returns: + bool: True, если гласных 3 или больше. Иначе False + """ + return len([letter for letter in word if letter.lower() in vowels]) >= 3 + + +result_j = [word for word in words_j.split() if is_valid(word)] +# - + +# K + +# + +numbers_k = [1, 2, 1, 3, 1, 2, 2, 4, 1, 2, 5, 1, 2] + +result_k = {num for num in numbers_k if numbers_k.count(num) == 1} +# - + +# L + +# + +numbers_l = {2, 4, 5, 7, -10, -8, 10, -9, -1} + +max({num1 * num2 for num1 in numbers_l for num2 in numbers_l if num1 != num2}) +# - + +# M + +# + +data_m = {"a": [1, 2, 3], "b": [2, 3, 4, 5]} + +result_m = min((sum(nums), letter) for letter, nums in data_m.items())[1] +# - + +# N + +# + +data_n = {"a": [1, 2, 1], "b": [2, 3, 2, 5, 1]} + +items_n = data_n.items() + +result_n = {letter for letter, nums in items_n if len(nums) != len(set(nums))} +# - + +# O + +# + +text_o = "Мама мыла раму!" + +result_o = { + letter: text_o.lower().count(letter) + for letter in text_o.lower() + if letter.isalpha() +} +# - + +# P + +# + +rle = [("a", 2), ("b", 3), ("c", 1)] + +"".join(letter * count for letter, count in rle) +# - + +# Q + +# + +num_q = 3 + +range_q = range(1, num_q + 1) + +result_q = [[column * row for column in range_q] for row in range_q] +# - + +# R + +# + +numbers_r = {1, 2, 3, 4, 5} + +result_r = { + num: [divider for divider in range(1, num + 1) if num % divider == 0] + for num in numbers_r +} +# - + +# S + +# + +numbers_s = {1, 2, 3, 4, 5} + + +def is_prime(num: int) -> bool: + """Функция для проверки простого числа. + + Args: + num (int): слово для проверки + + Returns: + bool: True, если число простое. Иначе False + """ + if num <= 1: + return False + + dividers_range = range(2, int(num**0.5 + 1)) + + return len([div for div in dividers_range if not num % div]) == 0 + + +result_s = {num for num in numbers_s if is_prime(num)} +# - + +# T + +# + +text_t = "ехали медведи на велосипеде" + +result_t = { + tuple(sorted([word1, word2])) + for word1 in text_t.split() + for word2 in text_t.split() + if word1 != word2 and len(set(word1) & set(word2)) >= 3 +} diff --git a/Python/yandex-python-handbook/chapter_3_4.ipynb b/Python/yandex-python-handbook/chapter_3_4.ipynb new file mode 100644 index 00000000..495160e5 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_4.ipynb @@ -0,0 +1,659 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 3.4.\n", + "\n", + "\"Встроенные возможности по работе с коллекциями\".\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import (\n", + " accumulate,\n", + " chain,\n", + " combinations,\n", + " count,\n", + " cycle,\n", + " permutations,\n", + " product,\n", + ")\n", + "\n", + "items_list = input().split()\n", + "\n", + "for index, item in enumerate(items_list):\n", + " print(f\"{index + 1}. {item}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "colunm1 = input().split(\", \")\n", + "colunm2 = input().split(\", \")\n", + "\n", + "for child1, child2 in zip(colunm1, colunm2):\n", + " print(f\"{child1} - {child2}\")" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "nums = list(map(float, input().split()))\n", + "\n", + "for num in count(nums[0], nums[2]):\n", + " rounded = round(num, 2)\n", + "\n", + " if rounded > nums[1]:\n", + " break\n", + "\n", + " print(rounded)" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "words = input().split()\n", + "\n", + "for word in accumulate(map(lambda x: \" \" + x, words)):\n", + " print(word[1:])" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "list1 = input().split(\", \")\n", + "list2 = input().split(\", \")\n", + "list3 = input().split(\", \")\n", + "\n", + "\n", + "main_list = sorted(chain(list1, list2, list3))\n", + "\n", + "for index, item in enumerate(main_list):\n", + " print(f\"{index + 1}. {item}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "suits_f = [\"пик\", \"треф\", \"бубен\", \"червей\"]\n", + "weights_f = [2, 3, 4, 5, 6, 7, 8, 9, 10, \"валет\", \"дама\", \"король\", \"туз\"]\n", + "\n", + "suit_to_exclude = input()\n", + "\n", + "suits_f.remove(suit_to_exclude)\n", + "\n", + "for weight, suit in product(weights_f, suits_f):\n", + " print(weight, suit)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "students = [input() for _ in range(int(input()))]\n", + "\n", + "for student1, student2 in combinations(students, r=2):\n", + " print(f\"{student1} - {student2}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "porridges = [input() for _ in range(int(input()))]\n", + "days_amount = int(input())\n", + "\n", + "counter = 1\n", + "\n", + "for porridge in cycle(porridges):\n", + " if counter > days_amount:\n", + " break\n", + "\n", + " print(porridge)\n", + " counter += 1" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "size = int(input())\n", + "\n", + "table = [x * y for x, y in product(range(1, size + 1), repeat=2)]\n", + "\n", + "for index in range(0, size * size):\n", + " end = \" \" if (index + 1) % size != 0 else \"\\n\"\n", + "\n", + " print(table[index], end=end)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "slices = int(input())\n", + "\n", + "print(\"А Б В\")\n", + "\n", + "for value1, value2 in product(range(1, slices), repeat=2):\n", + " value3 = slices - value1 - value2\n", + "\n", + " if value3 > 0:\n", + " print(value1, value2, value3)" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "height_k = int(input())\n", + "width_k = int(input())\n", + "\n", + "max_length = len(str(height_k * width_k))\n", + "\n", + "for row, column in product(range(1, height_k + 1), range(1, width_k + 1)):\n", + " print(f\"{column + width_k * (row - 1):>{max_length}}\", end=\" \")\n", + "\n", + " if column == width_k:\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "products_list = []\n", + "\n", + "for _ in range(int(input())):\n", + " products_list.append(input().split(\", \"))\n", + "\n", + "for index, value in enumerate(sorted(chain.from_iterable(products_list))):\n", + " print(f\"{index + 1}. {value}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "athlete_list = []\n", + "\n", + "for _ in range(int(input())):\n", + " athlete_list.append(input())\n", + "\n", + "athlete_list.sort()\n", + "\n", + "for combination in permutations(athlete_list, len(athlete_list)):\n", + " print(\", \".join(combination))" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "winners_list = []\n", + "\n", + "for _ in range(int(input())):\n", + " winners_list.append(input())\n", + "\n", + "winners_permutations = permutations(sorted(winners_list), r=3)\n", + "\n", + "for winners in winners_permutations:\n", + " print(\", \".join(winners))" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "purchases_list = []\n", + "\n", + "for _ in range(int(input())):\n", + " purchases_list.extend(input().split(\", \"))\n", + "\n", + "\n", + "purchases_list.sort()\n", + "\n", + "purchases_permutations = permutations(purchases_list, r=3)\n", + "\n", + "for purchases in purchases_permutations:\n", + " print(\" \".join(purchases))" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "suits: list[str] = [\"бубен\", \"пик\", \"треф\", \"червей\"]\n", + "weights: list[str] = [\n", + " \"10\",\n", + " \"2\",\n", + " \"3\",\n", + " \"4\",\n", + " \"5\",\n", + " \"6\",\n", + " \"7\",\n", + " \"8\",\n", + " \"9\",\n", + " \"валет\",\n", + " \"дама\",\n", + " \"король\",\n", + " \"туз\",\n", + "]\n", + "\n", + "suits_dict: dict[str, str] = {\n", + " \"буби\": \"бубен\",\n", + " \"пики\": \"пик\",\n", + " \"трефы\": \"треф\",\n", + " \"черви\": \"червей\",\n", + "}\n", + "\n", + "required_suit = input()\n", + "weight_to_exclude = input()\n", + "\n", + "weights.remove(weight_to_exclude)\n", + "\n", + "card_products = product(weights, suits)\n", + "\n", + "card_combinations = combinations(card_products, r=3)\n", + "\n", + "counter = 0\n", + "\n", + "\n", + "for combination_p in card_combinations:\n", + " if counter >= 10:\n", + " break\n", + "\n", + " current = \", \".join(map(\" \".join, chain(combination_p)))\n", + "\n", + " if suits_dict[required_suit] not in current:\n", + " continue\n", + "\n", + " print(current)\n", + "\n", + " counter += 1" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "suits_q: list[str] = [\"бубен\", \"пик\", \"треф\", \"червей\"]\n", + "weights_q: list[str] = [\n", + " \"10\",\n", + " \"2\",\n", + " \"3\",\n", + " \"4\",\n", + " \"5\",\n", + " \"6\",\n", + " \"7\",\n", + " \"8\",\n", + " \"9\",\n", + " \"валет\",\n", + " \"дама\",\n", + " \"король\",\n", + " \"туз\",\n", + "]\n", + "\n", + "suits_dict_q: dict[str, str] = {\n", + " \"буби\": \"бубен\",\n", + " \"пики\": \"пик\",\n", + " \"трефы\": \"треф\",\n", + " \"черви\": \"червей\",\n", + "}\n", + "\n", + "required_suit = input()\n", + "weight_to_exclude = input()\n", + "previous = input()\n", + "\n", + "weights_q.remove(weight_to_exclude)\n", + "\n", + "card_products = product(weights_q, suits_q)\n", + "\n", + "card_combinations_q = combinations(card_products, r=3)\n", + "\n", + "is_last = False\n", + "\n", + "for combination_q in card_combinations_q:\n", + "\n", + " current = \", \".join(map(\" \".join, chain(combination_q)))\n", + "\n", + " if suits_dict_q[required_suit] not in current:\n", + " continue\n", + "\n", + " if is_last:\n", + " print(current)\n", + " break\n", + "\n", + " if current == previous:\n", + " is_last = True" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "values_list_r: list[int] = [0, 1]\n", + "\n", + "triples = product(values_list_r, repeat=3)\n", + "\n", + "expression = input()\n", + "\n", + "# Из-за того, что линтер не разрешает имена вида a, b, c\n", + "# заменим все однобуквенные имена на более длинные\n", + "expression_list = expression.split()\n", + "\n", + "for index, word in enumerate(expression_list):\n", + " match word:\n", + " case \"a\":\n", + " expression_list[index] = \"a_value\"\n", + " case \"b\":\n", + " expression_list[index] = \"b_value\"\n", + " case \"c\":\n", + " expression_list[index] = \"c_value\"\n", + "\n", + "expression = \" \".join(expression_list)\n", + "\n", + "print(\"a b c f\")\n", + "\n", + "for triple in triples:\n", + " a_value, b_value, c_value = triple\n", + "\n", + " # pylint: disable=eval-used\n", + " result = int(eval(expression))\n", + "\n", + " print(a_value, b_value, c_value, result)" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "upper_letters = [chr(i) for i in range(ord(\"A\"), ord(\"Z\") + 1)]\n", + "\n", + "expression = input()\n", + "\n", + "count_uppers = len({word for word in expression.split() if word.isupper()})\n", + "\n", + "values_list: list[str] = [\"0\", \"1\"]\n", + "\n", + "digit_sets = product(values_list, repeat=count_uppers)\n", + "\n", + "print(\" \".join(upper_letters[:count_uppers]), \"F\")\n", + "\n", + "for digit_set in digit_sets:\n", + " current = expression\n", + "\n", + " for index, value in enumerate(digit_set):\n", + " current = current.replace(upper_letters[index], value)\n", + "\n", + " # Условие задачи требует использовать eval\n", + " # pylint: disable=eval-used\n", + " result = int(eval(current))\n", + "\n", + " print(\" \".join(list(digit_set)), result)" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_3_4.py b/Python/yandex-python-handbook/chapter_3_4.py new file mode 100644 index 00000000..7fd6fcaf --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_4.py @@ -0,0 +1,382 @@ +"""Задания к главе 3.4. + +"Встроенные возможности по работе с коллекциями". + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from itertools import ( + accumulate, + chain, + combinations, + count, + cycle, + permutations, + product, +) + +items_list = input().split() + +for index, item in enumerate(items_list): + print(f"{index + 1}. {item}") +# - + +# B + +# + +colunm1 = input().split(", ") +colunm2 = input().split(", ") + +for child1, child2 in zip(colunm1, colunm2): + print(f"{child1} - {child2}") +# - + +# C + +# + +nums = list(map(float, input().split())) + +for num in count(nums[0], nums[2]): + rounded = round(num, 2) + + if rounded > nums[1]: + break + + print(rounded) +# - + +# D + +# + +words = input().split() + +for word in accumulate(map(lambda x: " " + x, words)): + print(word[1:]) +# - + +# E + +# + +list1 = input().split(", ") +list2 = input().split(", ") +list3 = input().split(", ") + + +main_list = sorted(chain(list1, list2, list3)) + +for index, item in enumerate(main_list): + print(f"{index + 1}. {item}") +# - + +# F + +# + +suits_f = ["пик", "треф", "бубен", "червей"] +weights_f = [2, 3, 4, 5, 6, 7, 8, 9, 10, "валет", "дама", "король", "туз"] + +suit_to_exclude = input() + +suits_f.remove(suit_to_exclude) + +for weight, suit in product(weights_f, suits_f): + print(weight, suit) +# - + +# G + +# + +students = [input() for _ in range(int(input()))] + +for student1, student2 in combinations(students, r=2): + print(f"{student1} - {student2}") +# - + +# H + +# + +porridges = [input() for _ in range(int(input()))] +days_amount = int(input()) + +counter = 1 + +for porridge in cycle(porridges): + if counter > days_amount: + break + + print(porridge) + counter += 1 +# - + +# I + +# + +size = int(input()) + +table = [x * y for x, y in product(range(1, size + 1), repeat=2)] + +for index in range(0, size * size): + end = " " if (index + 1) % size != 0 else "\n" + + print(table[index], end=end) +# - + +# J + +# + +slices = int(input()) + +print("А Б В") + +for value1, value2 in product(range(1, slices), repeat=2): + value3 = slices - value1 - value2 + + if value3 > 0: + print(value1, value2, value3) +# - + +# K + +# + +height_k = int(input()) +width_k = int(input()) + +max_length = len(str(height_k * width_k)) + +for row, column in product(range(1, height_k + 1), range(1, width_k + 1)): + print(f"{column + width_k * (row - 1):>{max_length}}", end=" ") + + if column == width_k: + print() +# - + +# L + +# + +products_list = [] + +for _ in range(int(input())): + products_list.append(input().split(", ")) + +for index, value in enumerate(sorted(chain.from_iterable(products_list))): + print(f"{index + 1}. {value}") +# - + +# M + +# + +athlete_list = [] + +for _ in range(int(input())): + athlete_list.append(input()) + +athlete_list.sort() + +for combination in permutations(athlete_list, len(athlete_list)): + print(", ".join(combination)) +# - + +# N + +# + +winners_list = [] + +for _ in range(int(input())): + winners_list.append(input()) + +winners_permutations = permutations(sorted(winners_list), r=3) + +for winners in winners_permutations: + print(", ".join(winners)) +# - + +# O + +# + +purchases_list = [] + +for _ in range(int(input())): + purchases_list.extend(input().split(", ")) + + +purchases_list.sort() + +purchases_permutations = permutations(purchases_list, r=3) + +for purchases in purchases_permutations: + print(" ".join(purchases)) +# - + +# P + +# + +suits: list[str] = ["бубен", "пик", "треф", "червей"] +weights: list[str] = [ + "10", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "валет", + "дама", + "король", + "туз", +] + +suits_dict: dict[str, str] = { + "буби": "бубен", + "пики": "пик", + "трефы": "треф", + "черви": "червей", +} + +required_suit = input() +weight_to_exclude = input() + +weights.remove(weight_to_exclude) + +card_products = product(weights, suits) + +card_combinations = combinations(card_products, r=3) + +counter = 0 + + +for combination_p in card_combinations: + if counter >= 10: + break + + current = ", ".join(map(" ".join, chain(combination_p))) + + if suits_dict[required_suit] not in current: + continue + + print(current) + + counter += 1 +# - + +# Q + +# + +suits_q: list[str] = ["бубен", "пик", "треф", "червей"] +weights_q: list[str] = [ + "10", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "валет", + "дама", + "король", + "туз", +] + +suits_dict_q: dict[str, str] = { + "буби": "бубен", + "пики": "пик", + "трефы": "треф", + "черви": "червей", +} + +required_suit = input() +weight_to_exclude = input() +previous = input() + +weights_q.remove(weight_to_exclude) + +card_products = product(weights_q, suits_q) + +card_combinations_q = combinations(card_products, r=3) + +is_last = False + +for combination_q in card_combinations_q: + + current = ", ".join(map(" ".join, chain(combination_q))) + + if suits_dict_q[required_suit] not in current: + continue + + if is_last: + print(current) + break + + if current == previous: + is_last = True +# - + +# R + +# + +values_list_r: list[int] = [0, 1] + +triples = product(values_list_r, repeat=3) + +expression = input() + +# Из-за того, что линтер не разрешает имена вида a, b, c +# заменим все однобуквенные имена на более длинные +expression_list = expression.split() + +for index, word in enumerate(expression_list): + match word: + case "a": + expression_list[index] = "a_value" + case "b": + expression_list[index] = "b_value" + case "c": + expression_list[index] = "c_value" + +expression = " ".join(expression_list) + +print("a b c f") + +for triple in triples: + a_value, b_value, c_value = triple + + # pylint: disable=eval-used + result = int(eval(expression)) + + print(a_value, b_value, c_value, result) +# - + +# S + +# + +upper_letters = [chr(i) for i in range(ord("A"), ord("Z") + 1)] + +expression = input() + +count_uppers = len({word for word in expression.split() if word.isupper()}) + +values_list: list[str] = ["0", "1"] + +digit_sets = product(values_list, repeat=count_uppers) + +print(" ".join(upper_letters[:count_uppers]), "F") + +for digit_set in digit_sets: + current = expression + + for index, value in enumerate(digit_set): + current = current.replace(upper_letters[index], value) + + # Условие задачи требует использовать eval + # pylint: disable=eval-used + result = int(eval(current)) + + print(" ".join(list(digit_set)), result) +# - + +# T diff --git a/Python/yandex-python-handbook/chapter_3_5.ipynb b/Python/yandex-python-handbook/chapter_3_5.ipynb new file mode 100644 index 00000000..3f8175dc --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_5.ipynb @@ -0,0 +1,741 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 3.5.\n", + "\n", + "Потоковый ввод/вывод. Работа с текстовыми файлами. JSON.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import math\n", + "from itertools import chain\n", + "from sys import stdin\n", + "\n", + "sum_a = sum(map(int, stdin.read().split()))\n", + "\n", + "print(sum_a)" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "children = list(map(lambda line: line.rstrip(\"\\n\").split(), stdin.readlines()))\n", + "\n", + "total_diff = sum(map(lambda child: int(child[2]) - int(child[1]), children))\n", + "\n", + "print(round(total_diff / len(children)))" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "code_lines = map(lambda line: line.rstrip(\"\\n\"), stdin.readlines())\n", + "\n", + "for code_line in code_lines:\n", + " hash_index = code_line.find(\"#\")\n", + "\n", + " match (hash_index):\n", + " case -1:\n", + " print(code_line)\n", + " case 0:\n", + " continue\n", + " case _:\n", + " print(code_line[:hash_index])" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "input_d = stdin.readlines()\n", + "titles = input_d[:-1]\n", + "search_request = input_d[-1].rstrip(\"\\n\").lower()\n", + "\n", + "\n", + "for title in titles:\n", + " if search_request in title.lower():\n", + " print(title.rstrip(\"\\n\"))" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "input_words: list[str] = []\n", + "\n", + "for words in stdin:\n", + " input_words.extend(words.rstrip(\"\\n\").split())\n", + "\n", + "palindromes = list(\n", + " {word for word in input_words if word.lower() == word[::-1].lower()}\n", + ")\n", + "\n", + "palindromes.sort()\n", + "\n", + "print(\"\\n\".join(palindromes))" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "# _буква добавлена для того, чтобы избежать дублирования кода\n", + "# и как следствие ошибок pylint\n", + "LETTERS: dict[str, str] = {\n", + " \"А_буква\": \"A\",\n", + " \"Б_буква\": \"B\",\n", + " \"В_буква\": \"V\",\n", + " \"Г_буква\": \"G\",\n", + " \"Д_буква\": \"D\",\n", + " \"Е_буква\": \"E\",\n", + " \"Ё_буква\": \"E\",\n", + " \"Ж_буква\": \"Zh\",\n", + " \"З_буква\": \"Z\",\n", + " \"И_буква\": \"I\",\n", + " \"Й_буква\": \"I\",\n", + " \"К_буква\": \"K\",\n", + " \"Л_буква\": \"L\",\n", + " \"М_буква\": \"M\",\n", + " \"Н_буква\": \"N\",\n", + " \"О_буква\": \"O\",\n", + " \"П_буква\": \"P\",\n", + " \"Р_буква\": \"R\",\n", + " \"С_буква\": \"S\",\n", + " \"Т_буква\": \"T\",\n", + " \"У_буква\": \"U\",\n", + " \"Ф_буква\": \"F\",\n", + " \"Х_буква\": \"Kh\",\n", + " \"Ц_буква\": \"Tc\",\n", + " \"Ч_буква\": \"Ch\",\n", + " \"Ш_буква\": \"Sh\",\n", + " \"Щ_буква\": \"Shch\",\n", + " \"Ы_буква\": \"Y\",\n", + " \"Э_буква\": \"E\",\n", + " \"Ю_буква\": \"Iu\",\n", + " \"Я_буква\": \"Ia\",\n", + " \"Ь_буква\": \"\",\n", + " \"Ъ_буква\": \"\",\n", + "}\n", + "\n", + "with open(\"cyrillic.txt\", encoding=\"utf-8\") as my_file:\n", + " text = my_file.read()\n", + "\n", + "transformed_text = \"\"\n", + "\n", + "for letter in text:\n", + " upper_letter = letter.upper()\n", + "\n", + " key = f\"{upper_letter}_буква\"\n", + "\n", + " if key not in LETTERS:\n", + " transformed_text += letter\n", + " continue\n", + "\n", + " is_upper = letter.isupper()\n", + "\n", + " transformed_text += LETTERS[key] if is_upper else LETTERS[key].lower()\n", + "\n", + "\n", + "with open(\"transliteration.txt\", \"w\", encoding=\"UTF-8\") as file_out:\n", + " file_out.write(transformed_text)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "file_name = input()\n", + "\n", + "with open(file_name, encoding=\"utf-8\") as my_file:\n", + " nums_iter = chain(\n", + " *[line.rstrip(\"/n\").split() for line in my_file.readlines()]\n", + " )\n", + "\n", + " numbers = [int(num) for num in nums_iter]\n", + "\n", + "print(len(numbers))\n", + "print(len([num for num in numbers if num > 0]))\n", + "print(min(numbers))\n", + "print(max(numbers))\n", + "print(sum(numbers))\n", + "print(round(sum(numbers) / len(numbers), 2))" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "file1 = input()\n", + "file2 = input()\n", + "file3 = input()\n", + "\n", + "set1: set[str] = set()\n", + "set2: set[str] = set()\n", + "\n", + "with open(file1, encoding=\"utf-8\") as current_file:\n", + " set1 = {word for word in current_file.read().split()}\n", + "\n", + "with open(file2, encoding=\"utf-8\") as current_file:\n", + " set2 = {word for word in current_file.read().split()}\n", + "\n", + "\n", + "with open(file3, \"w\", encoding=\"utf-8\") as file_out:\n", + " file_out.write(\"\\n\".join(sorted(set1.symmetric_difference(set2))))" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "file_in_i = input()\n", + "file_out_i = input()\n", + "\n", + "result: list[list[str]] = []\n", + "\n", + "with open(file_in_i, encoding=\"utf-8\") as file:\n", + " for line in file:\n", + " result.append(line.strip().replace(\"\\t\", \"\").split())\n", + "\n", + "\n", + "with open(file_out_i, \"w\", encoding=\"utf-8\") as file:\n", + " file.writelines([\" \".join(line) + \"\\n\" for line in result if line])" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "file_name = input()\n", + "lines_amount = int(input())\n", + "\n", + "with open(file_name, encoding=\"utf-8\") as file:\n", + " display_lines = file.readlines()[-lines_amount:]\n", + " print(\"\".join(display_lines))" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "file_in = input()\n", + "file_out_k = input()\n", + "\n", + "with open(file_in, encoding=\"utf-8\") as my_file:\n", + " nums = chain(*[line.rstrip(\"/n\").split() for line in my_file.readlines()])\n", + " numbers = [int(num) for num in nums]\n", + "\n", + "\n", + "statistic: dict[str, int | float] = {\n", + " \"count\": len(numbers),\n", + " \"positive_count\": len([num for num in numbers if num > 0]),\n", + " \"min\": min(numbers),\n", + " \"max\": max(numbers),\n", + " \"sum\": sum(numbers),\n", + " \"average\": round(sum(numbers) / len(numbers), 2),\n", + "}\n", + "\n", + "with open(file_out_k, \"w\", encoding=\"UTF-8\") as my_file:\n", + " json.dump(statistic, my_file, ensure_ascii=False, indent=2)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "file_1 = input()\n", + "file_2 = input()\n", + "file_3 = input()\n", + "file_4 = input()\n", + "\n", + "evens: list[str] = []\n", + "odds: list[str] = []\n", + "equals: list[str] = []\n", + "\n", + "even_digits = \"02468\"\n", + "odd_digits = \"13579\"\n", + "\n", + "with open(file_1, encoding=\"utf-8\") as my_file:\n", + " nums_lines = my_file.readlines()\n", + "\n", + " for line in nums_lines:\n", + "\n", + " temp_evens: list[str] = []\n", + " temp_odds: list[str] = []\n", + " temp_equals: list[str] = []\n", + "\n", + " for number in line.split():\n", + " evens_count = len([i for i in number if i in even_digits])\n", + " odd_count = len([i for i in number if i in odd_digits])\n", + "\n", + " if evens_count > odd_count:\n", + " temp_evens.append(number)\n", + " elif evens_count < odd_count:\n", + " temp_odds.append(number)\n", + " else:\n", + " temp_equals.append(number)\n", + "\n", + " evens.append(\" \".join(temp_evens) + \"\\n\")\n", + " odds.append(\" \".join(temp_odds) + \"\\n\")\n", + " equals.append(\" \".join(temp_equals) + \"\\n\")\n", + "\n", + " with open(file_2, \"w\", encoding=\"UTF-8\") as file:\n", + " file.writelines(evens)\n", + " with open(file_3, \"w\", encoding=\"UTF-8\") as file:\n", + " file.writelines(odds)\n", + " with open(file_4, \"w\", encoding=\"UTF-8\") as file:\n", + " file.writelines(equals)" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "file_name = input()\n", + "\n", + "result_m: dict[str, str]\n", + "\n", + "with open(file_name, encoding=\"utf-8\") as file:\n", + " result_m = json.load(file)\n", + "\n", + "for line in stdin.readlines():\n", + "\n", + " key, value = line.strip().split(\" == \")\n", + " result_m[key] = value\n", + "\n", + "\n", + "with open(file_name, mode=\"w\", encoding=\"utf-8\") as my_file:\n", + " json.dump(result_m, my_file, ensure_ascii=False, indent=2)" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "file_1 = input()\n", + "file_2 = input()\n", + "\n", + "result_n: dict[str, dict[str, str]] = {}\n", + "\n", + "with open(file_1, encoding=\"utf-8\") as file:\n", + " data = json.load(file)\n", + "\n", + "with open(file_2, encoding=\"utf-8\") as file:\n", + " update = json.load(file)\n", + "\n", + "for user in data:\n", + " name = user.pop(\"name\")\n", + "\n", + " result_n[name] = user\n", + "\n", + "\n", + "for user in update:\n", + " name = user.pop(\"name\")\n", + "\n", + " if name not in result_n:\n", + " result_n[name] = {}\n", + "\n", + " for key in user:\n", + " print(key)\n", + " should_up = (\n", + " key not in result_n[name] or user[key] > result_n[name][key]\n", + " )\n", + "\n", + " if should_up:\n", + " result_n[name][key] = user[key]\n", + "\n", + "with open(file_1, mode=\"w\", encoding=\"utf-8\") as my_file:\n", + " json.dump(result_n, my_file, ensure_ascii=False, indent=2)" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "answers = (answer.strip() for answer in stdin.readlines())\n", + "\n", + "file_name = \"scoring.json\"\n", + "\n", + "points = 0\n", + "\n", + "with open(file_name, encoding=\"UTF-8\") as file:\n", + " test_list = json.load(file)\n", + "\n", + "for group in test_list:\n", + " possible_points = group[\"points\"]\n", + "\n", + " correct_amount = 0\n", + "\n", + " for test in group[\"tests\"]:\n", + " current_answer = next(answers)\n", + "\n", + " correct_answer = test[\"pattern\"]\n", + "\n", + " if current_answer == correct_answer:\n", + " correct_amount += 1\n", + "\n", + " group_points = (possible_points / len(group[\"tests\"])) * correct_amount\n", + "\n", + " points += group_points\n", + "\n", + "print(int(points))" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "request = \" \".join(input().lower().split())\n", + "\n", + "result_p: list[str] = []\n", + "\n", + "for file_p in stdin:\n", + " with open(file_p.strip(), encoding=\"UTF-8\") as my_file:\n", + " if request in \" \".join(my_file.read().lower().split()):\n", + " result_p.append(file_p.strip())\n", + "\n", + "print(\"\\n\".join(result_p) if result_p else \"404. Not Found\")" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "decoded_text = \"\"\n", + "\n", + "with open(\"secret.txt\", encoding=\"UTF-8\") as file:\n", + " coded_text = file.read()\n", + "\n", + " for char in coded_text:\n", + " code = ord(char)\n", + " code = code % 256 if code >= 128 else code\n", + " decoded_text += chr(code)\n", + "\n", + "\n", + "print(decoded_text)" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "file_name = input()\n", + "\n", + "with open(file_name, encoding=\"UTF-8\") as file:\n", + " file.seek(0, 2)\n", + " position = file.tell()\n", + "\n", + "size: float = position\n", + "\n", + "postfix = [\"Б\", \"КБ\", \"МБ\", \"ГБ\", \"ТБ\"]\n", + "index = 0\n", + "\n", + "while size > 1024 and index < len(postfix) - 1:\n", + " index += 1\n", + " size = size / 1024\n", + "\n", + "print(f\"{math.ceil(size)}{postfix[index]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "offset = int(input()) % 26\n", + "\n", + "encoded = \"\"\n", + "\n", + "a_code = ord(\"a\")\n", + "z_code = ord(\"z\")\n", + "\n", + "with open(\"public.txt\", encoding=\"UTF-8\") as file:\n", + " text = file.read()\n", + "\n", + " for char in text:\n", + " lower = char.lower()\n", + "\n", + " is_upper = char.isupper()\n", + "\n", + " code = ord(lower)\n", + "\n", + " if code < a_code or code > z_code:\n", + " encoded += char\n", + " continue\n", + "\n", + " new_code = code + offset\n", + "\n", + " if new_code > z_code:\n", + " new_code -= 26\n", + " if new_code < a_code:\n", + " new_code += 26\n", + "\n", + " encoded += chr(new_code).upper() if is_upper else chr(new_code)\n", + "\n", + "\n", + "with open(\"private.txt\", mode=\"w\", encoding=\"UTF-8\") as file:\n", + " file.write(encoded)" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_3_5.py b/Python/yandex-python-handbook/chapter_3_5.py new file mode 100644 index 00000000..a09ce966 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_3_5.py @@ -0,0 +1,446 @@ +# %% +"""Задания к главе 3.5. + +Потоковый ввод/вывод. Работа с текстовыми файлами. JSON. + +Хендбук Яндекс "Основы Python". +""" + +# A + +# %% +import json +import math +from itertools import chain +from sys import stdin + +sum_a = sum(map(int, stdin.read().split())) + +print(sum_a) + +# B + +# %% +children = list(map(lambda line: line.rstrip("\n").split(), stdin.readlines())) + +total_diff = sum(map(lambda child: int(child[2]) - int(child[1]), children)) + +print(round(total_diff / len(children))) + +# C + +# %% +code_lines = map(lambda line: line.rstrip("\n"), stdin.readlines()) + +for code_line in code_lines: + hash_index = code_line.find("#") + + match (hash_index): + case -1: + print(code_line) + case 0: + continue + case _: + print(code_line[:hash_index]) + +# D + +# %% +input_d = stdin.readlines() +titles = input_d[:-1] +search_request = input_d[-1].rstrip("\n").lower() + + +for title in titles: + if search_request in title.lower(): + print(title.rstrip("\n")) + +# E + +# %% +input_words: list[str] = [] + +for words in stdin: + input_words.extend(words.rstrip("\n").split()) + +palindromes = list( + {word for word in input_words if word.lower() == word[::-1].lower()} +) + +palindromes.sort() + +print("\n".join(palindromes)) + +# F + +# %% +# _буква добавлена для того, чтобы избежать дублирования кода +# и как следствие ошибок pylint +LETTERS: dict[str, str] = { + "А_буква": "A", + "Б_буква": "B", + "В_буква": "V", + "Г_буква": "G", + "Д_буква": "D", + "Е_буква": "E", + "Ё_буква": "E", + "Ж_буква": "Zh", + "З_буква": "Z", + "И_буква": "I", + "Й_буква": "I", + "К_буква": "K", + "Л_буква": "L", + "М_буква": "M", + "Н_буква": "N", + "О_буква": "O", + "П_буква": "P", + "Р_буква": "R", + "С_буква": "S", + "Т_буква": "T", + "У_буква": "U", + "Ф_буква": "F", + "Х_буква": "Kh", + "Ц_буква": "Tc", + "Ч_буква": "Ch", + "Ш_буква": "Sh", + "Щ_буква": "Shch", + "Ы_буква": "Y", + "Э_буква": "E", + "Ю_буква": "Iu", + "Я_буква": "Ia", + "Ь_буква": "", + "Ъ_буква": "", +} + +with open("cyrillic.txt", encoding="utf-8") as my_file: + text = my_file.read() + +transformed_text = "" + +for letter in text: + upper_letter = letter.upper() + + key = f"{upper_letter}_буква" + + if key not in LETTERS: + transformed_text += letter + continue + + is_upper = letter.isupper() + + transformed_text += LETTERS[key] if is_upper else LETTERS[key].lower() + + +with open("transliteration.txt", "w", encoding="UTF-8") as file_out: + file_out.write(transformed_text) + +# G + +# %% +file_name = input() + +with open(file_name, encoding="utf-8") as my_file: + nums_iter = chain( + *[line.rstrip("/n").split() for line in my_file.readlines()] + ) + + numbers = [int(num) for num in nums_iter] + +print(len(numbers)) +print(len([num for num in numbers if num > 0])) +print(min(numbers)) +print(max(numbers)) +print(sum(numbers)) +print(round(sum(numbers) / len(numbers), 2)) + +# H + +# %% +file1 = input() +file2 = input() +file3 = input() + +set1: set[str] = set() +set2: set[str] = set() + +with open(file1, encoding="utf-8") as current_file: + set1 = {word for word in current_file.read().split()} + +with open(file2, encoding="utf-8") as current_file: + set2 = {word for word in current_file.read().split()} + + +with open(file3, "w", encoding="utf-8") as file_out: + file_out.write("\n".join(sorted(set1.symmetric_difference(set2)))) + +# I + +# %% +file_in_i = input() +file_out_i = input() + +result: list[list[str]] = [] + +with open(file_in_i, encoding="utf-8") as file: + for line in file: + result.append(line.strip().replace("\t", "").split()) + + +with open(file_out_i, "w", encoding="utf-8") as file: + file.writelines([" ".join(line) + "\n" for line in result if line]) + +# J + +# %% +file_name = input() +lines_amount = int(input()) + +with open(file_name, encoding="utf-8") as file: + display_lines = file.readlines()[-lines_amount:] + print("".join(display_lines)) + +# K + +# %% +file_in = input() +file_out_k = input() + +with open(file_in, encoding="utf-8") as my_file: + nums = chain(*[line.rstrip("/n").split() for line in my_file.readlines()]) + numbers = [int(num) for num in nums] + + +statistic: dict[str, int | float] = { + "count": len(numbers), + "positive_count": len([num for num in numbers if num > 0]), + "min": min(numbers), + "max": max(numbers), + "sum": sum(numbers), + "average": round(sum(numbers) / len(numbers), 2), +} + +with open(file_out_k, "w", encoding="UTF-8") as my_file: + json.dump(statistic, my_file, ensure_ascii=False, indent=2) + +# L + +# %% +file_1 = input() +file_2 = input() +file_3 = input() +file_4 = input() + +evens: list[str] = [] +odds: list[str] = [] +equals: list[str] = [] + +even_digits = "02468" +odd_digits = "13579" + +with open(file_1, encoding="utf-8") as my_file: + nums_lines = my_file.readlines() + + for line in nums_lines: + + temp_evens: list[str] = [] + temp_odds: list[str] = [] + temp_equals: list[str] = [] + + for number in line.split(): + evens_count = len([i for i in number if i in even_digits]) + odd_count = len([i for i in number if i in odd_digits]) + + if evens_count > odd_count: + temp_evens.append(number) + elif evens_count < odd_count: + temp_odds.append(number) + else: + temp_equals.append(number) + + evens.append(" ".join(temp_evens) + "\n") + odds.append(" ".join(temp_odds) + "\n") + equals.append(" ".join(temp_equals) + "\n") + + with open(file_2, "w", encoding="UTF-8") as file: + file.writelines(evens) + with open(file_3, "w", encoding="UTF-8") as file: + file.writelines(odds) + with open(file_4, "w", encoding="UTF-8") as file: + file.writelines(equals) + +# M + +# %% +file_name = input() + +result_m: dict[str, str] + +with open(file_name, encoding="utf-8") as file: + result_m = json.load(file) + +for line in stdin.readlines(): + + key, value = line.strip().split(" == ") + result_m[key] = value + + +with open(file_name, mode="w", encoding="utf-8") as my_file: + json.dump(result_m, my_file, ensure_ascii=False, indent=2) + +# N + +# %% +file_1 = input() +file_2 = input() + +result_n: dict[str, dict[str, str]] = {} + +with open(file_1, encoding="utf-8") as file: + data = json.load(file) + +with open(file_2, encoding="utf-8") as file: + update = json.load(file) + +for user in data: + name = user.pop("name") + + result_n[name] = user + + +for user in update: + name = user.pop("name") + + if name not in result_n: + result_n[name] = {} + + for key in user: + print(key) + should_up = ( + key not in result_n[name] or user[key] > result_n[name][key] + ) + + if should_up: + result_n[name][key] = user[key] + +with open(file_1, mode="w", encoding="utf-8") as my_file: + json.dump(result_n, my_file, ensure_ascii=False, indent=2) + +# O + +# %% +answers = (answer.strip() for answer in stdin.readlines()) + +file_name = "scoring.json" + +points = 0 + +with open(file_name, encoding="UTF-8") as file: + test_list = json.load(file) + +for group in test_list: + possible_points = group["points"] + + correct_amount = 0 + + for test in group["tests"]: + current_answer = next(answers) + + correct_answer = test["pattern"] + + if current_answer == correct_answer: + correct_amount += 1 + + group_points = (possible_points / len(group["tests"])) * correct_amount + + points += group_points + +print(int(points)) + +# P + +# %% +request = " ".join(input().lower().split()) + +result_p: list[str] = [] + +for file_p in stdin: + with open(file_p.strip(), encoding="UTF-8") as my_file: + if request in " ".join(my_file.read().lower().split()): + result_p.append(file_p.strip()) + +print("\n".join(result_p) if result_p else "404. Not Found") + +# Q + +# %% +decoded_text = "" + +with open("secret.txt", encoding="UTF-8") as file: + coded_text = file.read() + + for char in coded_text: + code = ord(char) + code = code % 256 if code >= 128 else code + decoded_text += chr(code) + + +print(decoded_text) + +# R + +# %% +file_name = input() + +with open(file_name, encoding="UTF-8") as file: + file.seek(0, 2) + position = file.tell() + +size: float = position + +postfix = ["Б", "КБ", "МБ", "ГБ", "ТБ"] +index = 0 + +while size > 1024 and index < len(postfix) - 1: + index += 1 + size = size / 1024 + +print(f"{math.ceil(size)}{postfix[index]}") + +# S + +# %% +offset = int(input()) % 26 + +encoded = "" + +a_code = ord("a") +z_code = ord("z") + +with open("public.txt", encoding="UTF-8") as file: + text = file.read() + + for char in text: + lower = char.lower() + + is_upper = char.isupper() + + code = ord(lower) + + if code < a_code or code > z_code: + encoded += char + continue + + new_code = code + offset + + if new_code > z_code: + new_code -= 26 + if new_code < a_code: + new_code += 26 + + encoded += chr(new_code).upper() if is_upper else chr(new_code) + + +with open("private.txt", mode="w", encoding="UTF-8") as file: + file.write(encoded) + +# T diff --git a/Python/yandex-python-handbook/chapter_4_1.ipynb b/Python/yandex-python-handbook/chapter_4_1.ipynb new file mode 100644 index 00000000..57946c52 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_1.ipynb @@ -0,0 +1,909 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 4.1.\n", + "\n", + "Функции. Области видимости. Передача параметров в функции.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import chain\n", + "from typing import Literal, Union\n", + "\n", + "\n", + "def print_hello(name: str) -> None:\n", + " \"\"\"\n", + " Выводит приветственное сообщение с указанным именем.\n", + "\n", + " Args:\n", + " name (str): Имя пользователя, которое будет включено в приветствие.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " print(f\"Hello, {name}!\")" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "def gcd(a_value: int, b_value: int) -> int:\n", + " \"\"\"\n", + " Фукнция возвращает НОД двух целых чисел.\n", + "\n", + " Args:\n", + " a_value (int): Первое целое число.\n", + " b_value (int): Второе целое число.\n", + "\n", + " Returns:\n", + " int: НОД чисел a_value и b_value.\n", + " \"\"\"\n", + " while b_value:\n", + " a_value, b_value = b_value, a_value % b_value\n", + "\n", + " return a_value" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "def number_length(number: int) -> int:\n", + " \"\"\"\n", + " Функция подсчитывает количество цифр в числе.\n", + "\n", + " Args:\n", + " number (int): Целое число.\n", + "\n", + " Returns:\n", + " int: Количество цифр в числе number.\n", + " \"\"\"\n", + " return len(str(abs(number)))" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "def take_small(money: list[int]) -> list[int]:\n", + " \"\"\"\n", + " Фильтрует список номиналов.\n", + "\n", + " Функция принимает список номиналов и возвращает список номиналов,\n", + " которые меньше 100.\n", + "\n", + " Args:\n", + " money (list[int]): Список номиналов.\n", + "\n", + " Returns:\n", + " list[int]: Список номиналов, которые меньше 100.\n", + " \"\"\"\n", + " return [num for num in money if num < 100]" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "counter = 0\n", + "\n", + "\n", + "def click() -> None:\n", + " \"\"\"\n", + " Функция увеличивает глобальную переменную counter на 1.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " # Требуется по условию задачи\n", + " # pylint: disable=global-statement\n", + " global counter\n", + " counter += 1\n", + "\n", + "\n", + "def get_count() -> int:\n", + " \"\"\"\n", + " Функция возвращает текущее значение глобальной переменной counter.\n", + "\n", + " Returns:\n", + " int: Текущее значение глобальной переменной counter.\n", + " \"\"\"\n", + " return counter" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "counter = 0\n", + "\n", + "\n", + "def move(name: Literal[\"Ваня\", \"Петя\"], number: int) -> None:\n", + " \"\"\"\n", + " Функция изменят глобальную переменную counter на number.\n", + "\n", + " В зависимости от имени игрока увеличивает или уменьшает counter.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " # Требуется по условию задачи\n", + " # pylint: disable=global-statement\n", + " global counter\n", + "\n", + " if name == \"Петя\":\n", + " counter += number\n", + " else:\n", + " counter -= number\n", + "\n", + "\n", + "def game_over() -> Literal[\"Ваня\", \"Петя\", \"Ничья\"]:\n", + " \"\"\"\n", + " Функция возвращает результат игры.\n", + "\n", + " Returns:\n", + " Literal[\"Ваня\" , \"Петя\" , \"Ничья\"]: Имя игрока или 'Ничья'.\n", + " \"\"\"\n", + " match counter:\n", + " case num if num > 0:\n", + " return \"Петя\"\n", + " case num if num < 0:\n", + " return \"Ваня\"\n", + " case _:\n", + " return \"Ничья\"" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "def max_2d(matrix: list[list[int]]) -> int:\n", + " \"\"\"\n", + " Функция находит максимальных элемент в матрице.\n", + "\n", + " Args:\n", + " matrix (list[list[int]]): Двумерная матрица целых чисел.\n", + "\n", + " Returns:\n", + " int: Максимальных элемент в матрице.\n", + " \"\"\"\n", + " return max(chain(*matrix))" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "def fragments(numbers: list[int]) -> list[list[int]]:\n", + " \"\"\"\n", + " Возвращает список возрастающих списков.\n", + "\n", + " Функция принимает список целых чисел и разбивает его на несколько списков,\n", + " каждый из которых представляет собой возрастающий список элементов\n", + "\n", + " Args:\n", + " matrix (list[int]): Список целых чисел.\n", + "\n", + " Returns:\n", + " int: Список списков целых чисел.\n", + " \"\"\"\n", + " result: list[list[int]] = []\n", + "\n", + " temp: list[int] = []\n", + "\n", + " for index, number in enumerate(numbers):\n", + " temp.append(number)\n", + " print(temp)\n", + "\n", + " if index == len(numbers) - 1 or number >= numbers[index + 1]:\n", + " result.append(temp)\n", + " temp = []\n", + "\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "def month(number: int, language: Literal[\"ru\", \"en\"]) -> str:\n", + " \"\"\"\n", + " Функция для получения названия месяца.\n", + "\n", + " Функция возвращает имя месяца в соответствии с переданными номером месяца\n", + " и языком\n", + "\n", + " Args:\n", + " number (int): Номер месяца от 1 до 12.\n", + " language (Literal[\"ru\", \"en\"]): Язык.\n", + "\n", + " Returns:\n", + " str: Название месяца.\n", + " \"\"\"\n", + " months_dict: dict[str, list[str]] = {\n", + " \"en\": [\n", + " \"January month\",\n", + " \"February month\",\n", + " \"March month\",\n", + " \"April month\",\n", + " \"May month\",\n", + " \"June month\",\n", + " \"July month\",\n", + " \"August month\",\n", + " \"September month\",\n", + " \"October month\",\n", + " \"November month\",\n", + " \"December month\",\n", + " ],\n", + " \"ru\": [\n", + " \"Январь месяц\",\n", + " \"Февраль месяц\",\n", + " \"Март месяц\",\n", + " \"Апрель месяц\",\n", + " \"Май месяц\",\n", + " \"Июнь месяц\",\n", + " \"Июль месяц\",\n", + " \"Август месяц\",\n", + " \"Сентябрь месяц\",\n", + " \"Октябрь месяц\",\n", + " \"Ноябрь месяц\",\n", + " \"Декабрь месяц\",\n", + " ],\n", + " }\n", + "\n", + " return months_dict[language][number - 1]" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "def split_numbers(string: str) -> tuple[int, ...]:\n", + " \"\"\"\n", + " Преобразование строки чисел в кортеж чисел.\n", + "\n", + " Функция принимает строку, представляющую собой целые числа,\n", + " разделенные пробелами.\n", + " Возвращает кортеж этих чисел.\n", + "\n", + " Args:\n", + " string (str): Строка целых чисел, разделенных пробелами.\n", + "\n", + " Returns:\n", + " tuple(int): Кортеж чисел из строки string.\n", + " \"\"\"\n", + " return tuple(map(int, string.split()))" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "def find_mountains_k(heights: list[int]) -> tuple[int, ...]:\n", + " \"\"\"\n", + " Функция принимает список высот.\n", + "\n", + " Возвращает кортеж \"гор\" - высот, которые больше своих ближайших соседей\n", + "\n", + " Args:\n", + " heights (list[int]): Список высот\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Список \"гор\"\n", + " \"\"\"\n", + " result: list[int] = []\n", + "\n", + " neighbours = zip(heights, heights[1:], heights[2:])\n", + "\n", + " for index, currents in enumerate(neighbours):\n", + " left, middle, right = currents\n", + "\n", + " if left < middle and middle > right:\n", + " result.append(index + 2)\n", + "\n", + " return tuple(result)" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "def find_mountains(heights: list[list[int]]) -> tuple[tuple[int, ...], ...]:\n", + " \"\"\"\n", + " Функция принимает двумерную матрицу - список высот.\n", + "\n", + " Возвращает кортеж кортежей \"гор\" - пар, обозначающих координаты высот\n", + " в матрице, окруженных более низкими соседями\n", + "\n", + " Args:\n", + " heights (list[list[int]]): Список высот\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Список \"гор\"\n", + " \"\"\"\n", + " result: list[tuple[int, int]] = []\n", + "\n", + " max_y = len(heights) - 1\n", + " max_x = len(heights[0]) - 1\n", + "\n", + " for y_coord in range(1, max_y):\n", + " for x_coord in range(1, max_x):\n", + " current = heights[y_coord][x_coord]\n", + "\n", + " conditions = [\n", + " heights[y_coord - 1][x_coord] < current,\n", + " heights[y_coord + 1][x_coord] < current,\n", + " heights[y_coord][x_coord - 1] < current,\n", + " heights[y_coord][x_coord + 1] < current,\n", + " heights[y_coord + 1][x_coord + 1] < current,\n", + " heights[y_coord + 1][x_coord - 1] < current,\n", + " heights[y_coord - 1][x_coord + 1] < current,\n", + " heights[y_coord - 1][x_coord + 1] < current,\n", + " ]\n", + "\n", + " if all(conditions):\n", + " result.append((y_coord + 1, x_coord + 1))\n", + "\n", + " return tuple(result)" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "cache: set[str] = set()\n", + "\n", + "\n", + "def modern_print(text: str) -> None:\n", + " \"\"\"\n", + " Печатает текст, только если он ещё не был напечатан.\n", + "\n", + " Args:\n", + " text (str): Текст, который нужно напечатать.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " if text in cache:\n", + " return\n", + "\n", + " print(text)\n", + " cache.add(text)" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "def can_eat(horse: tuple[int, int], other: tuple[int, int]) -> bool:\n", + " \"\"\"\n", + " Проверят, может ли конь \"съесть\" другую фигуру.\n", + "\n", + " Функция получает координаты коня и другой фигуры.\n", + " И проверят, может ли конь съесть другую фигуру,\n", + " исходя из того, что конь ходит на две клетки по горизонтали\n", + " или вертикали. А затем на одну клетку по другому направлению.\n", + "\n", + " Args:\n", + " horse (tuple[int, int]): Координаты коня.\n", + " other (tuple[int, int]): Координаты другой фигуры.\n", + "\n", + " Returns:\n", + " bool: True, если конь может съесть фигуру; иначе False.\n", + " \"\"\"\n", + " (horse_x, horse_y), (other_x, other_y) = horse, other\n", + "\n", + " conditions: list[bool] = [\n", + " abs(horse_x - other_x) == 1 and (abs(horse_y - other_y)) == 2,\n", + " abs(horse_x - other_x) == 2 and (abs(horse_y - other_y)) == 1,\n", + " ]\n", + "\n", + " return any(conditions)" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "Value = Union[int | float | str]\n", + "MyDict = dict[str, Value]\n", + "\n", + "\n", + "def get_dict(text: str) -> dict[str, int | float | str]:\n", + " \"\"\"\n", + " Преобразует переданную строку текста в словарь.\n", + "\n", + " Разбивает переданную строку по символу ;.\n", + " Получившиеся строки разбивает по символу =.\n", + " Первый элемент списка становится ключом, второй - значением.\n", + " Если возможно, значение будет преобразовано к int или float.\n", + "\n", + " Args:\n", + " text (str): Входная строка для преобразования.\n", + "\n", + " Returns:\n", + " dict[str, int | float | str]: Словарь, полученный после трансформации\n", + " строки.\n", + " \"\"\"\n", + " my_dict: MyDict = {}\n", + " for string in text.split(\";\"):\n", + " key, value = string.split(\"=\")\n", + "\n", + " float_conditions: list[bool] = [\n", + " value.count(\".\") == 1,\n", + " value.lstrip(\"+-\").replace(\".\", \"\").isdigit(),\n", + " ]\n", + "\n", + " if value.lstrip(\"+-\").isdigit():\n", + " formatted: Value = int(value)\n", + " elif all(float_conditions):\n", + " formatted = float(value)\n", + " else:\n", + " formatted = value\n", + "\n", + " my_dict[key] = formatted\n", + "\n", + " return my_dict" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "def is_palindrome(value: str | int | list[int] | tuple[int]) -> bool:\n", + " \"\"\"\n", + " Преобразует переданную строку текста в словарь.\n", + "\n", + " Разбивает переданную строку по символу ;.\n", + " Получившиеся строки разбивает по символу =.\n", + " Первый элемент списка становится ключом, второй - значением.\n", + " Если возможно, значение будет преобразовано к int или float.\n", + "\n", + " Args:\n", + " text (str): Входная строка для преобразования.\n", + "\n", + " Returns:\n", + " dict[str, int | float | str]: Словарь, полученный после трансформации\n", + " строки.\n", + " \"\"\"\n", + "\n", + " def is_alpha_or_digit(char: str) -> bool:\n", + " \"\"\"\n", + " Проверят, является ли переданный символ буквой или цифрой.\n", + "\n", + " Args:\n", + " char (str): Символ для проверки.\n", + "\n", + " Returns:\n", + " bool: True, если символ буква или цифра. Иначе False.\n", + " \"\"\"\n", + " return any([char.isalpha(), char.isdigit()])\n", + "\n", + " if isinstance(value, (list, tuple)):\n", + " text = \"\"\n", + "\n", + " for item in value:\n", + " text += str(item)\n", + "\n", + " else:\n", + " text = str(value)\n", + "\n", + " sanitized_text = [char for char in text if is_alpha_or_digit(char)]\n", + "\n", + " return sanitized_text == sanitized_text[::-1]" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "def is_prime(number: int) -> bool:\n", + " \"\"\"\n", + " Проверяет, является ли переданное число простым.\n", + "\n", + " Args:\n", + " number (int): Число для проверки.\n", + "\n", + " Returns:\n", + " bool: True, если число простое. Иначе False.\n", + " \"\"\"\n", + " if number < 2:\n", + " return False\n", + "\n", + " for divider in range(2, int(number**0.5) + 1):\n", + " if number % divider == 0:\n", + " return False\n", + "\n", + " return True" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "def merge(tuple1: tuple[int, ...], tuple2: tuple[int, ...]) -> tuple[int, ...]:\n", + " \"\"\"\n", + " Объединяет два отсортированных кортежа в один отсортированный.\n", + "\n", + " Args:\n", + " tuple1 (tuple[int, ...]): Первый кортеж отсортированных чисел.\n", + " tuple1 (tuple[int, ...]): Второй кортеж отсортированных чисел.\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Отсортированный по возрастанию Кортеж целых чисел,\n", + " содержащий все числа из первого и второго кортежа\n", + " \"\"\"\n", + " list1 = list(tuple1)\n", + " list2 = list(tuple2)\n", + "\n", + " result: list[int] = []\n", + "\n", + " while list1 and list2:\n", + " if list1[0] < list2[0]:\n", + " result.append(list1.pop(0))\n", + " else:\n", + " result.append(list2.pop(0))\n", + "\n", + " result.extend(list1)\n", + " result.extend(list2)\n", + "\n", + " return tuple(result)\n", + "\n", + "\n", + "print(merge((), (1, 2)))" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "def swap(list1: list[int], list2: list[int]) -> None:\n", + " \"\"\"\n", + " Меняет местами содержимое двух массивов.\n", + "\n", + " Args:\n", + " list1 (list): Первый массив.\n", + " list2 (list): Второй массив.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " len1 = len(list1)\n", + " len2 = len(list2)\n", + "\n", + " list1.extend(list2)\n", + " list2.extend(list1[:len1])\n", + "\n", + " del list1[:len1]\n", + " del list2[:len2]" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "def convert_to_roman(num: int) -> str:\n", + " \"\"\"\n", + " Конвертирует натуральное число в римское.\n", + "\n", + " Функция конвертирует переданное натуральное число,\n", + " записанное арабскими цифрами, в римское.\n", + "\n", + " Args:\n", + " num1 (int): Натуральное число.\n", + "\n", + " Returns:\n", + " str: Римское представление переданного числа.\n", + " \"\"\"\n", + " romans: list[tuple[int, str]] = [\n", + " (1000, \"M\"),\n", + " (900, \"CM\"),\n", + " (500, \"D\"),\n", + " (400, \"CD\"),\n", + " (100, \"C\"),\n", + " (90, \"XC\"),\n", + " (50, \"L\"),\n", + " (40, \"XL\"),\n", + " (10, \"X\"),\n", + " (9, \"IX\"),\n", + " (5, \"V\"),\n", + " (4, \"IV\"),\n", + " (1, \"I\"),\n", + " ]\n", + "\n", + " result = \"\"\n", + "\n", + " for arabic, roman_char in romans:\n", + " if arabic > num:\n", + " continue\n", + "\n", + " repeat = num // arabic\n", + " result += roman_char * repeat\n", + " num = num - arabic * repeat\n", + "\n", + " if num == 0:\n", + " break\n", + "\n", + " return result\n", + "\n", + "\n", + "def roman(num1: int, num2: int) -> str:\n", + " \"\"\"\n", + " Вычисляет римскую сумму двух чисел.\n", + "\n", + " Фунция принимает два натуральных числа.\n", + " Возвращает их сумму в строке вида:\n", + " РИМСКОЕ_А + РИМСКОЕ_B = РИМСКАЯ_СУММА\n", + "\n", + "\n", + " Args:\n", + " num1 (int): Первое натуральное число.\n", + " num1 (int): Второе натуральное число.\n", + "\n", + " Returns:\n", + " str: Римская сумма двух чисел\n", + " \"\"\"\n", + " roman1 = convert_to_roman(num1)\n", + " roman2 = convert_to_roman(num2)\n", + " result = convert_to_roman(num1 + num2)\n", + "\n", + " return f\"{roman1} + {roman2} = {result}\"" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_4_1.py b/Python/yandex-python-handbook/chapter_4_1.py new file mode 100644 index 00000000..a6a6df4f --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_1.py @@ -0,0 +1,626 @@ +# %% +"""Задания к главе 4.1. + +Функции. Области видимости. Передача параметров в функции. + +Хендбук Яндекс "Основы Python". +""" + +# A + +# %% +from itertools import chain +from typing import Literal, Union + + +def print_hello(name: str) -> None: + """Выводит приветственное сообщение с указанным именем. + + Args: + name (str): Имя пользователя, которое будет включено в приветствие. + + Returns: + None: Функция ничего не возвращает. + """ + print(f"Hello, {name}!") + + +# B + + +# %% +def gcd(a_value: int, b_value: int) -> int: + """Фукнция возвращает НОД двух целых чисел. + + Args: + a_value (int): Первое целое число. + b_value (int): Второе целое число. + + Returns: + int: НОД чисел a_value и b_value. + """ + while b_value: + a_value, b_value = b_value, a_value % b_value + + return a_value + + +# C + + +# %% +def number_length(number: int) -> int: + """Функция подсчитывает количество цифр в числе. + + Args: + number (int): Целое число. + + Returns: + int: Количество цифр в числе number. + """ + return len(str(abs(number))) + + +# D + + +# %% +def take_small(money: list[int]) -> list[int]: + """Фильтрует список номиналов. + + Функция принимает список номиналов и возвращает список номиналов, + которые меньше 100. + + Args: + money (list[int]): Список номиналов. + + Returns: + list[int]: Список номиналов, которые меньше 100. + """ + return [num for num in money if num < 100] + + +# E + +# %% +counter = 0 + + +def click() -> None: + """Функция увеличивает глобальную переменную counter на 1. + + Returns: + None: Функция ничего не возвращает. + """ + # Требуется по условию задачи + # pylint: disable=global-statement + global counter + counter += 1 + + +def get_count() -> int: + """Функция возвращает текущее значение глобальной переменной counter. + + Returns: + int: Текущее значение глобальной переменной counter. + """ + return counter + + +# F + +# %% +counter = 0 + + +def move(name: Literal["Ваня", "Петя"], number: int) -> None: + """Функция изменят глобальную переменную counter на number. + + В зависимости от имени игрока увеличивает или уменьшает counter. + + Returns: + None: Функция ничего не возвращает. + """ + # Требуется по условию задачи + # pylint: disable=global-statement + global counter + + if name == "Петя": + counter += number + else: + counter -= number + + +def game_over() -> Literal["Ваня", "Петя", "Ничья"]: + """Функция возвращает результат игры. + + Returns: + Literal["Ваня" , "Петя" , "Ничья"]: Имя игрока или 'Ничья'. + """ + match counter: + case num if num > 0: + return "Петя" + case num if num < 0: + return "Ваня" + case _: + return "Ничья" + + +# G + + +# %% +def max_2d(matrix: list[list[int]]) -> int: + """Функция находит максимальных элемент в матрице. + + Args: + matrix (list[list[int]]): Двумерная матрица целых чисел. + + Returns: + int: Максимальных элемент в матрице. + """ + return max(chain(*matrix)) + + +# H + + +# %% +def fragments(numbers: list[int]) -> list[list[int]]: + """Возвращает список возрастающих списков. + + Функция принимает список целых чисел и разбивает его на несколько списков, + каждый из которых представляет собой возрастающий список элементов + + Args: + matrix (list[int]): Список целых чисел. + + Returns: + int: Список списков целых чисел. + """ + result: list[list[int]] = [] + + temp: list[int] = [] + + for index, number in enumerate(numbers): + temp.append(number) + print(temp) + + if index == len(numbers) - 1 or number >= numbers[index + 1]: + result.append(temp) + temp = [] + + return result + + +# I + + +# %% +def month(number: int, language: Literal["ru", "en"]) -> str: + """Функция для получения названия месяца. + + Функция возвращает имя месяца в соответствии с переданными номером месяца + и языком + + Args: + number (int): Номер месяца от 1 до 12. + language (Literal["ru", "en"]): Язык. + + Returns: + str: Название месяца. + """ + months_dict: dict[str, list[str]] = { + "en": [ + "January month", + "February month", + "March month", + "April month", + "May month", + "June month", + "July month", + "August month", + "September month", + "October month", + "November month", + "December month", + ], + "ru": [ + "Январь месяц", + "Февраль месяц", + "Март месяц", + "Апрель месяц", + "Май месяц", + "Июнь месяц", + "Июль месяц", + "Август месяц", + "Сентябрь месяц", + "Октябрь месяц", + "Ноябрь месяц", + "Декабрь месяц", + ], + } + + return months_dict[language][number - 1] + + +# J + + +# %% +def split_numbers(string: str) -> tuple[int, ...]: + """Преобразование строки чисел в кортеж чисел. + + Функция принимает строку, представляющую собой целые числа, + разделенные пробелами. + Возвращает кортеж этих чисел. + + Args: + string (str): Строка целых чисел, разделенных пробелами. + + Returns: + tuple(int): Кортеж чисел из строки string. + """ + return tuple(map(int, string.split())) + + +# K + + +# %% +def find_mountains_k(heights: list[int]) -> tuple[int, ...]: + """Функция принимает список высот. + + Возвращает кортеж "гор" - высот, которые больше своих ближайших соседей + + Args: + heights (list[int]): Список высот + + Returns: + tuple[int, ...]: Список "гор" + """ + result: list[int] = [] + + neighbours = zip(heights, heights[1:], heights[2:]) + + for index, currents in enumerate(neighbours): + left, middle, right = currents + + if left < middle and middle > right: + result.append(index + 2) + + return tuple(result) + + +# L + + +# %% +def find_mountains(heights: list[list[int]]) -> tuple[tuple[int, ...], ...]: + """ + Функция принимает двумерную матрицу - список высот. + + Возвращает кортеж кортежей "гор" - пар, обозначающих координаты высот + в матрице, окруженных более низкими соседями + + Args: + heights (list[list[int]]): Список высот + + Returns: + tuple[int, ...]: Список "гор" + """ + result: list[tuple[int, int]] = [] + + max_y = len(heights) - 1 + max_x = len(heights[0]) - 1 + + for y_coord in range(1, max_y): + for x_coord in range(1, max_x): + current = heights[y_coord][x_coord] + + conditions = [ + heights[y_coord - 1][x_coord] < current, + heights[y_coord + 1][x_coord] < current, + heights[y_coord][x_coord - 1] < current, + heights[y_coord][x_coord + 1] < current, + heights[y_coord + 1][x_coord + 1] < current, + heights[y_coord + 1][x_coord - 1] < current, + heights[y_coord - 1][x_coord + 1] < current, + heights[y_coord - 1][x_coord + 1] < current, + ] + + if all(conditions): + result.append((y_coord + 1, x_coord + 1)) + + return tuple(result) + + +# M + +# %% +cache: set[str] = set() + + +def modern_print(text: str) -> None: + """Печатает текст, только если он ещё не был напечатан. + + Args: + text (str): Текст, который нужно напечатать. + + Returns: + None: Функция ничего не возвращает. + """ + if text in cache: + return + + print(text) + cache.add(text) + + +# N + + +# %% +def can_eat(horse: tuple[int, int], other: tuple[int, int]) -> bool: + """Проверят, может ли конь "съесть" другую фигуру. + + Функция получает координаты коня и другой фигуры. + И проверят, может ли конь съесть другую фигуру, + исходя из того, что конь ходит на две клетки по горизонтали + или вертикали. А затем на одну клетку по другому направлению. + + Args: + horse (tuple[int, int]): Координаты коня. + other (tuple[int, int]): Координаты другой фигуры. + + Returns: + bool: True, если конь может съесть фигуру; иначе False. + """ + (horse_x, horse_y), (other_x, other_y) = horse, other + + conditions: list[bool] = [ + abs(horse_x - other_x) == 1 and (abs(horse_y - other_y)) == 2, + abs(horse_x - other_x) == 2 and (abs(horse_y - other_y)) == 1, + ] + + return any(conditions) + + +# O + +# %% +Value = Union[int | float | str] +MyDict = dict[str, Value] + + +def get_dict(text: str) -> dict[str, int | float | str]: + """Преобразует переданную строку текста в словарь. + + Разбивает переданную строку по символу ;. + Получившиеся строки разбивает по символу =. + Первый элемент списка становится ключом, второй - значением. + Если возможно, значение будет преобразовано к int или float. + + Args: + text (str): Входная строка для преобразования. + + Returns: + dict[str, int | float | str]: Словарь, полученный после трансформации + строки. + """ + my_dict: MyDict = {} + for string in text.split(";"): + key, value = string.split("=") + + float_conditions: list[bool] = [ + value.count(".") == 1, + value.lstrip("+-").replace(".", "").isdigit(), + ] + + if value.lstrip("+-").isdigit(): + formatted: Value = int(value) + elif all(float_conditions): + formatted = float(value) + else: + formatted = value + + my_dict[key] = formatted + + return my_dict + + +# P + + +# %% +def is_palindrome(value: str | int | list[int] | tuple[int]) -> bool: + """Преобразует переданную строку текста в словарь. + + Разбивает переданную строку по символу ;. + Получившиеся строки разбивает по символу =. + Первый элемент списка становится ключом, второй - значением. + Если возможно, значение будет преобразовано к int или float. + + Args: + text (str): Входная строка для преобразования. + + Returns: + dict[str, int | float | str]: Словарь, полученный после трансформации + строки. + """ + + def is_alpha_or_digit(char: str) -> bool: + """Проверят, является ли переданный символ буквой или цифрой. + + Args: + char (str): Символ для проверки. + + Returns: + bool: True, если символ буква или цифра. Иначе False. + """ + return any([char.isalpha(), char.isdigit()]) + + if isinstance(value, (list, tuple)): + text = "" + + for item in value: + text += str(item) + + else: + text = str(value) + + sanitized_text = [char for char in text if is_alpha_or_digit(char)] + + return sanitized_text == sanitized_text[::-1] + + +# Q + + +# %% +def is_prime(number: int) -> bool: + """Проверяет, является ли переданное число простым. + + Args: + number (int): Число для проверки. + + Returns: + bool: True, если число простое. Иначе False. + """ + if number < 2: + return False + + for divider in range(2, int(number**0.5) + 1): + if number % divider == 0: + return False + + return True + + +# R + + +# %% +def merge(tuple1: tuple[int, ...], tuple2: tuple[int, ...]) -> tuple[int, ...]: + """Объединяет два отсортированных кортежа в один отсортированный. + + Args: + tuple1 (tuple[int, ...]): Первый кортеж отсортированных чисел. + tuple1 (tuple[int, ...]): Второй кортеж отсортированных чисел. + + Returns: + tuple[int, ...]: Отсортированный по возрастанию Кортеж целых чисел, + содержащий все числа из первого и второго кортежа + """ + list1 = list(tuple1) + list2 = list(tuple2) + + result: list[int] = [] + + while list1 and list2: + if list1[0] < list2[0]: + result.append(list1.pop(0)) + else: + result.append(list2.pop(0)) + + result.extend(list1) + result.extend(list2) + + return tuple(result) + + +print(merge((), (1, 2))) + + +# S + + +# %% +def swap(list1: list[int], list2: list[int]) -> None: + """Меняет местами содержимое двух массивов. + + Args: + list1 (list): Первый массив. + list2 (list): Второй массив. + + Returns: + None: Функция ничего не возвращает. + """ + len1 = len(list1) + len2 = len(list2) + + list1.extend(list2) + list2.extend(list1[:len1]) + + del list1[:len1] + del list2[:len2] + + +# T + + +# %% +def convert_to_roman(num: int) -> str: + """Конвертирует натуральное число в римское. + + Функция конвертирует переданное натуральное число, + записанное арабскими цифрами, в римское. + + Args: + num1 (int): Натуральное число. + + Returns: + str: Римское представление переданного числа. + """ + romans: list[tuple[int, str]] = [ + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I"), + ] + + result = "" + + for arabic, roman_char in romans: + if arabic > num: + continue + + repeat = num // arabic + result += roman_char * repeat + num = num - arabic * repeat + + if num == 0: + break + + return result + + +def roman(num1: int, num2: int) -> str: + """Вычисляет римскую сумму двух чисел. + + Фунция принимает два натуральных числа. + Возвращает их сумму в строке вида: + РИМСКОЕ_А + РИМСКОЕ_B = РИМСКАЯ_СУММА + + + Args: + num1 (int): Первое натуральное число. + num1 (int): Второе натуральное число. + + Returns: + str: Римская сумма двух чисел + """ + roman1 = convert_to_roman(num1) + roman2 = convert_to_roman(num2) + result = convert_to_roman(num1 + num2) + + return f"{roman1} + {roman2} = {result}" diff --git a/Python/yandex-python-handbook/chapter_4_2.ipynb b/Python/yandex-python-handbook/chapter_4_2.ipynb new file mode 100644 index 00000000..b5621694 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_2.ipynb @@ -0,0 +1,891 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 4.2.\n", + "\n", + "Позиционные и именованные аргументы.\n", + "Функции высших порядков.\n", + "Лямбда-функции\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "import operator\n", + "from datetime import datetime\n", + "from itertools import cycle\n", + "from typing import Callable, Literal, TypeVar\n", + "\n", + "\n", + "def make_list(length: int, value: int = 0) -> list[int]:\n", + " \"\"\"\n", + " Создает список указанной длины, заполненный переданным значением.\n", + "\n", + " Args:\n", + " length (int): Длина списка.\n", + " value (int): Элемент для заполнения.\n", + "\n", + " Returns:\n", + " list[int]: Список длины length, заполненный значением value.\n", + " \"\"\"\n", + " return [value] * length" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "Size = int | tuple[int, int]\n", + "Matrix = list[list[int]]\n", + "\n", + "\n", + "def make_matrix(size: Size, value: int = 0) -> Matrix:\n", + " \"\"\"\n", + " Создает матрицу указанного размера, заполненную переданным значением.\n", + "\n", + " Args:\n", + " size (int | tuple[int, int]): Размер матрицы. Если кортеж, то\n", + " (ширина, высота). Если одно значение, то создаем квадратную матрицу\n", + " value (int): Элемент для заполнения.\n", + "\n", + " Returns:\n", + " list[int]: Матрица размера size, заполненная значением value.\n", + " \"\"\"\n", + " if isinstance(size, int):\n", + " width = size\n", + " height = size\n", + " else:\n", + " width = size[0]\n", + " height = size[1]\n", + "\n", + " return [[value] * width for _ in range(height)]" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "def gcd(*numbers: int) -> int:\n", + " \"\"\"\n", + " Вычисляет НОД для переданных чисел.\n", + "\n", + " Args:\n", + " *numbers (int): Одно или более целое число. Не должно быть пустым.\n", + "\n", + " Returns:\n", + " int: НОД переданных чисел.\n", + " \"\"\"\n", + " divider = numbers[0]\n", + "\n", + " for index in range(1, len(numbers), 1):\n", + " next_var = numbers[index]\n", + "\n", + " while next_var:\n", + " divider, next_var = next_var, divider % next_var\n", + "\n", + " return divider" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "months: dict[str, list[str]] = {\n", + " \"ru\": [\n", + " \"Январь\",\n", + " \"Февраль\",\n", + " \"Март\",\n", + " \"Апрель\",\n", + " \"Май\",\n", + " \"Июнь\",\n", + " \"Июль\",\n", + " \"Август\",\n", + " \"Сентябрь\",\n", + " \"Октябрь\",\n", + " \"Ноябрь\",\n", + " \"Декабрь\",\n", + " ],\n", + " \"en\": [\n", + " \"January\",\n", + " \"February\",\n", + " \"March\",\n", + " \"April\",\n", + " \"May\",\n", + " \"June\",\n", + " \"July\",\n", + " \"August\",\n", + " \"September\",\n", + " \"October\",\n", + " \"November\",\n", + " \"December\",\n", + " ],\n", + "}\n", + "\n", + "\n", + "def month(number: int, language: Literal[\"ru\", \"en\"] = \"ru\") -> str:\n", + " \"\"\"\n", + " Функция для получения названия месяца.\n", + "\n", + " Функция возвращает имя месяца в соответствии с переданными номером месяца\n", + " и языком\n", + "\n", + " Args:\n", + " number (int): Номер месяца от 1 до 12.\n", + " language (Literal[\"ru\", \"en\"]): Язык.\n", + "\n", + " Returns:\n", + " str: Название месяца.\n", + " \"\"\"\n", + " return months[language][number - 1]" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "def to_string(*items: object, sep: str = \" \", end: str = \"\\n\") -> str:\n", + " r\"\"\"\n", + " Функция возвращает строку из переданных элементов.\n", + "\n", + " Элементы разделены сепаратором sep. Заканчивается строкой end.\n", + "\n", + " Args:\n", + " *items (object): Элементы, которые надо объединить в строку.\n", + " sep (str, optional): Разделитель элементов в строке. По умолчанию \" \".\n", + " end (str, optional): Окончание строки. По умолчанию \"\\n\".\n", + "\n", + " Returns:\n", + " str: _description_\n", + " \"\"\"\n", + " return f\"{sep.join(map(str, items))}{end}\"" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "Operator = Literal[\"+\", \"-\", \"*\", \"//\", \"**\"]\n", + "OperatorFn = Callable[[int, int], int]\n", + "\n", + "\n", + "def get_operator_fn(operator_f: Operator) -> OperatorFn:\n", + " \"\"\"\n", + " Возвращает функцию-оператор для двух целых чисел.\n", + "\n", + " Args:\n", + " operator_f (Operator): Математическая операция.\n", + "\n", + " Returns:\n", + " OperatorFn: Функция, принимающая два int и возвращающая int.\n", + " \"\"\"\n", + " match operator_f:\n", + " case \"+\":\n", + " return lambda operand1, operand2: operand1 + operand2\n", + " case \"-\":\n", + " return lambda operand1, operand2: operand1 - operand2\n", + " case \"*\":\n", + " return lambda operand1, operand2: operand1 * operand2\n", + " case \"//\":\n", + " return lambda operand1, operand2: operand1 // operand2\n", + " case \"**\":\n", + " return lambda operand1, operand2: operand1**operand2" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "T = TypeVar(\"T\")\n", + "\n", + "OperatorFnG = Callable[[tuple[object, ...]], str]\n", + "\n", + "\n", + "def get_formatter(sep: str = \" \", end: str = \"\") -> OperatorFnG:\n", + " \"\"\"\n", + " Возвращает функцию, которая объединяет аргументы в строку.\n", + "\n", + " Args:\n", + " sep (str, optional): Разделитель элементов в строке. По умолчанию \" \".\n", + " end (str, optional): Окончание строки. По умолчанию \"\".\n", + "\n", + " Returns:\n", + " OperatorFnG: Функция, принимающая не определенное количество аргументов.\n", + " Возвращает строку, объединяющую эти аргументы через sep и оканчивающуюся\n", + " строкой end.\n", + " \"\"\"\n", + " return lambda *args: f\"{sep.join(map(str, args))}{end}\"" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "def grow(*numbers: int, **kwargs: int) -> tuple[int, ...]:\n", + " \"\"\"\n", + " Преобразует позиционные аргументы.\n", + "\n", + " Функция принимает не определенное количество позиционных аргументов.\n", + " И не определенное количество именованных аргументов.\n", + "\n", + " Возвращает кортеж, состоящий из по аргументов, увеличенных на значение\n", + " именованных параметров, если значение позиционного аргумента кратно\n", + " длине имени именованного аргумента.\n", + "\n", + " Args:\n", + " *numbers (int): Целые числа.\n", + " **kwargs (int): Целые числа.\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Кортеж целых чисел.\n", + " \"\"\"\n", + " result = []\n", + "\n", + " for number in numbers:\n", + " updated = number\n", + "\n", + " for key, value in kwargs.items():\n", + " if number % len(key) == 0:\n", + " updated += value\n", + "\n", + " result.append(updated)\n", + "\n", + " return tuple(result)" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "def product_i(*items: str, **kwargs: int) -> tuple[int, ...]:\n", + " \"\"\"\n", + " Заменяет каждую строку на произведение значений именованных параметров.\n", + "\n", + " Если ни одно имя не найдено — строка игнорируется.\n", + "\n", + " Args:\n", + " *args (str): Строки, которые нужно обработать.\n", + " **kwargs (int): Именованные параметры.\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Кортеж произведений для строк, где нашлись совпадения.\n", + " \"\"\"\n", + " result = []\n", + "\n", + " for item in items:\n", + " product: None | int = None\n", + "\n", + " for key, value in kwargs.items():\n", + " if key in item:\n", + " product = product * value if product is not None else value\n", + "\n", + " if product is not None:\n", + " result.append(product)\n", + "\n", + " return tuple(result)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "def choice(*numbers: int, **kwargs: Callable[[int], int]) -> int:\n", + " \"\"\"\n", + " Применяет переданную функцию ко всем позиционным аргументам.\n", + "\n", + " Если передан именованный параметр min, то возвращает минимальное значение\n", + " из результатов выполнения функции min. Иначе - максимальное из результатов\n", + " выполнения функции max.\n", + "\n", + " Args:\n", + " *numbers (int): Целые числа.\n", + " **kwargs (Function): Функция. Параметр может иметь имя min или max\n", + "\n", + " Returns:\n", + " (int): Целое число\n", + " \"\"\"\n", + " if \"min\" in kwargs:\n", + " return min(map(kwargs[\"min\"], numbers))\n", + "\n", + " return max(map(kwargs[\"max\"], numbers))" + ] + }, + { + "cell_type": "markdown", + "id": "eb8f2ac3", + "metadata": {}, + "source": [ + "K" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57e55079", + "metadata": {}, + "outputs": [], + "source": [ + "in_stock: dict[str, int] = {}\n", + "\n", + "\n", + "def order(*coffees: str) -> str:\n", + " \"\"\"\n", + " Функция возвращает название напитка, который можно приготовить.\n", + "\n", + " Если ни одиннапиток нельзя приготовить, функция возвращает строку:\n", + " \"К сожалению, не можем предложить Вам напиток\"\n", + "\n", + " Args:\n", + " *coffees (str): Список напитков.\n", + "\n", + " Returns:\n", + " (str): Название напитка. Если напиток нельзя приготовить, то строка:\n", + " \"К сожалению, не можем предложить Вам напиток\"\n", + " \"\"\"\n", + " ingidients: dict[str, dict[str, int]] = {\n", + " \"Эспрессо\": {\"coffee\": 1},\n", + " \"Капучино\": {\"coffee\": 1, \"milk\": 3},\n", + " \"Макиато\": {\"coffee\": 2, \"milk\": 1},\n", + " \"Кофе по-венски\": {\"coffee\": 1, \"cream\": 2},\n", + " \"Латте Макиато\": {\"coffee\": 1, \"milk\": 2, \"cream\": 1},\n", + " \"Кон Панна\": {\"coffee\": 1, \"cream\": 1},\n", + " }\n", + "\n", + " for coffee in coffees:\n", + " for ingidient, amount in ingidients[coffee].items():\n", + " if in_stock[ingidient] < amount:\n", + " break\n", + " else:\n", + " for ingidient, amount in ingidients[coffee].items():\n", + " in_stock[ingidient] -= amount\n", + "\n", + " return coffee\n", + "\n", + " return \"К сожалению, не можем предложить Вам напиток\"" + ] + }, + { + "cell_type": "markdown", + "id": "e3a265ac", + "metadata": {}, + "source": [ + "L" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66aebe14", + "metadata": {}, + "outputs": [], + "source": [ + "DATA: dict[Literal[\"even\", \"odd\"], list[int]] = {\"even\": [], \"odd\": []}\n", + "\n", + "\n", + "def enter_results(*numbers: int) -> None:\n", + " \"\"\"\n", + " Функция сохраняет переданные данные в переменную DATA.\n", + "\n", + " Нечетные числа сохранются в массив DATA['odd'], четные - в DATA['even']\n", + "\n", + " Args:\n", + " *numbers (int): Список целых чисел.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " DATA[\"odd\"].extend(numbers[::2])\n", + " DATA[\"even\"].extend(numbers[1::2])\n", + "\n", + "\n", + "def get_sum() -> tuple[int, int]:\n", + " \"\"\"\n", + " Возвращает пару целых чисел: сумма всех нечетных и всех четных чисел.\n", + "\n", + " Returns:\n", + " tuple[int, int]: Кортеж из двух целых чисел.\n", + " \"\"\"\n", + " return (sum(DATA[\"odd\"]), sum(DATA[\"even\"]))\n", + "\n", + "\n", + "def get_average() -> tuple[float, float]:\n", + " \"\"\"\n", + " Возвращает пару целых чисел.\n", + "\n", + " Пара числе - среднее арифметическое всех нечетных и всех четных чисел.\n", + "\n", + " Returns:\n", + " tuple[int, int]: Кортеж из двух чисел с плавающей точкой.\n", + " \"\"\"\n", + " average_odd = round(sum(DATA[\"odd\"]) / len(DATA[\"odd\"]), 2)\n", + " average_even = round(sum(DATA[\"even\"]) / len(DATA[\"even\"]), 2)\n", + "\n", + " return (average_odd, average_even)" + ] + }, + { + "cell_type": "markdown", + "id": "e87348a8", + "metadata": {}, + "source": [ + "M" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f80533cb", + "metadata": {}, + "outputs": [], + "source": [ + "FnM = Callable[[str], tuple[int, str]]\n", + "\n", + "fn_m: FnM = lambda string: (len(string), string.lower())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33b32b4", + "metadata": {}, + "outputs": [], + "source": [ + "SortFn = Callable[[str], tuple[int, str]]\n", + "\n", + "sort_fn: SortFn = lambda string: (len(string), string.lower())" + ] + }, + { + "cell_type": "markdown", + "id": "4634b00e", + "metadata": {}, + "source": [ + "N" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5e96b1", + "metadata": {}, + "outputs": [], + "source": [ + "FilterFn = Callable[[int], bool]\n", + "filter_fn: FilterFn = lambda num: sum(map(int, str(num))) % 2 == 0" + ] + }, + { + "cell_type": "markdown", + "id": "dfb2012e", + "metadata": {}, + "source": [ + "O" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09758100", + "metadata": {}, + "outputs": [], + "source": [ + "Repeater = Callable[[int], int]\n", + "\n", + "\n", + "def get_repeater(func: Callable[[int], int], count: int) -> Repeater:\n", + " \"\"\"\n", + " Возвращает функцию, которая вызовет func count раз.\n", + "\n", + " Args:\n", + " func (Callable[[int], int]): Функция, которую надо вызывать.\n", + " count (int): Количество раз вывзова функции.\n", + "\n", + " Returns:\n", + " Repeater: Функция, которая вызовет func count раз.\n", + " \"\"\"\n", + "\n", + " def repeater(num: int) -> int:\n", + " current: int = num\n", + "\n", + " for _ in range(count):\n", + " current = func(current)\n", + "\n", + " return current\n", + "\n", + " return repeater" + ] + }, + { + "cell_type": "markdown", + "id": "fe4b7049", + "metadata": {}, + "source": [ + "P" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28908b74", + "metadata": {}, + "outputs": [], + "source": [ + "Cb = Callable[[str], None]\n", + "\n", + "\n", + "def login(name: str, password: str, on_login: Cb, on_fail: Cb) -> None:\n", + " \"\"\"\n", + " Вызывает on_login в случае успешной авторизации, иначе - on_fail.\n", + "\n", + " Args:\n", + " name (str): Имя пользователя.\n", + " password (str): Пароль.\n", + " on_login (Cb): Функция, вызываемая в случае успешной авторизации.\n", + " on_fail (Cb): Функция, вызываемая в случае неуспешной авторизации.\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " expected = f\"{sum(ord(char) for char in name) * len(name):x}\"\n", + "\n", + " if expected[::-1].lower() == password.lower():\n", + " on_login(name)\n", + " else:\n", + " on_fail(name)" + ] + }, + { + "cell_type": "markdown", + "id": "0219bb20", + "metadata": {}, + "source": [ + "Q" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15948e37", + "metadata": {}, + "outputs": [], + "source": [ + "FilterFnQ = Callable[[tuple[str, list[int]]], bool]\n", + "\n", + "filter_fn_q: FilterFnQ = lambda item: isinstance(item[1], list) and any(\n", + " num % 2 == 0 for num in item[1]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "984fcee7", + "metadata": {}, + "source": [ + "R" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58a58b", + "metadata": {}, + "outputs": [], + "source": [ + "Fn = Callable[[tuple[str, object]], tuple[str, object]]\n", + "\n", + "fn: Fn = lambda item: (\n", + " \"\".join([char.lower() for char in item[0] if char.isalpha()]),\n", + " sum(item[1]) if hasattr(item[1], \"__iter__\") else item[1],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ab65767b", + "metadata": {}, + "source": [ + "S" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82ab0eb5", + "metadata": {}, + "outputs": [], + "source": [ + "def secret_replace(text: str, **kwargs: tuple[str, ...]) -> str:\n", + " \"\"\"\n", + " Возвращает строку, зашифрованную на основе переданных параметров.\n", + "\n", + " Args:\n", + " text (str): Строка, которую надо зашифровать.\n", + " **kwargs (tuple[str, ...]): Параметры шифрования\n", + " Returns:\n", + " str: Зашифрованная строка.\n", + " \"\"\"\n", + " my_dict = {key: cycle(value) for key, value in kwargs.items()}\n", + "\n", + " result = \"\".join(\n", + " [next(my_dict[char]) if char in my_dict else char for char in text]\n", + " )\n", + "\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "61f9f74e", + "metadata": {}, + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c79c6b", + "metadata": {}, + "outputs": [], + "source": [ + "User = dict[str, str | datetime | int]\n", + "\n", + "\n", + "DATA_BASE: list[User] = []\n", + "\n", + "\n", + "def insert(*users: User) -> None:\n", + " \"\"\"\n", + " Функция добавляет пользователя в базу данных DATA_BASE.\n", + "\n", + " Args:\n", + " *users (User): Произвольное количество пользователей.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " for user in users:\n", + " formatted_user: User = {**user}\n", + "\n", + " if isinstance(user[\"birth\"], str):\n", + " formatted_date = datetime.strptime(user[\"birth\"], \"%d.%m.%Y\")\n", + " formatted_user[\"date\"] = formatted_date\n", + "\n", + " DATA_BASE.append(formatted_user)\n", + "\n", + "\n", + "def delete_date(users: list[User]) -> list[User]:\n", + " \"\"\"\n", + " Создает копию массива пользователей.\n", + "\n", + " У каждого пользователя удаляет ключ date\n", + "\n", + " Args:\n", + " users (list[User]): Массив пользователей.\n", + "\n", + " Returns:\n", + " list[User]: Массив пользователей, у которых удален ключ date.\n", + " \"\"\"\n", + " result: list[User] = []\n", + "\n", + " for user in users:\n", + " copy: User = {key: val for key, val in user.items() if key != \"date\"}\n", + " result.append(copy)\n", + "\n", + " return result\n", + "\n", + "\n", + "Comparator = Callable[[str | datetime | int, str | datetime | int], bool]\n", + "\n", + "operators: dict[str, Comparator] = {\n", + " \"<\": operator.lt,\n", + " \"<=\": operator.le,\n", + " \"==\": operator.eq,\n", + " \"!=\": operator.ne,\n", + " \">\": operator.gt,\n", + " \">=\": operator.ge,\n", + "}\n", + "\n", + "\n", + "def select(request: str | None = None) -> list[User]:\n", + " \"\"\"\n", + " Возвращает массив пользователей, удовлетворяющих запросу request.\n", + "\n", + " Args:\n", + " request (str | None, optional): Строка запроса.\n", + " Имеет формат \"<ключ> <оператор> <значение>\" По умолчанию None.\n", + "\n", + " Returns:\n", + " list[User]: Список пользователей, удовлетворяющий переданной строке\n", + " запроса\n", + " \"\"\"\n", + " if request is None:\n", + " return delete_date(DATA_BASE)\n", + "\n", + " key, operator_sign, condition = request.split()\n", + "\n", + " formatted_condition: str | datetime | int\n", + "\n", + " if key == \"birth\":\n", + " formatted_condition = datetime.strptime(condition, \"%d.%m.%Y\")\n", + " elif condition.isdigit():\n", + " formatted_condition = int(condition)\n", + " else:\n", + " formatted_condition = condition\n", + "\n", + " result: list[User] = []\n", + "\n", + " for user in DATA_BASE:\n", + " compare_key = \"date\" if key == \"birth\" else key\n", + "\n", + " if operators[operator_sign](user[compare_key], formatted_condition):\n", + " result.append(user)\n", + "\n", + " return sorted(\n", + " delete_date(result),\n", + " key=lambda user: user[\"id\"],\n", + " )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_4_2.py b/Python/yandex-python-handbook/chapter_4_2.py new file mode 100644 index 00000000..0d1b96e8 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_2.py @@ -0,0 +1,605 @@ +"""Задания к главе 4.2. + +Позиционные и именованные аргументы. Функции высших порядков. Лямбда-функции + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +import operator +from datetime import datetime +from itertools import cycle +from typing import Callable, Literal, TypeVar + + +def make_list(length: int, value: int = 0) -> list[int]: + """Создает список указанной длины, заполненный переданным значением. + + Args: + length (int): Длина списка. + value (int): Элемент для заполнения. + + Returns: + list[int]: Список длины length, заполненный значением value. + """ + return [value] * length + + +# - + +# B + +# + +Size = int | tuple[int, int] +Matrix = list[list[int]] + + +def make_matrix(size: Size, value: int = 0) -> Matrix: + """Создает матрицу указанного размера, заполненную переданным значением. + + Args: + size (int | tuple[int, int]): Размер матрицы. Если кортеж, то + (ширина, высота). Если одно значение, то создаем квадратную матрицу + value (int): Элемент для заполнения. + + Returns: + list[int]: Матрица размера size, заполненная значением value. + """ + if isinstance(size, int): + width = size + height = size + else: + width = size[0] + height = size[1] + + return [[value] * width for _ in range(height)] + + +# - + +# C + + +def gcd(*numbers: int) -> int: + """Вычисляет НОД для переданных чисел. + + Args: + *numbers (int): Одно или более целое число. Не должно быть пустым. + + Returns: + int: НОД переданных чисел. + """ + divider = numbers[0] + + for index in range(1, len(numbers), 1): + next_var = numbers[index] + + while next_var: + divider, next_var = next_var, divider % next_var + + return divider + + +# D + +# + +months: dict[str, list[str]] = { + "ru": [ + "Январь", + "Февраль", + "Март", + "Апрель", + "Май", + "Июнь", + "Июль", + "Август", + "Сентябрь", + "Октябрь", + "Ноябрь", + "Декабрь", + ], + "en": [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ], +} + + +def month(number: int, language: Literal["ru", "en"] = "ru") -> str: + """Функция для получения названия месяца. + + Функция возвращает имя месяца в соответствии с переданными номером месяца + и языком + + Args: + number (int): Номер месяца от 1 до 12. + language (Literal["ru", "en"]): Язык. + + Returns: + str: Название месяца. + """ + return months[language][number - 1] + + +# - + +# E + + +def to_string(*items: object, sep: str = " ", end: str = "\n") -> str: + r"""Функция возвращает строку из переданных элементов. + + Элементы разделены сепаратором sep. Заканчивается строкой end. + + Args: + *items (object): Элементы, которые надо объединить в строку. + sep (str, optional): Разделитель элементов в строке. По умолчанию " ". + end (str, optional): Окончание строки. По умолчанию "\n". + + Returns: + str: _description_ + """ + return f"{sep.join(map(str, items))}{end}" + + +# F + +# + +Operator = Literal["+", "-", "*", "//", "**"] +OperatorFn = Callable[[int, int], int] + + +def get_operator_fn(operator_f: Operator) -> OperatorFn: + """Возвращает функцию-оператор для двух целых чисел. + + Args: + operator_f (Operator): Математическая операция. + + Returns: + OperatorFn: Функция, принимающая два int и возвращающая int. + """ + match operator_f: + case "+": + return lambda operand1, operand2: operand1 + operand2 + case "-": + return lambda operand1, operand2: operand1 - operand2 + case "*": + return lambda operand1, operand2: operand1 * operand2 + case "//": + return lambda operand1, operand2: operand1 // operand2 + case "**": + return lambda operand1, operand2: operand1**operand2 + + +# - + +# G + +# + +T = TypeVar("T") + +OperatorFnG = Callable[[tuple[object, ...]], str] + + +def get_formatter(sep: str = " ", end: str = "") -> OperatorFnG: + """Возвращает функцию, которая объединяет аргументы в строку. + + Args: + sep (str, optional): Разделитель элементов в строке. По умолчанию " ". + end (str, optional): Окончание строки. По умолчанию "". + + Returns: + OperatorFnG: Функция, принимающая не определенное количество аргументов. + Возвращает строку, объединяющую эти аргументы через sep и оканчивающуюся + строкой end. + """ + return lambda *args: f"{sep.join(map(str, args))}{end}" + + +# - + +# H + + +def grow(*numbers: int, **kwargs: int) -> tuple[int, ...]: + """Преобразует позиционные аргументы. + + Функция принимает не определенное количество позиционных аргументов. + И не определенное количество именованных аргументов. + + Возвращает кортеж, состоящий из по аргументов, увеличенных на значение + именованных параметров, если значение позиционного аргумента кратно + длине имени именованного аргумента. + + Args: + *numbers (int): Целые числа. + **kwargs (int): Целые числа. + + Returns: + tuple[int, ...]: Кортеж целых чисел. + """ + result = [] + + for number in numbers: + updated = number + + for key, value in kwargs.items(): + if number % len(key) == 0: + updated += value + + result.append(updated) + + return tuple(result) + + +# I + + +def product_i(*items: str, **kwargs: int) -> tuple[int, ...]: + """Заменяет каждую строку на произведение значений именованных параметров. + + Если ни одно имя не найдено — строка игнорируется. + + Args: + *args (str): Строки, которые нужно обработать. + **kwargs (int): Именованные параметры. + + Returns: + tuple[int, ...]: Кортеж произведений для строк, где нашлись совпадения. + """ + result = [] + + for item in items: + product: None | int = None + + for key, value in kwargs.items(): + if key in item: + product = product * value if product is not None else value + + if product is not None: + result.append(product) + + return tuple(result) + + +# J + + +def choice(*numbers: int, **kwargs: Callable[[int], int]) -> int: + """Применяет переданную функцию ко всем позиционным аргументам. + + Если передан именованный параметр min, то возвращает минимальное значение + из результатов выполнения функции min. Иначе - максимальное из результатов + выполнения функции max. + + Args: + *numbers (int): Целые числа. + **kwargs (Function): Функция. Параметр может иметь имя min или max + + Returns: + (int): Целое число + """ + if "min" in kwargs: + return min(map(kwargs["min"], numbers)) + + return max(map(kwargs["max"], numbers)) + + +# K + +# + +in_stock: dict[str, int] = {} + + +def order(*coffees: str) -> str: + """Функция возвращает название напитка, который можно приготовить. + + Если ни одиннапиток нельзя приготовить, функция возвращает строку: + "К сожалению, не можем предложить Вам напиток" + + Args: + *coffees (str): Список напитков. + + Returns: + (str): Название напитка. Если напиток нельзя приготовить, то строка: + "К сожалению, не можем предложить Вам напиток" + """ + ingidients: dict[str, dict[str, int]] = { + "Эспрессо": {"coffee": 1}, + "Капучино": {"coffee": 1, "milk": 3}, + "Макиато": {"coffee": 2, "milk": 1}, + "Кофе по-венски": {"coffee": 1, "cream": 2}, + "Латте Макиато": {"coffee": 1, "milk": 2, "cream": 1}, + "Кон Панна": {"coffee": 1, "cream": 1}, + } + + for coffee in coffees: + for ingidient, amount in ingidients[coffee].items(): + if in_stock[ingidient] < amount: + break + else: + for ingidient, amount in ingidients[coffee].items(): + in_stock[ingidient] -= amount + + return coffee + + return "К сожалению, не можем предложить Вам напиток" + + +# - + +# L + +# + +DATA: dict[Literal["even", "odd"], list[int]] = {"even": [], "odd": []} + + +def enter_results(*numbers: int) -> None: + """Функция сохраняет переданные данные в переменную DATA. + + Нечетные числа сохранются в массив DATA['odd'], четные - в DATA['even'] + + Args: + *numbers (int): Список целых чисел. + + Returns: + None: Функция ничего не возвращает. + """ + DATA["odd"].extend(numbers[::2]) + DATA["even"].extend(numbers[1::2]) + + +def get_sum() -> tuple[int, int]: + """Возвращает пару целых чисел: сумма всех нечетных и всех четных чисел. + + Returns: + tuple[int, int]: Кортеж из двух целых чисел. + """ + return (sum(DATA["odd"]), sum(DATA["even"])) + + +def get_average() -> tuple[float, float]: + """Возвращает пару целых чисел. + + Пара числе - среднее арифметическое всех нечетных и всех четных чисел. + + Returns: + tuple[int, int]: Кортеж из двух чисел с плавающей точкой. + """ + average_odd = round(sum(DATA["odd"]) / len(DATA["odd"]), 2) + average_even = round(sum(DATA["even"]) / len(DATA["even"]), 2) + + return (average_odd, average_even) + + +# - + +# M + +# + +FnM = Callable[[str], tuple[int, str]] + +fn_m: FnM = lambda string: (len(string), string.lower()) + +# + +SortFn = Callable[[str], tuple[int, str]] + +sort_fn: SortFn = lambda string: (len(string), string.lower()) +# - + +# N + +FilterFn = Callable[[int], bool] +filter_fn: FilterFn = lambda num: sum(map(int, str(num))) % 2 == 0 + +# O + +# + +Repeater = Callable[[int], int] + + +def get_repeater(func: Callable[[int], int], count: int) -> Repeater: + """Возвращает функцию, которая вызовет func count раз. + + Args: + func (Callable[[int], int]): Функция, которую надо вызывать. + count (int): Количество раз вывзова функции. + + Returns: + Repeater: Функция, которая вызовет func count раз. + """ + + def repeater(num: int) -> int: + current: int = num + + for _ in range(count): + current = func(current) + + return current + + return repeater + + +# - + +# P + +# + +Cb = Callable[[str], None] + + +def login(name: str, password: str, on_login: Cb, on_fail: Cb) -> None: + """ + Вызывает on_login в случае успешной авторизации, иначе - on_fail. + + Args: + name (str): Имя пользователя. + password (str): Пароль. + on_login (Cb): Функция, вызываемая в случае успешной авторизации. + on_fail (Cb): Функция, вызываемая в случае неуспешной авторизации. + Returns: + None: Функция ничего не возвращает. + """ + expected = f"{sum(ord(char) for char in name) * len(name):x}" + + if expected[::-1].lower() == password.lower(): + on_login(name) + else: + on_fail(name) + + +# - + +# Q + +# + +FilterFnQ = Callable[[tuple[str, list[int]]], bool] + +filter_fn_q: FilterFnQ = lambda item: isinstance(item[1], list) and any( + num % 2 == 0 for num in item[1] +) +# - + +# R + +# + +Fn = Callable[[tuple[str, object]], tuple[str, object]] + +fn: Fn = lambda item: ( + "".join([char.lower() for char in item[0] if char.isalpha()]), + sum(item[1]) if hasattr(item[1], "__iter__") else item[1], +) + + +# - + +# S + + +def secret_replace(text: str, **kwargs: tuple[str, ...]) -> str: + """Возвращает строку, зашифрованную на основе переданных параметров. + + Args: + text (str): Строка, которую надо зашифровать. + **kwargs (tuple[str, ...]): Параметры шифрования + Returns: + str: Зашифрованная строка. + """ + my_dict = {key: cycle(value) for key, value in kwargs.items()} + + result = "".join( + [next(my_dict[char]) if char in my_dict else char for char in text] + ) + + return result + + +# T + +# + +User = dict[str, str | datetime | int] + + +DATA_BASE: list[User] = [] + + +def insert(*users: User) -> None: + """Функция добавляет пользователя в базу данных DATA_BASE. + + Args: + *users (User): Произвольное количество пользователей. + + Returns: + None: Функция ничего не возвращает. + """ + for user in users: + formatted_user: User = {**user} + + if isinstance(user["birth"], str): + formatted_date = datetime.strptime(user["birth"], "%d.%m.%Y") + formatted_user["date"] = formatted_date + + DATA_BASE.append(formatted_user) + + +def delete_date(users: list[User]) -> list[User]: + """Создает копию массива пользователей. + + У каждого пользователя удаляет ключ date + + Args: + users (list[User]): Массив пользователей. + + Returns: + list[User]: Массив пользователей, у которых удален ключ date. + """ + result: list[User] = [] + + for user in users: + copy: User = {key: val for key, val in user.items() if key != "date"} + result.append(copy) + + return result + + +Comparator = Callable[[str | datetime | int, str | datetime | int], bool] + +operators: dict[str, Comparator] = { + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">": operator.gt, + ">=": operator.ge, +} + + +def select(request: str | None = None) -> list[User]: + """Возвращает массив пользователей, удовлетворяющих запросу request. + + Args: + request (str | None, optional): Строка запроса. + Имеет формат "<ключ> <оператор> <значение>" По умолчанию None. + + Returns: + list[User]: Список пользователей, удовлетворяющий переданной строке + запроса + """ + if request is None: + return delete_date(DATA_BASE) + + key, operator_sign, condition = request.split() + + formatted_condition: str | datetime | int + + if key == "birth": + formatted_condition = datetime.strptime(condition, "%d.%m.%Y") + elif condition.isdigit(): + formatted_condition = int(condition) + else: + formatted_condition = condition + + result: list[User] = [] + + for user in DATA_BASE: + compare_key = "date" if key == "birth" else key + + if operators[operator_sign](user[compare_key], formatted_condition): + result.append(user) + + return sorted( + delete_date(result), + key=lambda user: user["id"], + ) diff --git a/Python/yandex-python-handbook/chapter_4_3.ipynb b/Python/yandex-python-handbook/chapter_4_3.ipynb new file mode 100644 index 00000000..4571f318 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_3.ipynb @@ -0,0 +1,452 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 4.3.\n", + "\n", + "Рекурсия. Декораторы. Генераторы\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Generator, Union\n", + "\n", + "\n", + "def recursive_sum(*nums: int) -> int:\n", + " \"\"\"\n", + " Возвращает рекурсивную сумму переданных аргументов.\n", + "\n", + " Args:\n", + " *nums (int): Целые числа.\n", + "\n", + " Returns:\n", + " int: Сумма.\n", + " \"\"\"\n", + " if len(nums) == 0:\n", + " return 0\n", + "\n", + " return nums[-1] + recursive_sum(*nums[:-1])" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "def recursive_digit_sum(num: int) -> int:\n", + " \"\"\"\n", + " Возвращает сумму цифр переданного числа.\n", + "\n", + " Args:\n", + " num (int): Целое число.\n", + "\n", + " Returns:\n", + " int: Сумма цифр переданного числа.\n", + " \"\"\"\n", + " if num == 0:\n", + " return 0\n", + "\n", + " return num % 10 + recursive_digit_sum(num // 10)" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "def make_equation(*nums: int) -> str:\n", + " \"\"\"\n", + " Возвращает многочлен N-ой степени.\n", + "\n", + " Для построения используются переданные коэффициенты.\n", + "\n", + " Args:\n", + " *num (int): Целые числа.\n", + "\n", + " Returns:\n", + " int: Многочлен N-ой степени.\n", + " \"\"\"\n", + " if len(nums) == 1:\n", + " return f\"{nums[0]}\"\n", + "\n", + " return f\"({make_equation(*nums[:-1])}) * x + {nums[-1]}\"" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "markdown", + "id": "018edb4e", + "metadata": {}, + "source": [ + "T = TypeVar(\"T\")\n", + "AnyFunction = Callable[..., T]\n", + "\n", + "\n", + "def answer(fn: AnyFunction) -> Callable[[int,...], str]:\n", + " \"\"\"\n", + " Декоратор, который оборачивает функцию.\n", + "\n", + " Возвращает строку с результатом ее выполнения.\n", + "\n", + " Добавляет префикс \"Результат функции: \" к строковому представлению\n", + " возвращаемого значения декорируемой функции.\n", + "\n", + " Args:\n", + " fn (AnyFunction): Функция, которую нужно обернуть. Может принимать\n", + " любые позиционные и именованные аргументы (*args, **kwargs) и\n", + " возвращать значение любого типа.\n", + "\n", + " Returns:\n", + " Callable[..., str]: Функция-обёртка.\n", + " \"\"\"\n", + "\n", + " def inner(*args, **kwargs):\n", + " return f\"Результат функции: {fn(*args, **kwargs)}\"\n", + "\n", + " return inner" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "markdown", + "id": "090b27b8", + "metadata": {}, + "source": [ + "Method = Literal[\"accumulate\", \"drop\"]\n", + "\n", + "\n", + "def result_accumulator[Result](\n", + " fn: Callable[..., Result],\n", + ") -> Callable[..., list[Result] | None]:\n", + " \"\"\"\n", + " Декоратор, который позволяет функции аккумулировать значения.\n", + "\n", + " Args:\n", + " fn (Callable[..., Result]): Обрачиваемая функция.\n", + " Returns:\n", + " _type_: Функция, которая может аккумулировать значения.\n", + " \"\"\"\n", + " queue: list[Result] = []\n", + "\n", + " @wraps(fn)\n", + " def inner(\n", + " *args,\n", + " method: Method = \"accumulate\",\n", + " **kwargs,\n", + " ):\n", + " \"\"\"\n", + " Функция, которая может аккумулировать значения.\n", + "\n", + " Args:\n", + " method (Method, optional): Если передано 'accumulate', функция\n", + " накапливает значения. Если передано 'drop' - возвращает\n", + " массив накопленных значений и очищает накопленные результаты.\n", + " Defaults to \"accumulate\".\n", + "\n", + " Returns:\n", + " _type_: Массив результатов.\n", + " \"\"\"\n", + " if method == \"accumulate\":\n", + " queue.append(fn(*args, **kwargs))\n", + " if method == \"drop\":\n", + " queue.append(fn(*args, **kwargs))\n", + " result = list(queue)\n", + " queue.clear()\n", + "\n", + " return result\n", + "\n", + " return inner" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "def merge(left: list[int], right: list[int]) -> list[int]:\n", + " \"\"\"\n", + " Функция для слияния двух отсортированных списков.\n", + "\n", + " Args:\n", + " left (list[int]): Первый отсортированный список целых чисел.\n", + " right (list[int]): Второй отсортированный список целых чисел.\n", + "\n", + " Returns:\n", + " list[int]: Отсортированный список целых чисел.\n", + " \"\"\"\n", + " result: list[int] = []\n", + " pos_left = pos_right = 0\n", + "\n", + " while pos_left < len(left) and pos_right < len(right):\n", + " if left[pos_left] < right[pos_right]:\n", + " result.append(left[pos_left])\n", + " pos_left += 1\n", + " else:\n", + " result.append(right[pos_right])\n", + " pos_right += 1\n", + "\n", + " result += left[pos_left:]\n", + " result += right[pos_right:]\n", + "\n", + " return result\n", + "\n", + "\n", + "def merge_sort(nums: list[int]) -> list[int]:\n", + " \"\"\"\n", + " Функция для сортировки списка слиянием.\n", + "\n", + " Использует вспомогательную функцию merge, которая объединяет два\n", + " отсортированных списка.\n", + "\n", + " Args:\n", + " nums (list[int]): Несортированный список целых чисел.\n", + "\n", + " Returns:\n", + " list[int]: Отсортированный список целых чисел.\n", + " \"\"\"\n", + " if len(nums) <= 1:\n", + " return nums\n", + "\n", + " middle = len(nums) // 2\n", + "\n", + " left = merge_sort(nums[:middle])\n", + " right = merge_sort(nums[middle:])\n", + "\n", + " return merge(left, right)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "markdown", + "id": "b479afd6", + "metadata": {}, + "source": [ + "def same_type(fn):\n", + " \"\"\"\n", + " Декоратор, который проверяет аргументы оборачиваемой функции.\n", + "\n", + " Если аргументы одного типа, возвращает результат выполнения функции.\n", + " Если аргументы разного типа, выподит строку:\n", + " \"Обнаружены различные типы данных\"\n", + "\n", + " Args:\n", + " fn (function): Оборачиваемая функция, которая принимает неограниченное\n", + " количество аргументов\n", + " \"\"\"\n", + "\n", + " def inner(*args):\n", + " if all(isinstance(arg, type(args[0])) for arg in args):\n", + " return fn(*args)\n", + "\n", + " print(\"Обнаружены различные типы данных\")\n", + "\n", + " return inner" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "def fibonacci(num: int) -> Generator[int, None, None]:\n", + " \"\"\"\n", + " Функция возвращает num первых членов последовательности Фибоначчи.\n", + "\n", + " Первый член последовательности равен 0.\n", + "\n", + " Args:\n", + " num (int): Целое число.\n", + "\n", + " Yields:\n", + " Generator[int, None, None]: Целое число.\n", + " \"\"\"\n", + " prev_prev = 0\n", + " prev = 1\n", + "\n", + " for _ in range(num):\n", + " yield prev_prev\n", + " prev_prev, prev = prev, prev + prev_prev" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "def cycle(nums: list[int]) -> Generator[int, None, None]:\n", + " \"\"\"\n", + " Генератор зацикливает переданный список.\n", + "\n", + " Бесконечно возвращает следующий элемент списка.\n", + " Если список закончился, начинает возвращать элементы с начала списка\n", + "\n", + " Args:\n", + " nums (list[int]): Список целых чисел.\n", + "\n", + " Yields:\n", + " Generator[int, None, None]: Целое число, элемент списка.\n", + " \"\"\"\n", + " index = 0\n", + "\n", + " while True:\n", + " yield nums[index]\n", + "\n", + " index = index + 1 if index < len(nums) - 1 else 0" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "IntList = Union[int, list[\"IntList\"]]\n", + "\n", + "\n", + "def make_linear(items: list[IntList]) -> list[int]:\n", + " \"\"\"\n", + " Распаковывает переданный список.\n", + "\n", + " Args:\n", + " nums (list[list | int]): Список, состоящий из целых чисел или\n", + " других списков.\n", + "\n", + " Yields:\n", + " list[int]: Список целых чисел.\n", + " \"\"\"\n", + " result: list[int] = []\n", + "\n", + " for item in items:\n", + " if isinstance(item, list):\n", + " result.extend(make_linear(item))\n", + " else:\n", + " result.append(item)\n", + "\n", + " return result" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_4_3.py b/Python/yandex-python-handbook/chapter_4_3.py new file mode 100644 index 00000000..52f0c4d2 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_4_3.py @@ -0,0 +1,295 @@ +"""Задания к главе 4.3. + +Рекурсия. Декораторы. Генераторы + +Хендбук Яндекс "Основы Python". +""" + +# A + +from collections.abc import Generator + +# + +from typing import Union + + +def recursive_sum(*nums: int) -> int: + """Возвращает рекурсивную сумму переданных аргументов. + + Args: + *nums (int): Целые числа. + + Returns: + int: Сумма. + """ + if len(nums) == 0: + return 0 + + return nums[-1] + recursive_sum(*nums[:-1]) + + +# - + +# B + + +def recursive_digit_sum(num: int) -> int: + """Возвращает сумму цифр переданного числа. + + Args: + num (int): Целое число. + + Returns: + int: Сумма цифр переданного числа. + """ + if num == 0: + return 0 + + return num % 10 + recursive_digit_sum(num // 10) + + +# C + + +def make_equation(*nums: int) -> str: + """Возвращает многочлен N-ой степени. + + Для построения используются переданные коэффициенты. + + Args: + *num (int): Целые числа. + + Returns: + int: Многочлен N-ой степени. + """ + if len(nums) == 1: + return f"{nums[0]}" + + return f"({make_equation(*nums[:-1])}) * x + {nums[-1]}" + + +# D + +# T = TypeVar("T") +# AnyFunction = Callable[..., T] +# +# +# def answer(fn: AnyFunction) -> Callable[[int,...], str]: +# """ +# Декоратор, который оборачивает функцию. +# +# Возвращает строку с результатом ее выполнения. +# +# Добавляет префикс "Результат функции: " к строковому представлению +# возвращаемого значения декорируемой функции. +# +# Args: +# fn (AnyFunction): Функция, которую нужно обернуть. Может принимать +# любые позиционные и именованные аргументы (*args, **kwargs) и +# возвращать значение любого типа. +# +# Returns: +# Callable[..., str]: Функция-обёртка. +# """ +# +# def inner(*args, **kwargs): +# return f"Результат функции: {fn(*args, **kwargs)}" +# +# return inner + +# E + +# Method = Literal["accumulate", "drop"] +# +# +# def result_accumulator[Result]( +# fn: Callable[..., Result], +# ) -> Callable[..., list[Result] | None]: +# """ +# Декоратор, который позволяет функции аккумулировать значения. +# +# Args: +# fn (Callable[..., Result]): Обрачиваемая функция. +# Returns: +# _type_: Функция, которая может аккумулировать значения. +# """ +# queue: list[Result] = [] +# +# @wraps(fn) +# def inner( +# *args, +# method: Method = "accumulate", +# **kwargs, +# ): +# """ +# Функция, которая может аккумулировать значения. +# +# Args: +# method (Method, optional): Если передано 'accumulate', функция +# накапливает значения. Если передано 'drop' - возвращает +# массив накопленных значений и очищает накопленные результаты. +# Defaults to "accumulate". +# +# Returns: +# _type_: Массив результатов. +# """ +# if method == "accumulate": +# queue.append(fn(*args, **kwargs)) +# if method == "drop": +# queue.append(fn(*args, **kwargs)) +# result = list(queue) +# queue.clear() +# +# return result +# +# return inner + +# F + + +# + +def merge(left: list[int], right: list[int]) -> list[int]: + """Функция для слияния двух отсортированных списков. + + Args: + left (list[int]): Первый отсортированный список целых чисел. + right (list[int]): Второй отсортированный список целых чисел. + + Returns: + list[int]: Отсортированный список целых чисел. + """ + result: list[int] = [] + pos_left = pos_right = 0 + + while pos_left < len(left) and pos_right < len(right): + if left[pos_left] < right[pos_right]: + result.append(left[pos_left]) + pos_left += 1 + else: + result.append(right[pos_right]) + pos_right += 1 + + result += left[pos_left:] + result += right[pos_right:] + + return result + + +def merge_sort(nums: list[int]) -> list[int]: + """Функция для сортировки списка слиянием. + + Использует вспомогательную функцию merge, которая объединяет два + отсортированных списка. + + Args: + nums (list[int]): Несортированный список целых чисел. + + Returns: + list[int]: Отсортированный список целых чисел. + """ + if len(nums) <= 1: + return nums + + middle = len(nums) // 2 + + left = merge_sort(nums[:middle]) + right = merge_sort(nums[middle:]) + + return merge(left, right) + + +# - + +# G + +# def same_type(fn): +# """ +# Декоратор, который проверяет аргументы оборачиваемой функции. +# +# Если аргументы одного типа, возвращает результат выполнения функции. +# Если аргументы разного типа, выподит строку: +# "Обнаружены различные типы данных" +# +# Args: +# fn (function): Оборачиваемая функция, которая принимает неограниченное +# количество аргументов +# """ +# +# def inner(*args): +# if all(isinstance(arg, type(args[0])) for arg in args): +# return fn(*args) +# +# print("Обнаружены различные типы данных") +# +# return inner + +# H + + +def fibonacci(num: int) -> Generator[int, None, None]: + """Функция возвращает num первых членов последовательности Фибоначчи. + + Первый член последовательности равен 0. + + Args: + num (int): Целое число. + + Yields: + Generator[int, None, None]: Целое число. + """ + prev_prev = 0 + prev = 1 + + for _ in range(num): + yield prev_prev + prev_prev, prev = prev, prev + prev_prev + + +# I + + +def cycle(nums: list[int]) -> Generator[int, None, None]: + """Генератор зацикливает переданный список. + + Бесконечно возвращает следующий элемент списка. + Если список закончился, начинает возвращать элементы с начала списка + + Args: + nums (list[int]): Список целых чисел. + + Yields: + Generator[int, None, None]: Целое число, элемент списка. + """ + index = 0 + + while True: + yield nums[index] + + index = index + 1 if index < len(nums) - 1 else 0 + + +# J + +# + +IntList = Union[int, list["IntList"]] + + +def make_linear(items: list[IntList]) -> list[int]: + """Распаковывает переданный список. + + Args: + nums (list[list | int]): Список, состоящий из целых чисел или + других списков. + + Yields: + list[int]: Список целых чисел. + """ + result: list[int] = [] + + for item in items: + if isinstance(item, list): + result.extend(make_linear(item)) + else: + result.append(item) + + return result diff --git a/Python/yandex-python-handbook/chapter_5_1.ipynb b/Python/yandex-python-handbook/chapter_5_1.ipynb new file mode 100644 index 00000000..933dbe9d --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_1.ipynb @@ -0,0 +1,986 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 5.1.\n", + "\n", + "Объектная модель Python. Классы, поля и методы.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal, Self\n", + "\n", + "\n", + "class Point:\n", + " \"\"\"\n", + " Точка на плоскости с двумя координатами.\n", + "\n", + " Предоставляет методы:\n", + " move() - перемещение точки.\n", + " length() - вычисление расстояния до другой точки.\n", + "\n", + " Attributes:\n", + " x (int): Координата по оси X.\n", + " y (int): Координата по оси Y.\n", + " \"\"\"\n", + "\n", + " def __init__(self, x_coord: int, y_coord: int) -> None:\n", + " \"\"\"\n", + " Создает точку с заданными координатами.\n", + "\n", + " Args:\n", + " x_coord (int): Координата по оси X.\n", + " y_coord (int): Координата по оси Y.\n", + " \"\"\"\n", + " self.x = x_coord\n", + " self.y = y_coord" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "class PointB:\n", + " \"\"\"\n", + " Точка на плоскости с двумя координатами.\n", + "\n", + " Предоставляет методы:\n", + " move() - перемещение точки.\n", + " length() - вычисление расстояния до другой точки.\n", + "\n", + " Attributes:\n", + " x (int): Координата по оси X.\n", + " y (int): Координата по оси Y.\n", + " \"\"\"\n", + "\n", + " def __init__(self, x_coord: int, y_coord: int) -> None:\n", + " \"\"\"\n", + " Создает точку с заданными координатами.\n", + "\n", + " Args:\n", + " x_coord (int): Координата по оси X.\n", + " y_coord (int): Координата по оси Y.\n", + " \"\"\"\n", + " self.x = x_coord\n", + " self.y = y_coord\n", + "\n", + " def move(self, delta_x: int, delta_y: int) -> None:\n", + " \"\"\"\n", + " Перемещает точку на указанное расстояние.\n", + "\n", + " Args:\n", + " delta_x (int): Расстояние по оси X.\n", + " delta_y (int): Расстояние по оси Y.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " self.x += delta_x\n", + " self.y += delta_y\n", + "\n", + " def length(self, other: Self) -> float:\n", + " \"\"\"Возвращает расстояние до другой точки.\n", + "\n", + " Args:\n", + " other (Self): Другая точка.\n", + "\n", + " Returns:\n", + " float: Расстояние.\n", + " \"\"\"\n", + " result = ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5\n", + " return float(round(result, 2))" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "class RedButton:\n", + " \"\"\"\n", + " Красная кнопка, которое считает количество нажатий.\n", + "\n", + " На каждое нажатие вывод сообщение \"Тревога!\"\n", + "\n", + " Attributes:\n", + " counter (int): Количество раз, сколько была нажата кнопка.\n", + " \"\"\"\n", + "\n", + " counter: int\n", + "\n", + " def __init__(self) -> None:\n", + " \"\"\"Создает красную кнопку.\"\"\"\n", + " self.counter = 0\n", + "\n", + " def click(self) -> None:\n", + " \"\"\"\n", + " Осуществляет клик по кнопке.\n", + "\n", + " Увеличивает счетчик кликов.\n", + " Выводит сообщение \"Тревога!\"\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " self.counter += 1\n", + "\n", + " print(\"Тревога!\")\n", + "\n", + " def count(self) -> int:\n", + " \"\"\"\n", + " Возвращает текущее количество нажатий.\n", + "\n", + " Returns:\n", + " int: Количество нажатий.\n", + " \"\"\"\n", + " return self.counter" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "Coord = tuple[float, float]\n", + "\n", + "\n", + "class RectangleD:\n", + " \"\"\"\n", + " Прямоугольник.\n", + "\n", + " Attributes:\n", + " top_left (Coord): Координаты верхнего левого угла.\n", + " bottom_left (Coord): Координаты нижнего левого угла.\n", + " top_right (float): Координаты верхнего правого угла.\n", + " bottom_right (float): Координаты нижнего правого угла.\n", + " \"\"\"\n", + "\n", + " top_left: Coord\n", + " bottom_left: Coord\n", + " top_right: Coord\n", + " bottom_right: Coord\n", + "\n", + " def __init__(self, first: Coord, second: Coord) -> None:\n", + " \"\"\"\n", + " Создает прямоугольник.\n", + "\n", + " Args:\n", + " first (Coord): Координаты одного из противоположных углов.\n", + " second (Coord): Координаты второго из противоположных углов.\n", + " \"\"\"\n", + " (x1, y1), (x2, y2) = first, second\n", + "\n", + " self.top_left = (min(x1, x2), max(y1, y2))\n", + " self.bottom_left = (min(x1, x2), min(y1, y2))\n", + " self.top_right = (max(x1, x2), max(y1, y2))\n", + " self.bottom_right = (max(x1, x2), min(y1, y2))\n", + " self.second = second\n", + "\n", + " def perimeter(self) -> float:\n", + " \"\"\"\n", + " Возвращает периметр прямоугольника.\n", + "\n", + " Returns:\n", + " float: Периметр.\n", + " \"\"\"\n", + " return float(round(self.width * 2 + self.height * 2, 2))\n", + "\n", + " def area(self) -> float:\n", + " \"\"\"\n", + " Возвращает площадь прямоугольника.\n", + "\n", + " Returns:\n", + " float: Площадь.\n", + " \"\"\"\n", + " return float(round(self.width * self.height, 2))\n", + "\n", + " @property\n", + " def width(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает ширину прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return self.top_right[0] - self.top_left[0]\n", + "\n", + " @property\n", + " def height(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает высоту прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return self.top_left[1] - self.bottom_left[1]" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "class RectangleE:\n", + " \"\"\"\n", + " Прямоугольник.\n", + "\n", + " Attributes:\n", + " top_left (Coord): Координаты верхнего левого угла.\n", + " bottom_left (Coord): Координаты нижнего левого угла.\n", + " top_right (float): Координаты верхнего правого угла.\n", + " bottom_right (float): Координаты нижнего правого угла.\n", + " \"\"\"\n", + "\n", + " top_left: Coord\n", + " bottom_left: Coord\n", + " top_right: Coord\n", + " bottom_right: Coord\n", + "\n", + " def __init__(self, first: Coord, second: Coord) -> None:\n", + " \"\"\"\n", + " Создает прямоугольник.\n", + "\n", + " Args:\n", + " first (Coord): Координаты одного из противоположных углов.\n", + " second (Coord): Координаты второго из противоположных углов.\n", + " \"\"\"\n", + " (x1, y1), (x2, y2) = first, second\n", + "\n", + " self.top_left = (min(x1, x2), max(y1, y2))\n", + " self.bottom_left = (min(x1, x2), min(y1, y2))\n", + " self.top_right = (max(x1, x2), max(y1, y2))\n", + " self.bottom_right = (max(x1, x2), min(y1, y2))\n", + " self.second = second\n", + "\n", + " def perimeter(self) -> float:\n", + " \"\"\"\n", + " Возвращает периметр прямоугольника.\n", + "\n", + " Returns:\n", + " float: Периметр.\n", + " \"\"\"\n", + " return float(round(self.width * 2 + self.height * 2, 2))\n", + "\n", + " def area(self) -> float:\n", + " \"\"\"\n", + " Возвращает площадь прямоугольника.\n", + "\n", + " Returns:\n", + " float: Площадь.\n", + " \"\"\"\n", + " return float(round(self.width * self.height, 2))\n", + "\n", + " @property\n", + " def width(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает ширину прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return self.top_right[0] - self.top_left[0]\n", + "\n", + " @property\n", + " def height(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает высоту прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return self.top_left[1] - self.bottom_left[1]" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "class RectangleF:\n", + " \"\"\"\n", + " Прямоугольник.\n", + "\n", + " Attributes:\n", + " top_left (Coord): Координаты верхнего левого угла.\n", + " bottom_left (Coord): Координаты нижнего левого угла.\n", + " top_right (float): Координаты верхнего правого угла.\n", + " bottom_right (float): Координаты нижнего правого угла.\n", + " \"\"\"\n", + "\n", + " top_left: Coord\n", + " bottom_left: Coord\n", + " top_right: Coord\n", + " bottom_right: Coord\n", + "\n", + " def __init__(self, first: Coord, second: Coord) -> None:\n", + " \"\"\"\n", + " Создает прямоугольник.\n", + "\n", + " Args:\n", + " first (Coord): Координаты одного из противоположных углов.\n", + " second (Coord): Координаты второго из противоположных углов.\n", + " \"\"\"\n", + " (x1, y1), (x2, y2) = first, second\n", + "\n", + " self.top_left = (min(x1, x2), max(y1, y2))\n", + " self.bottom_left = (min(x1, x2), min(y1, y2))\n", + " self.top_right = (max(x1, x2), max(y1, y2))\n", + " self.bottom_right = (max(x1, x2), min(y1, y2))\n", + "\n", + " def perimeter(self) -> float:\n", + " \"\"\"\n", + " Возвращает периметр прямоугольника.\n", + "\n", + " Returns:\n", + " float: Периметр.\n", + " \"\"\"\n", + " return float(round(self.width * 2 + self.height * 2, 2))\n", + "\n", + " def area(self) -> float:\n", + " \"\"\"\n", + " Возвращает площадь прямоугольника.\n", + "\n", + " Returns:\n", + " float: Площадь.\n", + " \"\"\"\n", + " return float(round(self.width * self.height, 2))\n", + "\n", + " @property\n", + " def width(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает ширину прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return float(round(self.top_right[0] - self.top_left[0], 2))\n", + "\n", + " @property\n", + " def height(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает высоту прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return float(round(self.top_left[1] - self.bottom_left[1], 2))\n", + "\n", + " def get_pos(self) -> Coord:\n", + " \"\"\"\n", + " Возвращает координаты верхнего левого угла.\n", + "\n", + " Returns:\n", + " Coord: Координаты верхнего левого угла.\n", + " \"\"\"\n", + " return round(self.top_left[0], 2), round(self.top_left[1], 2)\n", + "\n", + " def get_size(self) -> tuple[float, float]:\n", + " \"\"\"\n", + " Возвращает ширину и высоту прямоугольника.\n", + "\n", + " Returns:\n", + " tuple[float, float]: Ширина и высота прямоугольника.\n", + " \"\"\"\n", + " return (self.width, self.height)\n", + "\n", + " def move(self, delta_x: float, delta_y: float) -> None:\n", + " \"\"\"\n", + " Перемещает прямоугольник на заданные расстояния.\n", + "\n", + " Args:\n", + " delta_x (float): Расстояние по оси X.\n", + " delta_y (float): Расстояние по оси Y.\n", + " \"\"\"\n", + " xtl, ytl = self.top_left\n", + " self.top_left = (xtl + delta_x, ytl + delta_y)\n", + "\n", + " xbl, ybl = self.bottom_left\n", + " self.bottom_left = (xbl + delta_x, ybl + delta_y)\n", + "\n", + " xtr, ytr = self.top_right\n", + " self.top_right = (xtr + delta_x, ytr + delta_y)\n", + "\n", + " xbr, ybr = self.bottom_right\n", + " self.bottom_right = (xbr + delta_x, ybr + delta_y)\n", + "\n", + " def resize(self, width: float, height: float) -> None:\n", + " \"\"\"\n", + " Изменяет размеры прямоугольника.\n", + "\n", + " Оставляет неизменной координаты левого верхнего угла.\n", + "\n", + " Args:\n", + " width (float): Новая ширина.\n", + " height (float): Новая высота.\n", + " \"\"\"\n", + " xtl, ytl = self.top_left\n", + "\n", + " self.bottom_left = (xtl, ytl - height)\n", + " self.top_right = (xtl + width, ytl)\n", + " self.bottom_right = (xtl + width, ytl - height)" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "class RectangleG:\n", + " \"\"\"\n", + " Прямоугольник.\n", + "\n", + " Attributes:\n", + " top_left (Coord): Координаты верхнего левого угла.\n", + " bottom_left (Coord): Координаты нижнего левого угла.\n", + " top_right (float): Координаты верхнего правого угла.\n", + " bottom_right (float): Координаты нижнего правого угла.\n", + " \"\"\"\n", + "\n", + " top_left: Coord\n", + " bottom_left: Coord\n", + " top_right: Coord\n", + " bottom_right: Coord\n", + "\n", + " def __init__(self, first: Coord, second: Coord) -> None:\n", + " \"\"\"\n", + " Создает прямоугольник.\n", + "\n", + " Args:\n", + " first (Coord): Координаты одного из противоположных углов.\n", + " second (Coord): Координаты второго из противоположных углов.\n", + " \"\"\"\n", + " (x1, y1), (x2, y2) = first, second\n", + "\n", + " self.top_left = (min(x1, x2), max(y1, y2))\n", + " self.bottom_left = (min(x1, x2), min(y1, y2))\n", + " self.top_right = (max(x1, x2), max(y1, y2))\n", + " self.bottom_right = (max(x1, x2), min(y1, y2))\n", + "\n", + " def perimeter(self) -> float:\n", + " \"\"\"\n", + " Возвращает периметр прямоугольника.\n", + "\n", + " Returns:\n", + " float: Периметр.\n", + " \"\"\"\n", + " return float(round(self.width * 2 + self.height * 2, 2))\n", + "\n", + " def area(self) -> float:\n", + " \"\"\"\n", + " Возвращает площадь прямоугольника.\n", + "\n", + " Returns:\n", + " float: Площадь.\n", + " \"\"\"\n", + " return float(round(self.width * self.height, 2))\n", + "\n", + " @property\n", + " def width(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает ширину прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return float(round(self.top_right[0] - self.top_left[0], 2))\n", + "\n", + " @property\n", + " def height(self) -> float:\n", + " \"\"\"\n", + " Геттер, который возвращает высоту прямоугольника.\n", + "\n", + " Returns:\n", + " float: Ширина прямоугольника\n", + " \"\"\"\n", + " return float(round(self.top_left[1] - self.bottom_left[1], 2))\n", + "\n", + " def get_pos(self) -> Coord:\n", + " \"\"\"\n", + " Возвращает координаты верхнего левого угла.\n", + "\n", + " Returns:\n", + " Coord: Координаты верхнего левого угла.\n", + " \"\"\"\n", + " return round(self.top_left[0], 2), round(self.top_left[1], 2)\n", + "\n", + " def get_size(self) -> tuple[float, float]:\n", + " \"\"\"\n", + " Возвращает ширину и высоту прямоугольника.\n", + "\n", + " Returns:\n", + " tuple[float, float]: Ширина и высота прямоугольника.\n", + " \"\"\"\n", + " return (self.width, self.height)\n", + "\n", + " def move(self, delta_x: float, delta_y: float) -> None:\n", + " \"\"\"\n", + " Перемещает прямоугольник на заданные расстояния.\n", + "\n", + " Args:\n", + " delta_x (float): Расстояние по оси X.\n", + " delta_y (float): Расстояние по оси Y.\n", + " \"\"\"\n", + " xtl, ytl = self.top_left\n", + " self.top_left = (xtl + delta_x, ytl + delta_y)\n", + "\n", + " xbl, ybl = self.bottom_left\n", + " self.bottom_left = (xbl + delta_x, ybl + delta_y)\n", + "\n", + " xtr, ytr = self.top_right\n", + " self.top_right = (xtr + delta_x, ytr + delta_y)\n", + "\n", + " xbr, ybr = self.bottom_right\n", + " self.bottom_right = (xbr + delta_x, ybr + delta_y)\n", + "\n", + " def resize(self, width: float, height: float) -> None:\n", + " \"\"\"\n", + " Изменяет размеры прямоугольника.\n", + "\n", + " Оставляет неизменной координаты левого верхнего угла.\n", + "\n", + " Args:\n", + " width (float): Новая ширина.\n", + " height (float): Новая высота.\n", + " \"\"\"\n", + " xtl, ytl = self.top_left\n", + "\n", + " self.bottom_left = (xtl, ytl - height)\n", + " self.top_right = (xtl + width, ytl)\n", + " self.bottom_right = (xtl + width, ytl - height)\n", + "\n", + " @property\n", + " def center(self) -> Coord:\n", + " \"\"\"\n", + " Вычисляет центр прямоугольника.\n", + "\n", + " Returns:\n", + " Coord: Центр прямоугольника.\n", + " \"\"\"\n", + " xtr, ytr = self.top_right\n", + "\n", + " center_x = float(round(xtr - self.width / 2, 2))\n", + " center_y = float(round(ytr - self.height / 2, 2))\n", + "\n", + " return center_x, center_y\n", + "\n", + " def turn(self) -> None:\n", + " \"\"\"Поворачивает прямоугольник на 90 градусов.\"\"\"\n", + " center_x, center_y = self.center\n", + "\n", + " half_width = self.width / 2\n", + " half_height = self.height / 2\n", + "\n", + " self.top_left = (center_x - half_height, center_y + half_width)\n", + " self.top_right = (center_x + half_height, center_y + half_width)\n", + " self.bottom_left = (center_x - half_height, center_y - half_width)\n", + " self.bottom_right = (center_x + half_height, center_y - half_width)\n", + "\n", + " def scale(self, factor: float) -> None:\n", + " \"\"\"\n", + " Изменяет размер прямоугольника в указанное количество раз.\n", + "\n", + " Изменения происходят относительно центра\n", + "\n", + " Args:\n", + " factor (_type_): _description_\n", + " \"\"\"\n", + " new_width = round(self.width * factor, 2)\n", + " new_height = round(self.height * factor, 2)\n", + "\n", + " half_width = new_width / 2\n", + " half_height = new_height / 2\n", + "\n", + " center_x, center_y = self.center\n", + "\n", + " self.top_left = (center_x - half_width, center_y + half_height)\n", + " self.top_right = (center_x + half_width, center_y + half_height)\n", + " self.bottom_left = (center_x - half_width, center_y - half_height)\n", + " self.bottom_right = (center_x + half_width, center_y - half_height)" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "CellState = Literal[\"W\", \"B\", \"X\"]\n", + "\n", + "\n", + "class Cell:\n", + " \"\"\"\n", + " Клетка игральной доски.\n", + "\n", + " Attributes:\n", + " state (CellState): Текущее состояние:\n", + " W - белая шашка\n", + " B - черная шашка\n", + " X - пустая клетка\n", + " \"\"\"\n", + "\n", + " state: CellState\n", + "\n", + " def __init__(self, state: CellState) -> None:\n", + " \"\"\"\n", + " Создает клетку.\n", + "\n", + " Args:\n", + " state (CellState): Начальное состояние клетки.\n", + " \"\"\"\n", + " self.change_status(state)\n", + "\n", + " def status(self) -> CellState:\n", + " \"\"\"\n", + " Возвращает текущее состояние клетки.\n", + "\n", + " Returns:\n", + " CellState: Текущее состояние клетки.\n", + " \"\"\"\n", + " return self.state\n", + "\n", + " def change_status(self, state: CellState) -> None:\n", + " \"\"\"\n", + " Изменяет текущее состояние клетки.\n", + "\n", + " Args:\n", + " state (CellState): Новое состояние.\n", + " \"\"\"\n", + " self.state = state\n", + "\n", + "\n", + "class Checkers:\n", + " \"\"\"\n", + " Игральная доска.\n", + "\n", + " Attributes:\n", + " matrix: list[list[Cell]]: Матрица 8х8, в которой хранятся объекты Cell.\n", + " \"\"\"\n", + "\n", + " columns: dict[str, int] = {\n", + " char: index for index, char in enumerate(\"ABCDEFGH\")\n", + " }\n", + "\n", + " rows: dict[str, int] = {\n", + " char: index for index, char in enumerate(\"12345678\")\n", + " }\n", + "\n", + " matrix: list[list[Cell]]\n", + "\n", + " def __init__(self) -> None:\n", + " \"\"\"Создает игральную доску и заполняет ее.\"\"\"\n", + " self.matrix = []\n", + "\n", + " self.fill_row(\"W\", \"even\")\n", + " self.fill_row(\"W\", \"odd\")\n", + " self.fill_row(\"W\", \"even\")\n", + " self.fill_row()\n", + " self.fill_row()\n", + " self.fill_row(\"B\", \"odd\")\n", + " self.fill_row(\"B\", \"even\")\n", + " self.fill_row(\"B\", \"odd\")\n", + "\n", + " def fill_row(\n", + " self,\n", + " state: CellState = \"X\",\n", + " cell_type: Literal[\"even\", \"odd\"] | None = None,\n", + " ) -> None:\n", + " \"\"\"\n", + " Добавляет ряд и заполняет его.\n", + "\n", + " Если состояние передано, то заполняет четные/нечетные координаты\n", + " ячейками с указанным состоянием. В остальных случаях заполняет\n", + " координаты ячейкой с состоянием Х.\n", + "\n", + " Args:\n", + " state (CellState, optional): Состояние ячейки. олчанию X.\n", + " cell_type (Literal[\"even\", \"odd\"] | None, optional): Тип столбца\n", + " (четный / нечетный). По умолчанию None.\n", + " \"\"\"\n", + " self.matrix.append([])\n", + "\n", + " row_num = len(self.matrix) - 1\n", + "\n", + " for index in range(8):\n", + " if cell_type == \"even\" and index % 2 == 0:\n", + " self.matrix[row_num].append(Cell(state))\n", + "\n", + " continue\n", + " if cell_type == \"odd\" and index % 2 == 1:\n", + " self.matrix[row_num].append(Cell(state))\n", + "\n", + " continue\n", + "\n", + " self.matrix[row_num].append(Cell(\"X\"))\n", + "\n", + " def get_cell(self, coords: str) -> Cell:\n", + " \"\"\"\n", + " Возвращает ячейку по указанным координатам.\n", + "\n", + " Args:\n", + " coords (str): Координаты.\n", + "\n", + " Returns:\n", + " Cell: Ячейка.\n", + " \"\"\"\n", + " row = self.rows[coords[1]]\n", + " column = self.columns[coords[0]]\n", + "\n", + " return self.matrix[row][column]\n", + "\n", + " def move(self, start: str, finish: str) -> None:\n", + " \"\"\"\n", + " Сделать ход.\n", + "\n", + " Args:\n", + " start (str): Начальные координаты.\n", + " finish (str): Конечные координаты.\n", + " \"\"\"\n", + " start_row = self.rows[start[1]]\n", + " start_column = self.columns[start[0]]\n", + "\n", + " finish_row = self.rows[finish[1]]\n", + " finish_column = self.columns[finish[0]]\n", + "\n", + " start_cell = self.matrix[start_row][start_column]\n", + " finish_cell = self.matrix[finish_row][finish_column]\n", + "\n", + " state = start_cell.status()\n", + " start_cell.change_status(\"X\")\n", + "\n", + " finish_cell.change_status(state)" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "class Queue:\n", + " \"\"\"\n", + " Очередь.\n", + "\n", + " Attributes:\n", + " queue (list[object]): Список, который обеспечивает хранение элементов\n", + " очереди.\n", + " \"\"\"\n", + "\n", + " queue: list[object]\n", + "\n", + " def __init__(self) -> None:\n", + " \"\"\"Создает очередь.\"\"\"\n", + " self.queue = []\n", + "\n", + " def is_empty(self) -> bool:\n", + " \"\"\"Проверяет, пустая ли очередь.\n", + "\n", + " Returns:\n", + " bool: True, если очередь пустая. Иначе False.\n", + " \"\"\"\n", + " return len(self.queue) == 0\n", + "\n", + " def push(self, item: object) -> None:\n", + " \"\"\"\n", + " Добавить элемент в конец очереди.\n", + "\n", + " Args:\n", + " item (object): Добавляемый элемент.\n", + " \"\"\"\n", + " self.queue.append(item)\n", + "\n", + " def pop(self) -> object:\n", + " \"\"\"\n", + " Вернуть элемент из начала очереди.\n", + "\n", + " Удаляет элемент из очереди.\n", + "\n", + " Returns:\n", + " object: Элемент из начала очереди.\n", + " \"\"\"\n", + " return self.queue.pop(0)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "class Stack:\n", + " \"\"\"\n", + " Стэк.\n", + "\n", + " Attributes:\n", + " stack (list[object]): Список, который обеспечивает хранение элементов\n", + " стэка.\n", + " \"\"\"\n", + "\n", + " stack: list[object]\n", + "\n", + " def __init__(self) -> None:\n", + " \"\"\"Создает стэк.\"\"\"\n", + " self.stack = []\n", + "\n", + " def is_empty(self) -> bool:\n", + " \"\"\"Проверяет, пуст ли стэк.\n", + "\n", + " Returns:\n", + " bool: True, если стэк пуст. Иначе False.\n", + " \"\"\"\n", + " return len(self.stack) == 0\n", + "\n", + " def push(self, item: object) -> None:\n", + " \"\"\"\n", + " Добавить элемент в конец стэка.\n", + "\n", + " Args:\n", + " item (object): Добавляемый элемент.\n", + " \"\"\"\n", + " self.stack.append(item)\n", + "\n", + " def pop(self) -> object:\n", + " \"\"\"\n", + " Вернуть последний элемент из стэка.\n", + "\n", + " Удаляет элемент из стэка.\n", + "\n", + " Returns:\n", + " object: Последний элемент из стэка.\n", + " \"\"\"\n", + " return self.stack.pop()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_5_1.py b/Python/yandex-python-handbook/chapter_5_1.py new file mode 100644 index 00000000..50ba3a42 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_1.py @@ -0,0 +1,780 @@ +"""Задания к главе 5.1. + +Объектная модель Python. Классы, поля и методы. + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from typing import Literal, Self + + +class Point: + """Точка на плоскости с двумя координатами. + + Предоставляет методы: + move() - перемещение точки. + length() - вычисление расстояния до другой точки. + + Attributes: + x (int): Координата по оси X. + y (int): Координата по оси Y. + """ + + def __init__(self, x_coord: int, y_coord: int) -> None: + """Создает точку с заданными координатами. + + Args: + x_coord (int): Координата по оси X. + y_coord (int): Координата по оси Y. + """ + self.x = x_coord + self.y = y_coord + + +# - + +# B + + +class PointB: + """Точка на плоскости с двумя координатами. + + Предоставляет методы: + move() - перемещение точки. + length() - вычисление расстояния до другой точки. + + Attributes: + x (int): Координата по оси X. + y (int): Координата по оси Y. + """ + + def __init__(self, x_coord: int, y_coord: int) -> None: + """Создает точку с заданными координатами. + + Args: + x_coord (int): Координата по оси X. + y_coord (int): Координата по оси Y. + """ + self.x = x_coord + self.y = y_coord + + def move(self, delta_x: int, delta_y: int) -> None: + """Перемещает точку на указанное расстояние. + + Args: + delta_x (int): Расстояние по оси X. + delta_y (int): Расстояние по оси Y. + + Returns: + None: Функция ничего не возвращает. + """ + self.x += delta_x + self.y += delta_y + + def length(self, other: Self) -> float: + """Возвращает расстояние до другой точки. + + Args: + other (Self): Другая точка. + + Returns: + float: Расстояние. + """ + result = ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5 + return float(round(result, 2)) + + +# C + + +class RedButton: + """Красная кнопка, которое считает количество нажатий. + + На каждое нажатие вывод сообщение "Тревога!" + + Attributes: + counter (int): Количество раз, сколько была нажата кнопка. + """ + + counter: int + + def __init__(self) -> None: + """Создает красную кнопку.""" + self.counter = 0 + + def click(self) -> None: + """Осуществляет клик по кнопке. + + Увеличивает счетчик кликов. + Выводит сообщение "Тревога!" + + Returns: + None: Функция ничего не возвращает. + """ + self.counter += 1 + + print("Тревога!") + + def count(self) -> int: + """Возвращает текущее количество нажатий. + + Returns: + int: Количество нажатий. + """ + return self.counter + + +# D + +# + +Coord = tuple[float, float] + + +class RectangleD: + """Прямоугольник. + + Attributes: + top_left (Coord): Координаты верхнего левого угла. + bottom_left (Coord): Координаты нижнего левого угла. + top_right (float): Координаты верхнего правого угла. + bottom_right (float): Координаты нижнего правого угла. + """ + + top_left: Coord + bottom_left: Coord + top_right: Coord + bottom_right: Coord + + def __init__(self, first: Coord, second: Coord) -> None: + """Создает прямоугольник. + + Args: + first (Coord): Координаты одного из противоположных углов. + second (Coord): Координаты второго из противоположных углов. + """ + (x1, y1), (x2, y2) = first, second + + self.top_left = (min(x1, x2), max(y1, y2)) + self.bottom_left = (min(x1, x2), min(y1, y2)) + self.top_right = (max(x1, x2), max(y1, y2)) + self.bottom_right = (max(x1, x2), min(y1, y2)) + self.second = second + + def perimeter(self) -> float: + """Возвращает периметр прямоугольника. + + Returns: + float: Периметр. + """ + return float(round(self.width * 2 + self.height * 2, 2)) + + def area(self) -> float: + """Возвращает площадь прямоугольника. + + Returns: + float: Площадь. + """ + return float(round(self.width * self.height, 2)) + + @property + def width(self) -> float: + """Геттер, который возвращает ширину прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return self.top_right[0] - self.top_left[0] + + @property + def height(self) -> float: + """Геттер, который возвращает высоту прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return self.top_left[1] - self.bottom_left[1] + + +# - + +# E + + +class RectangleE: + """Прямоугольник. + + Attributes: + top_left (Coord): Координаты верхнего левого угла. + bottom_left (Coord): Координаты нижнего левого угла. + top_right (float): Координаты верхнего правого угла. + bottom_right (float): Координаты нижнего правого угла. + """ + + top_left: Coord + bottom_left: Coord + top_right: Coord + bottom_right: Coord + + def __init__(self, first: Coord, second: Coord) -> None: + """Создает прямоугольник. + + Args: + first (Coord): Координаты одного из противоположных углов. + second (Coord): Координаты второго из противоположных углов. + """ + (x1, y1), (x2, y2) = first, second + + self.top_left = (min(x1, x2), max(y1, y2)) + self.bottom_left = (min(x1, x2), min(y1, y2)) + self.top_right = (max(x1, x2), max(y1, y2)) + self.bottom_right = (max(x1, x2), min(y1, y2)) + self.second = second + + def perimeter(self) -> float: + """Возвращает периметр прямоугольника. + + Returns: + float: Периметр. + """ + return float(round(self.width * 2 + self.height * 2, 2)) + + def area(self) -> float: + """Возвращает площадь прямоугольника. + + Returns: + float: Площадь. + """ + return float(round(self.width * self.height, 2)) + + @property + def width(self) -> float: + """Геттер, который возвращает ширину прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return self.top_right[0] - self.top_left[0] + + @property + def height(self) -> float: + """Геттер, который возвращает высоту прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return self.top_left[1] - self.bottom_left[1] + + +# F + + +class RectangleF: + """Прямоугольник. + + Attributes: + top_left (Coord): Координаты верхнего левого угла. + bottom_left (Coord): Координаты нижнего левого угла. + top_right (float): Координаты верхнего правого угла. + bottom_right (float): Координаты нижнего правого угла. + """ + + top_left: Coord + bottom_left: Coord + top_right: Coord + bottom_right: Coord + + def __init__(self, first: Coord, second: Coord) -> None: + """Создает прямоугольник. + + Args: + first (Coord): Координаты одного из противоположных углов. + second (Coord): Координаты второго из противоположных углов. + """ + (x1, y1), (x2, y2) = first, second + + self.top_left = (min(x1, x2), max(y1, y2)) + self.bottom_left = (min(x1, x2), min(y1, y2)) + self.top_right = (max(x1, x2), max(y1, y2)) + self.bottom_right = (max(x1, x2), min(y1, y2)) + + def perimeter(self) -> float: + """Возвращает периметр прямоугольника. + + Returns: + float: Периметр. + """ + return float(round(self.width * 2 + self.height * 2, 2)) + + def area(self) -> float: + """Возвращает площадь прямоугольника. + + Returns: + float: Площадь. + """ + return float(round(self.width * self.height, 2)) + + @property + def width(self) -> float: + """Геттер, который возвращает ширину прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return float(round(self.top_right[0] - self.top_left[0], 2)) + + @property + def height(self) -> float: + """Геттер, который возвращает высоту прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return float(round(self.top_left[1] - self.bottom_left[1], 2)) + + def get_pos(self) -> Coord: + """Возвращает координаты верхнего левого угла. + + Returns: + Coord: Координаты верхнего левого угла. + """ + return round(self.top_left[0], 2), round(self.top_left[1], 2) + + def get_size(self) -> tuple[float, float]: + """Возвращает ширину и высоту прямоугольника. + + Returns: + tuple[float, float]: Ширина и высота прямоугольника. + """ + return (self.width, self.height) + + def move(self, delta_x: float, delta_y: float) -> None: + """Перемещает прямоугольник на заданные расстояния. + + Args: + delta_x (float): Расстояние по оси X. + delta_y (float): Расстояние по оси Y. + """ + xtl, ytl = self.top_left + self.top_left = (xtl + delta_x, ytl + delta_y) + + xbl, ybl = self.bottom_left + self.bottom_left = (xbl + delta_x, ybl + delta_y) + + xtr, ytr = self.top_right + self.top_right = (xtr + delta_x, ytr + delta_y) + + xbr, ybr = self.bottom_right + self.bottom_right = (xbr + delta_x, ybr + delta_y) + + def resize(self, width: float, height: float) -> None: + """Изменяет размеры прямоугольника. + + Оставляет неизменной координаты левого верхнего угла. + + Args: + width (float): Новая ширина. + height (float): Новая высота. + """ + xtl, ytl = self.top_left + + self.bottom_left = (xtl, ytl - height) + self.top_right = (xtl + width, ytl) + self.bottom_right = (xtl + width, ytl - height) + + +# G + + +class RectangleG: + """Прямоугольник. + + Attributes: + top_left (Coord): Координаты верхнего левого угла. + bottom_left (Coord): Координаты нижнего левого угла. + top_right (float): Координаты верхнего правого угла. + bottom_right (float): Координаты нижнего правого угла. + """ + + top_left: Coord + bottom_left: Coord + top_right: Coord + bottom_right: Coord + + def __init__(self, first: Coord, second: Coord) -> None: + """Создает прямоугольник. + + Args: + first (Coord): Координаты одного из противоположных углов. + second (Coord): Координаты второго из противоположных углов. + """ + (x1, y1), (x2, y2) = first, second + + self.top_left = (min(x1, x2), max(y1, y2)) + self.bottom_left = (min(x1, x2), min(y1, y2)) + self.top_right = (max(x1, x2), max(y1, y2)) + self.bottom_right = (max(x1, x2), min(y1, y2)) + + def perimeter(self) -> float: + """Возвращает периметр прямоугольника. + + Returns: + float: Периметр. + """ + return float(round(self.width * 2 + self.height * 2, 2)) + + def area(self) -> float: + """Возвращает площадь прямоугольника. + + Returns: + float: Площадь. + """ + return float(round(self.width * self.height, 2)) + + @property + def width(self) -> float: + """Геттер, который возвращает ширину прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return float(round(self.top_right[0] - self.top_left[0], 2)) + + @property + def height(self) -> float: + """Геттер, который возвращает высоту прямоугольника. + + Returns: + float: Ширина прямоугольника + """ + return float(round(self.top_left[1] - self.bottom_left[1], 2)) + + def get_pos(self) -> Coord: + """Возвращает координаты верхнего левого угла. + + Returns: + Coord: Координаты верхнего левого угла. + """ + return round(self.top_left[0], 2), round(self.top_left[1], 2) + + def get_size(self) -> tuple[float, float]: + """Возвращает ширину и высоту прямоугольника. + + Returns: + tuple[float, float]: Ширина и высота прямоугольника. + """ + return (self.width, self.height) + + def move(self, delta_x: float, delta_y: float) -> None: + """Перемещает прямоугольник на заданные расстояния. + + Args: + delta_x (float): Расстояние по оси X. + delta_y (float): Расстояние по оси Y. + """ + xtl, ytl = self.top_left + self.top_left = (xtl + delta_x, ytl + delta_y) + + xbl, ybl = self.bottom_left + self.bottom_left = (xbl + delta_x, ybl + delta_y) + + xtr, ytr = self.top_right + self.top_right = (xtr + delta_x, ytr + delta_y) + + xbr, ybr = self.bottom_right + self.bottom_right = (xbr + delta_x, ybr + delta_y) + + def resize(self, width: float, height: float) -> None: + """Изменяет размеры прямоугольника. + + Оставляет неизменной координаты левого верхнего угла. + + Args: + width (float): Новая ширина. + height (float): Новая высота. + """ + xtl, ytl = self.top_left + + self.bottom_left = (xtl, ytl - height) + self.top_right = (xtl + width, ytl) + self.bottom_right = (xtl + width, ytl - height) + + @property + def center(self) -> Coord: + """Вычисляет центр прямоугольника. + + Returns: + Coord: Центр прямоугольника. + """ + xtr, ytr = self.top_right + + center_x = float(round(xtr - self.width / 2, 2)) + center_y = float(round(ytr - self.height / 2, 2)) + + return center_x, center_y + + def turn(self) -> None: + """Поворачивает прямоугольник на 90 градусов.""" + center_x, center_y = self.center + + half_width = self.width / 2 + half_height = self.height / 2 + + self.top_left = (center_x - half_height, center_y + half_width) + self.top_right = (center_x + half_height, center_y + half_width) + self.bottom_left = (center_x - half_height, center_y - half_width) + self.bottom_right = (center_x + half_height, center_y - half_width) + + def scale(self, factor: float) -> None: + """Изменяет размер прямоугольника в указанное количество раз. + + Изменения происходят относительно центра + + Args: + factor (_type_): _description_ + """ + new_width = round(self.width * factor, 2) + new_height = round(self.height * factor, 2) + + half_width = new_width / 2 + half_height = new_height / 2 + + center_x, center_y = self.center + + self.top_left = (center_x - half_width, center_y + half_height) + self.top_right = (center_x + half_width, center_y + half_height) + self.bottom_left = (center_x - half_width, center_y - half_height) + self.bottom_right = (center_x + half_width, center_y - half_height) + + +# H + +# + +CellState = Literal["W", "B", "X"] + + +class Cell: + """Клетка игральной доски. + + Attributes: + state (CellState): Текущее состояние: + W - белая шашка + B - черная шашка + X - пустая клетка + """ + + state: CellState + + def __init__(self, state: CellState) -> None: + """Создает клетку. + + Args: + state (CellState): Начальное состояние клетки. + """ + self.change_status(state) + + def status(self) -> CellState: + """Возвращает текущее состояние клетки. + + Returns: + CellState: Текущее состояние клетки. + """ + return self.state + + def change_status(self, state: CellState) -> None: + """Изменяет текущее состояние клетки. + + Args: + state (CellState): Новое состояние. + """ + self.state = state + + +class Checkers: + """Игральная доска. + + Attributes: + matrix: list[list[Cell]]: Матрица 8х8, в которой хранятся объекты Cell. + """ + + columns: dict[str, int] = { + char: index for index, char in enumerate("ABCDEFGH") + } + + rows: dict[str, int] = { + char: index for index, char in enumerate("12345678") + } + + matrix: list[list[Cell]] + + def __init__(self) -> None: + """Создает игральную доску и заполняет ее.""" + self.matrix = [] + + self.fill_row("W", "even") + self.fill_row("W", "odd") + self.fill_row("W", "even") + self.fill_row() + self.fill_row() + self.fill_row("B", "odd") + self.fill_row("B", "even") + self.fill_row("B", "odd") + + def fill_row( + self, + state: CellState = "X", + cell_type: Literal["even", "odd"] | None = None, + ) -> None: + """Добавляет ряд и заполняет его. + + Если состояние передано, то заполняет четные/нечетные координаты + ячейками с указанным состоянием. В остальных случаях заполняет + координаты ячейкой с состоянием Х. + + Args: + state (CellState, optional): Состояние ячейки. олчанию X. + cell_type (Literal["even", "odd"] | None, optional): Тип столбца + (четный / нечетный). По умолчанию None. + """ + self.matrix.append([]) + + row_num = len(self.matrix) - 1 + + for index in range(8): + if cell_type == "even" and index % 2 == 0: + self.matrix[row_num].append(Cell(state)) + + continue + if cell_type == "odd" and index % 2 == 1: + self.matrix[row_num].append(Cell(state)) + + continue + + self.matrix[row_num].append(Cell("X")) + + def get_cell(self, coords: str) -> Cell: + """Возвращает ячейку по указанным координатам. + + Args: + coords (str): Координаты. + + Returns: + Cell: Ячейка. + """ + row = self.rows[coords[1]] + column = self.columns[coords[0]] + + return self.matrix[row][column] + + def move(self, start: str, finish: str) -> None: + """Сделать ход. + + Args: + start (str): Начальные координаты. + finish (str): Конечные координаты. + """ + start_row = self.rows[start[1]] + start_column = self.columns[start[0]] + + finish_row = self.rows[finish[1]] + finish_column = self.columns[finish[0]] + + start_cell = self.matrix[start_row][start_column] + finish_cell = self.matrix[finish_row][finish_column] + + state = start_cell.status() + start_cell.change_status("X") + + finish_cell.change_status(state) + + +# - + +# I + + +class Queue: + """Очередь. + + Attributes: + queue (list[object]): Список, который обеспечивает хранение элементов + очереди. + """ + + queue: list[object] + + def __init__(self) -> None: + """Создает очередь.""" + self.queue = [] + + def is_empty(self) -> bool: + """Проверяет, пустая ли очередь. + + Returns: + bool: True, если очередь пустая. Иначе False. + """ + return len(self.queue) == 0 + + def push(self, item: object) -> None: + """Добавить элемент в конец очереди. + + Args: + item (object): Добавляемый элемент. + """ + self.queue.append(item) + + def pop(self) -> object: + """Вернуть элемент из начала очереди. + + Удаляет элемент из очереди. + + Returns: + object: Элемент из начала очереди. + """ + return self.queue.pop(0) + + +# J + + +class Stack: + """Стэк. + + Attributes: + stack (list[object]): Список, который обеспечивает хранение элементов + стэка. + """ + + stack: list[object] + + def __init__(self) -> None: + """Создает стэк.""" + self.stack = [] + + def is_empty(self) -> bool: + """Проверяет, пуст ли стэк. + + Returns: + bool: True, если стэк пуст. Иначе False. + """ + return len(self.stack) == 0 + + def push(self, item: object) -> None: + """Добавить элемент в конец стэка. + + Args: + item (object): Добавляемый элемент. + """ + self.stack.append(item) + + def pop(self) -> object: + """Вернуть последний элемент из стэка. + + Удаляет элемент из стэка. + + Returns: + object: Последний элемент из стэка. + """ + return self.stack.pop() diff --git a/Python/yandex-python-handbook/chapter_5_2_part_1.ipynb b/Python/yandex-python-handbook/chapter_5_2_part_1.ipynb new file mode 100644 index 00000000..a5a1b50b --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_1.ipynb @@ -0,0 +1,850 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 5.2.\n", + "\n", + "Волшебные методы, переопределение методов. Наследование.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\n", + "Из-за ограничения на количество строк в файле разбит на несколько частей.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "52663735", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "020c0eea", + "metadata": {}, + "outputs": [], + "source": [ + "from math import gcd\n", + "\n", + "\n", + "class Point:\n", + " \"\"\"\n", + " Точка на плоскости с двумя координатами.\n", + "\n", + " Предоставляет методы:\n", + " move() - перемещение точки.\n", + " length() - вычисление расстояния до другой точки.\n", + "\n", + " Attributes:\n", + " x (int): Координата по оси X.\n", + " y (int): Координата по оси Y.\n", + " \"\"\"\n", + "\n", + " def __init__(self, x_coord_: int, y_coord_: int) -> None:\n", + " \"\"\"\n", + " Создает точку с заданными координатами.\n", + "\n", + " Args:\n", + " x_coord (int): Координата по оси X.\n", + " y_coord (int): Координата по оси Y.\n", + " \"\"\"\n", + " self.x = x_coord_\n", + " self.y = y_coord_\n", + "\n", + " def move(self, delta_x_: int, delta_y_: int) -> None:\n", + " \"\"\"\n", + " Перемещает точку на указанное расстояние.\n", + "\n", + " Args:\n", + " delta_x (int): Расстояние по оси X.\n", + " delta_y (int): Расстояние по оси Y.\n", + "\n", + " Returns:\n", + " None: Функция ничего не возвращает.\n", + " \"\"\"\n", + " self.x += delta_x_\n", + " self.y += delta_y_\n", + "\n", + " def length(self, other: \"Point\") -> float:\n", + " \"\"\"Возвращает расстояние до другой точки.\n", + "\n", + " Args:\n", + " other (Self): Другая точка.\n", + "\n", + " Returns:\n", + " float: Расстояние.\n", + " \"\"\"\n", + " result_ = ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5\n", + " return float(round(result_, 2))\n", + "\n", + "\n", + "class PatchedPointA(Point):\n", + " \"\"\"\n", + " Расширенная точка - наследник класса Point.\n", + "\n", + " Расширяет базовый класс, добавляя возможность передавать координаты\n", + " в виде кортежа или не передавать координаты вовсе\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " first_coord: tuple[int, int] | int | None = None,\n", + " second_coord: int | None = None,\n", + " ) -> None:\n", + " \"\"\"\n", + " Создает расширенную точку.\n", + "\n", + " Координаты могут быть переданы в виде кортежа или не передаваться\n", + " вовсе.\n", + "\n", + " Args:\n", + " first_coord (tuple[int, int] | int | None, optional): Кортеж\n", + " координат или координата x.По умолчанию None.\n", + " second_coord (int | None, optional): Координата y.\n", + " По умолчанию None.\n", + " \"\"\"\n", + " if isinstance(first_coord, tuple):\n", + " x_coord, y_coord = first_coord\n", + " Point.__init__(self, x_coord, y_coord)\n", + "\n", + " elif first_coord is None or second_coord is None:\n", + " Point.__init__(self, 0, 0)\n", + "\n", + " else:\n", + " Point.__init__(self, first_coord, second_coord)" + ] + }, + { + "cell_type": "markdown", + "id": "9a7c198d", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc34bc16", + "metadata": {}, + "outputs": [], + "source": [ + "class PatchedPointB(Point):\n", + " \"\"\"\n", + " Расширенная точка - наследник класса Point.\n", + "\n", + " Расширяет базовый класс, добавляя возможность передавать координаты\n", + " в виде кортежа или не передавать координаты вовсе\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " first_coord: tuple[int, int] | int | None = None,\n", + " second_coord: int | None = None,\n", + " ) -> None:\n", + " \"\"\"\n", + " Создает расширенную точку.\n", + "\n", + " Координаты могут быть переданы в виде кортежа или не передаваться\n", + " вовсе.\n", + "\n", + " Args:\n", + " first_coord (tuple[int, int] | int | None, optional): Кортеж\n", + " координат или координата X.По умолчанию None.\n", + " second_coord (int | None, optional): Координата Y.\n", + " По умолчанию None.\n", + " \"\"\"\n", + " if isinstance(first_coord, tuple):\n", + " x_coord, y_coord = first_coord\n", + " Point.__init__(self, x_coord, y_coord)\n", + "\n", + " elif first_coord is None or second_coord is None:\n", + " Point.__init__(self, 0, 0)\n", + "\n", + " else:\n", + " Point.__init__(self, first_coord, second_coord)\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление объекта.\n", + "\n", + " Returns:\n", + " str: Строковое представление объекта.\n", + " \"\"\"\n", + " return f\"({self.x}, {self.y})\"\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление объекта.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление объекта.\n", + " \"\"\"\n", + " return f\"PatchedPoint({self.x}, {self.y})\"" + ] + }, + { + "cell_type": "markdown", + "id": "6688b968", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6115c69", + "metadata": {}, + "outputs": [], + "source": [ + "class PatchedPointC(Point):\n", + " \"\"\"\n", + " Расширенная точка - наследник класса Point.\n", + "\n", + " Расширяет базовый класс, добавляя возможность передавать координаты\n", + " в виде кортежа или не передавать координаты вовсе\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " first_coord: tuple[int, int] | int | None = None,\n", + " second_coord: int | None = None,\n", + " ) -> None:\n", + " \"\"\"\n", + " Создает расширенную точку.\n", + "\n", + " Координаты могут быть переданы в виде кортежа или не передаваться\n", + " вовсе.\n", + "\n", + " Args:\n", + " first_coord (tuple[int, int] | int | None, optional): Кортеж\n", + " координат или координата x.По умолчанию None.\n", + " second_coord (int | None, optional): Координата y.\n", + " По умолчанию None.\n", + " \"\"\"\n", + " if isinstance(first_coord, tuple):\n", + " x_coord, y_coord = first_coord\n", + " Point.__init__(self, x_coord, y_coord)\n", + "\n", + " elif first_coord is None or second_coord is None:\n", + " Point.__init__(self, 0, 0)\n", + "\n", + " else:\n", + " Point.__init__(self, first_coord, second_coord)\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление объекта.\n", + "\n", + " Returns:\n", + " str: Строковое представление объекта.\n", + " \"\"\"\n", + " return f\"({self.x}, {self.y})\"\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление объекта.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление объекта.\n", + " \"\"\"\n", + " return f\"PatchedPoint({self.x}, {self.y})\"\n", + "\n", + " def __add__(self, deltas: tuple[int, int]) -> \"PatchedPointC\":\n", + " \"\"\"\n", + " Создает новую точку при использовании оператора +.\n", + "\n", + " Новая точка смещена относительно начальной точки на указанное\n", + " расстояние.\n", + "\n", + " Args:\n", + " deltas (tuple[int, int]): Расстояние по осям X и Y, на которое\n", + " должна быть смещена новая точка.\n", + "\n", + " Returns:\n", + " PatchedPoint: Новая расширенная точка PatchedPoint.\n", + " \"\"\"\n", + " return PatchedPointC(self.x + deltas[0], self.y + deltas[1])\n", + "\n", + " def __iadd__(self, deltas: tuple[int, int]) -> \"PatchedPointC\":\n", + " \"\"\"\n", + " Перемещает точку при использовании оператора +=.\n", + "\n", + " Args:\n", + " deltas (tuple[int, int]): Расстояния, на которые надо переместить\n", + " точку.\n", + "\n", + " Returns:\n", + " PatchedPoint: Перемещенная точка.\n", + " \"\"\"\n", + " self.move(deltas[0], deltas[1])\n", + "\n", + " return self" + ] + }, + { + "cell_type": "markdown", + "id": "db1f0d7b", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "018edb4e", + "metadata": {}, + "outputs": [], + "source": [ + "class Fraction:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val (int): Числитель.\n", + " denominator_val (int): Знаменатель.\n", + " \"\"\"\n", + "\n", + " numerator_val: int\n", + " denominator_val: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Высисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod = gcd(self.numerator_val, self.denominator_val)\n", + "\n", + " if nod != 1:\n", + " self.numerator_val = int(self.numerator_val / nod)\n", + " self.denominator_val = int(self.denominator_val / nod)\n", + "\n", + " def __init__(self, arg1: str | int, arg2: int | None = None) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1 (str | int): Строка, в которой значения числителя и\n", + " знаменателя разедлены символом /, или целое число -\n", + " значение числителя.\n", + " arg2 (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1, str):\n", + " numerator, denominator = map(int, arg1.split(\"/\"))\n", + "\n", + " elif isinstance(arg1, int) and isinstance(arg2, int):\n", + " numerator, denominator = arg1, arg2\n", + " else:\n", + " raise TypeError(\"Invalid args type\")\n", + "\n", + " self.numerator_val = numerator\n", + " self.denominator_val = denominator\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number is None:\n", + " return self.numerator_val\n", + "\n", + " self.numerator_val = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val\n", + "\n", + " def denominator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number is None:\n", + " return self.denominator_val\n", + "\n", + " self.denominator_val = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " return f\"{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " return f\"Fraction({self.numerator_val}, {self.denominator_val})\"" + ] + }, + { + "cell_type": "markdown", + "id": "5b31b2d1", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090b27b8", + "metadata": {}, + "outputs": [], + "source": [ + "MINUS = \"-\"\n", + "\n", + "\n", + "class FractionE:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val (int): Числитель.\n", + " denominator_val (int): Знаменатель.\n", + " sign (str): Знак дроби (- или пустая строка).\n", + " \"\"\"\n", + "\n", + " numerator_val: int\n", + " denominator_val: int\n", + " sign: str\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod = gcd(self.numerator_val, self.denominator_val)\n", + "\n", + " if nod != 1:\n", + " self.numerator_val = int(self.numerator_val / nod)\n", + " self.denominator_val = int(self.denominator_val / nod)\n", + "\n", + " def __init__(self, arg1: str | int, arg2: int | None = None) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1 (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg2 (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1, str):\n", + " numerator, denominator = map(int, arg1.split(\"/\"))\n", + "\n", + " elif isinstance(arg1, int) and isinstance(arg2, int):\n", + " numerator, denominator = arg1, arg2\n", + " else:\n", + " raise TypeError(\"Invalid args type\")\n", + "\n", + " self.numerator_val = abs(numerator)\n", + " self.denominator_val = abs(denominator)\n", + "\n", + " if (numerator < 0) ^ (denominator < 0):\n", + " self.sign = MINUS\n", + " else:\n", + " self.sign = \"\"\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number is None:\n", + " return self.numerator_val\n", + "\n", + " self.numerator_val = abs(number)\n", + "\n", + " if number < 0:\n", + " self.sign = MINUS if not self.sign else \"\"\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val\n", + "\n", + " def denominator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number is None:\n", + " return self.denominator_val\n", + "\n", + " self.denominator_val = abs(number)\n", + "\n", + " if number < 0:\n", + " self.sign = MINUS if not self.sign else \"\"\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.sign}{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return fraction\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.sign}{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return f\"Fraction('{fraction}')\"\n", + "\n", + " def __neg__(self) -> \"FractionE\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val\n", + "\n", + " if self.sign == \"\":\n", + " numerator *= -1\n", + "\n", + " return FractionE(numerator, self.denominator_val)" + ] + }, + { + "cell_type": "markdown", + "id": "9f625dc1", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c6ea8d8", + "metadata": {}, + "outputs": [], + "source": [ + "class FractionF:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val_ (int): Числитель.\n", + " denominator_val_ (int): Знаменатель.\n", + " sign (str): Знак дроби (- или пустая строка).\n", + " \"\"\"\n", + "\n", + " numerator_val_: int\n", + "\n", + " denominator_val_: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod = gcd(self.numerator_val_, self.denominator_val_)\n", + " self.numerator_val_ = int(self.numerator_val_ / nod)\n", + " self.denominator_val_ = int(self.denominator_val_ / nod)\n", + "\n", + " if self.denominator_val_ < 0 and self.numerator_val_ < 0:\n", + " self.numerator_val_ = abs(self.numerator_val_)\n", + " self.denominator_val_ = abs(self.denominator_val_)\n", + "\n", + " return\n", + "\n", + " if self.denominator_val_ < 0:\n", + " self.numerator_val_ = self.numerator_val_ * (-1)\n", + " self.denominator_val_ = abs(self.denominator_val_)\n", + "\n", + " def __init__(self, arg1: str | int, arg2: int | None = None) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1 (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg2 (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1, str):\n", + " numerator, denominator = map(int, arg1.split(\"/\"))\n", + "\n", + " elif isinstance(arg1, int) and isinstance(arg2, int):\n", + " numerator, denominator = arg1, arg2\n", + " else:\n", + " raise TypeError(\"Invalid args type\")\n", + "\n", + " self.numerator_val_ = numerator\n", + " self.denominator_val_ = denominator\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number is None:\n", + " return abs(self.numerator_val_)\n", + "\n", + " self.numerator_val_ = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val_\n", + "\n", + " def denominator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number is None:\n", + " return abs(self.denominator_val_)\n", + "\n", + " self.denominator_val_ = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val_\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val_}/{self.denominator_val_}\"\n", + "\n", + " return fraction\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val_}/{self.denominator_val_}\"\n", + "\n", + " return f\"Fraction('{fraction}')\"\n", + "\n", + " def __neg__(self) -> \"FractionF\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " PatchedPoint: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val_ * (-1)\n", + "\n", + " return FractionF(numerator, self.denominator_val_)\n", + "\n", + " def __add__(self, other: \"FractionF\") -> \"FractionF\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного +.\n", + "\n", + " Args:\n", + " other (FractionF): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionF: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val_ * other.denominator_val_\n", + " numerator2 = other.numerator_val_ * self.denominator_val_\n", + " numerator = numerator1 + numerator2\n", + " denominator = self.denominator_val_ * other.denominator_val_\n", + "\n", + " return FractionF(numerator, denominator)\n", + "\n", + " def __sub__(self, other: \"FractionF\") -> \"FractionF\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного -.\n", + "\n", + " Args:\n", + " other (FractionF): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionF: Новая дробь Fraction.\n", + " \"\"\"\n", + " return self + (-other)\n", + "\n", + " def __iadd__(self, other: \"FractionF\") -> \"FractionF\":\n", + " \"\"\"\n", + " Складывает дроби при использовании +=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionF): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionF: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val_ * other.denominator_val_\n", + " numerator2 = other.numerator_val_ * self.denominator_val_\n", + " self.numerator_val_ = numerator1 + numerator2\n", + " self.denominator_val_ = self.denominator_val_ * other.denominator_val_\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __isub__(self, other: \"FractionF\") -> \"FractionF\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании -=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionF): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionF: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val_ * other.denominator_val_\n", + " numerator2 = other.numerator_val_ * self.denominator_val_\n", + " self.numerator_val_ = numerator1 - numerator2\n", + " self.denominator_val_ = self.denominator_val_ * other.denominator_val_\n", + "\n", + " self._readuction()\n", + "\n", + " return self" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_5_2_part_1.py b/Python/yandex-python-handbook/chapter_5_2_part_1.py new file mode 100644 index 00000000..bb204b31 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_1.py @@ -0,0 +1,708 @@ +# %% +"""Задания к главе 5.2. + +Волшебные методы, переопределение методов. Наследование. + +Хендбук Яндекс "Основы Python". + +Из-за ограничения на количество строк в файле разбит на несколько частей. +""" + +# A + +# %% +from math import gcd + + +class Point: + """Точка на плоскости с двумя координатами. + + Предоставляет методы: + move() - перемещение точки. + length() - вычисление расстояния до другой точки. + + Attributes: + x (int): Координата по оси X. + y (int): Координата по оси Y. + """ + + def __init__(self, x_coord_: int, y_coord_: int) -> None: + """Создает точку с заданными координатами. + + Args: + x_coord (int): Координата по оси X. + y_coord (int): Координата по оси Y. + """ + self.x = x_coord_ + self.y = y_coord_ + + def move(self, delta_x_: int, delta_y_: int) -> None: + """Перемещает точку на указанное расстояние. + + Args: + delta_x (int): Расстояние по оси X. + delta_y (int): Расстояние по оси Y. + + Returns: + None: Функция ничего не возвращает. + """ + self.x += delta_x_ + self.y += delta_y_ + + def length(self, other: "Point") -> float: + """Возвращает расстояние до другой точки. + + Args: + other (Self): Другая точка. + + Returns: + float: Расстояние. + """ + result_ = ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5 + return float(round(result_, 2)) + + +class PatchedPointA(Point): + """ + Расширенная точка - наследник класса Point. + + Расширяет базовый класс, добавляя возможность передавать координаты + в виде кортежа или не передавать координаты вовсе + """ + + def __init__( + self, + first_coord: tuple[int, int] | int | None = None, + second_coord: int | None = None, + ) -> None: + """Создает расширенную точку. + + Координаты могут быть переданы в виде кортежа или не передаваться + вовсе. + + Args: + first_coord (tuple[int, int] | int | None, optional): Кортеж + координат или координата x.По умолчанию None. + second_coord (int | None, optional): Координата y. + По умолчанию None. + """ + if isinstance(first_coord, tuple): + x_coord, y_coord = first_coord + Point.__init__(self, x_coord, y_coord) + + elif first_coord is None or second_coord is None: + Point.__init__(self, 0, 0) + + else: + Point.__init__(self, first_coord, second_coord) + + +# B + + +# %% +class PatchedPointB(Point): + """ + Расширенная точка - наследник класса Point. + + Расширяет базовый класс, добавляя возможность передавать координаты + в виде кортежа или не передавать координаты вовсе + """ + + def __init__( + self, + first_coord: tuple[int, int] | int | None = None, + second_coord: int | None = None, + ) -> None: + """Создает расширенную точку. + + Координаты могут быть переданы в виде кортежа или не передаваться + вовсе. + + Args: + first_coord (tuple[int, int] | int | None, optional): Кортеж + координат или координата X.По умолчанию None. + second_coord (int | None, optional): Координата Y. + По умолчанию None. + """ + if isinstance(first_coord, tuple): + x_coord, y_coord = first_coord + Point.__init__(self, x_coord, y_coord) + + elif first_coord is None or second_coord is None: + Point.__init__(self, 0, 0) + + else: + Point.__init__(self, first_coord, second_coord) + + def __str__(self) -> str: + """Возвращает строковое представление объекта. + + Returns: + str: Строковое представление объекта. + """ + return f"({self.x}, {self.y})" + + def __repr__(self) -> str: + """Возвращает репрезентативное представление объекта. + + Returns: + str: Репрезентативное представление объекта. + """ + return f"PatchedPoint({self.x}, {self.y})" + + +# C + + +# %% +class PatchedPointC(Point): + """ + Расширенная точка - наследник класса Point. + + Расширяет базовый класс, добавляя возможность передавать координаты + в виде кортежа или не передавать координаты вовсе + """ + + def __init__( + self, + first_coord: tuple[int, int] | int | None = None, + second_coord: int | None = None, + ) -> None: + """Создает расширенную точку. + + Координаты могут быть переданы в виде кортежа или не передаваться + вовсе. + + Args: + first_coord (tuple[int, int] | int | None, optional): Кортеж + координат или координата x.По умолчанию None. + second_coord (int | None, optional): Координата y. + По умолчанию None. + """ + if isinstance(first_coord, tuple): + x_coord, y_coord = first_coord + Point.__init__(self, x_coord, y_coord) + + elif first_coord is None or second_coord is None: + Point.__init__(self, 0, 0) + + else: + Point.__init__(self, first_coord, second_coord) + + def __str__(self) -> str: + """Возвращает строковое представление объекта. + + Returns: + str: Строковое представление объекта. + """ + return f"({self.x}, {self.y})" + + def __repr__(self) -> str: + """Возвращает репрезентативное представление объекта. + + Returns: + str: Репрезентативное представление объекта. + """ + return f"PatchedPoint({self.x}, {self.y})" + + def __add__(self, deltas: tuple[int, int]) -> "PatchedPointC": + """Создает новую точку при использовании оператора +. + + Новая точка смещена относительно начальной точки на указанное + расстояние. + + Args: + deltas (tuple[int, int]): Расстояние по осям X и Y, на которое + должна быть смещена новая точка. + + Returns: + PatchedPoint: Новая расширенная точка PatchedPoint. + """ + return PatchedPointC(self.x + deltas[0], self.y + deltas[1]) + + def __iadd__(self, deltas: tuple[int, int]) -> "PatchedPointC": + """Перемещает точку при использовании оператора +=. + + Args: + deltas (tuple[int, int]): Расстояния, на которые надо переместить + точку. + + Returns: + PatchedPoint: Перемещенная точка. + """ + self.move(deltas[0], deltas[1]) + + return self + + +# D + + +# %% +class Fraction: + """Рациональная дробь. + + Attributes: + numerator_val (int): Числитель. + denominator_val (int): Знаменатель. + """ + + numerator_val: int + denominator_val: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Высисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod = gcd(self.numerator_val, self.denominator_val) + + if nod != 1: + self.numerator_val = int(self.numerator_val / nod) + self.denominator_val = int(self.denominator_val / nod) + + def __init__(self, arg1: str | int, arg2: int | None = None) -> None: + """Создает рациональную дробь. + + Args: + arg1 (str | int): Строка, в которой значения числителя и + знаменателя разедлены символом /, или целое число - + значение числителя. + arg2 (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1, str): + numerator, denominator = map(int, arg1.split("/")) + + elif isinstance(arg1, int) and isinstance(arg2, int): + numerator, denominator = arg1, arg2 + else: + raise TypeError("Invalid args type") + + self.numerator_val = numerator + self.denominator_val = denominator + + self._readuction() + + def numerator(self, number: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number is None: + return self.numerator_val + + self.numerator_val = number + + self._readuction() + + return self.numerator_val + + def denominator(self, number: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number is None: + return self.denominator_val + + self.denominator_val = number + + self._readuction() + + return self.denominator_val + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + return f"{self.numerator_val}/{self.denominator_val}" + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + return f"Fraction({self.numerator_val}, {self.denominator_val})" + + +# E + +# %% +MINUS = "-" + + +class FractionE: + """Рациональная дробь. + + Attributes: + numerator_val (int): Числитель. + denominator_val (int): Знаменатель. + sign (str): Знак дроби (- или пустая строка). + """ + + numerator_val: int + denominator_val: int + sign: str + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod = gcd(self.numerator_val, self.denominator_val) + + if nod != 1: + self.numerator_val = int(self.numerator_val / nod) + self.denominator_val = int(self.denominator_val / nod) + + def __init__(self, arg1: str | int, arg2: int | None = None) -> None: + """Создает рациональную дробь. + + Args: + arg1 (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg2 (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1, str): + numerator, denominator = map(int, arg1.split("/")) + + elif isinstance(arg1, int) and isinstance(arg2, int): + numerator, denominator = arg1, arg2 + else: + raise TypeError("Invalid args type") + + self.numerator_val = abs(numerator) + self.denominator_val = abs(denominator) + + if (numerator < 0) ^ (denominator < 0): + self.sign = MINUS + else: + self.sign = "" + + self._readuction() + + def numerator(self, number: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number is None: + return self.numerator_val + + self.numerator_val = abs(number) + + if number < 0: + self.sign = MINUS if not self.sign else "" + + self._readuction() + + return self.numerator_val + + def denominator(self, number: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number is None: + return self.denominator_val + + self.denominator_val = abs(number) + + if number < 0: + self.sign = MINUS if not self.sign else "" + + self._readuction() + + return self.denominator_val + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction = f"{self.sign}{self.numerator_val}/{self.denominator_val}" + + return fraction + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction = f"{self.sign}{self.numerator_val}/{self.denominator_val}" + + return f"Fraction('{fraction}')" + + def __neg__(self) -> "FractionE": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + FractionG: Новая дробь Fraction. + """ + numerator = self.numerator_val + + if self.sign == "": + numerator *= -1 + + return FractionE(numerator, self.denominator_val) + + +# F + + +# %% +class FractionF: + """Рациональная дробь. + + Attributes: + numerator_val_ (int): Числитель. + denominator_val_ (int): Знаменатель. + sign (str): Знак дроби (- или пустая строка). + """ + + numerator_val_: int + + denominator_val_: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod = gcd(self.numerator_val_, self.denominator_val_) + self.numerator_val_ = int(self.numerator_val_ / nod) + self.denominator_val_ = int(self.denominator_val_ / nod) + + if self.denominator_val_ < 0 and self.numerator_val_ < 0: + self.numerator_val_ = abs(self.numerator_val_) + self.denominator_val_ = abs(self.denominator_val_) + + return + + if self.denominator_val_ < 0: + self.numerator_val_ = self.numerator_val_ * (-1) + self.denominator_val_ = abs(self.denominator_val_) + + def __init__(self, arg1: str | int, arg2: int | None = None) -> None: + """Создает рациональную дробь. + + Args: + arg1 (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg2 (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1, str): + numerator, denominator = map(int, arg1.split("/")) + + elif isinstance(arg1, int) and isinstance(arg2, int): + numerator, denominator = arg1, arg2 + else: + raise TypeError("Invalid args type") + + self.numerator_val_ = numerator + self.denominator_val_ = denominator + + self._readuction() + + def numerator(self, number: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number is None: + return abs(self.numerator_val_) + + self.numerator_val_ = number + + self._readuction() + + return self.numerator_val_ + + def denominator(self, number: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number is None: + return abs(self.denominator_val_) + + self.denominator_val_ = number + + self._readuction() + + return self.denominator_val_ + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction = f"{self.numerator_val_}/{self.denominator_val_}" + + return fraction + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction = f"{self.numerator_val_}/{self.denominator_val_}" + + return f"Fraction('{fraction}')" + + def __neg__(self) -> "FractionF": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + PatchedPoint: Новая дробь Fraction. + """ + numerator = self.numerator_val_ * (-1) + + return FractionF(numerator, self.denominator_val_) + + def __add__(self, other: "FractionF") -> "FractionF": + """Складывает дроби при использовании бинарного +. + + Args: + other (FractionF): Прибавляемая дробь. + + Returns: + FractionF: Новая дробь Fraction. + """ + numerator1 = self.numerator_val_ * other.denominator_val_ + numerator2 = other.numerator_val_ * self.denominator_val_ + numerator = numerator1 + numerator2 + denominator = self.denominator_val_ * other.denominator_val_ + + return FractionF(numerator, denominator) + + def __sub__(self, other: "FractionF") -> "FractionF": + """Вычитает дроби при использовании бинарного -. + + Args: + other (FractionF): Вычитаемая дробь. + + Returns: + FractionF: Новая дробь Fraction. + """ + return self + (-other) + + def __iadd__(self, other: "FractionF") -> "FractionF": + """Складывает дроби при использовании +=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionF): Прибавляемая дробь. + + Returns: + FractionF: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val_ * other.denominator_val_ + numerator2 = other.numerator_val_ * self.denominator_val_ + self.numerator_val_ = numerator1 + numerator2 + self.denominator_val_ = self.denominator_val_ * other.denominator_val_ + + self._readuction() + + return self + + def __isub__(self, other: "FractionF") -> "FractionF": + """Вычитает дроби при использовании -=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionF): Вычитаемая дробь. + + Returns: + FractionF: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val_ * other.denominator_val_ + numerator2 = other.numerator_val_ * self.denominator_val_ + self.numerator_val_ = numerator1 - numerator2 + self.denominator_val_ = self.denominator_val_ * other.denominator_val_ + + self._readuction() + + return self diff --git a/Python/yandex-python-handbook/chapter_5_2_part_2.ipynb b/Python/yandex-python-handbook/chapter_5_2_part_2.ipynb new file mode 100644 index 00000000..52af2289 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_2.ipynb @@ -0,0 +1,744 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 5.2.\n", + "\n", + "Волшебные методы, переопределение методов. Наследование.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\n", + "Из-за ограничения на количество строк в файле разбит на несколько частей.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "50954764", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b479afd6", + "metadata": {}, + "outputs": [], + "source": [ + "from math import gcd\n", + "\n", + "\n", + "class FractionG:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val__ (int): Числитель.\n", + " denominator_val__ (int): Знаменатель.\n", + " \"\"\"\n", + "\n", + " numerator_val__: int\n", + " denominator_val__: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod = gcd(self.numerator_val__, self.denominator_val__)\n", + "\n", + " self.numerator_val__ = int(self.numerator_val__ / nod)\n", + " self.denominator_val__ = int(self.denominator_val__ / nod)\n", + "\n", + " if self.denominator_val__ < 0 and self.numerator_val__ < 0:\n", + " self.numerator_val__ = abs(self.numerator_val__)\n", + " self.denominator_val__ = abs(self.denominator_val__)\n", + " elif self.denominator_val__ < 0:\n", + " self.numerator_val__ = self.numerator_val__ * (-1)\n", + " self.denominator_val__ = abs(self.denominator_val__)\n", + "\n", + " def __init__(self, arg_1: str | int, arg_2: int | None = None) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg_1 (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg_2 (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg_1, str):\n", + " numerator, denominator = map(int, arg_1.split(\"/\"))\n", + "\n", + " self.numerator_val__ = numerator\n", + " self.denominator_val__ = denominator\n", + "\n", + " self._readuction()\n", + "\n", + " return\n", + "\n", + " if isinstance(arg_1, int) and isinstance(arg_2, int):\n", + " numerator, denominator = arg_1, arg_2\n", + "\n", + " self.numerator_val__ = numerator\n", + " self.denominator_val__ = denominator\n", + "\n", + " self._readuction()\n", + "\n", + " return\n", + "\n", + " raise TypeError(\"Invalid args type\")\n", + "\n", + " def numerator(self, number_arg: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number_arg (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number_arg is None:\n", + " return abs(self.numerator_val__)\n", + "\n", + " self.numerator_val__ = number_arg\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val__\n", + "\n", + " def denominator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number is None:\n", + " return abs(self.denominator_val__)\n", + "\n", + " self.denominator_val__ = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val__\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val__}/{self.denominator_val__}\"\n", + "\n", + " return fraction\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val__}/{self.denominator_val__}\"\n", + "\n", + " return f\"Fraction('{fraction}')\"\n", + "\n", + " def __neg__(self) -> \"FractionG\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * (-1)\n", + "\n", + " return FractionG(numerator, self.denominator_val__)\n", + "\n", + " def __add__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного +.\n", + "\n", + " Args:\n", + " other (FractionG): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " numerator = numerator1 + numerator2\n", + " denominator = self.denominator_val__ * other.denominator_val__\n", + "\n", + " return FractionG(numerator, denominator)\n", + "\n", + " def __sub__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного -.\n", + "\n", + " Args:\n", + " other (FractionG): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " return self + (-other)\n", + "\n", + " def __iadd__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Складывает дроби при использовании +=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionG): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionG: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " self.numerator_val__ = numerator1 + numerator2\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __isub__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании -=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionG): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionG: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " self.numerator_val__ = numerator1 - numerator2\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __mul__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *.\n", + "\n", + " Args:\n", + " other (FractionG): Второй множитель.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * other.numerator_val__\n", + " denominator = self.denominator_val__ * other.denominator_val__\n", + "\n", + " return FractionG(numerator, denominator)\n", + "\n", + " def __truediv__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Вычисляет частное от деления дробей при использовании /.\n", + "\n", + " Args:\n", + " other (FractionG): Делитель.\n", + "\n", + " Returns:\n", + " FractionG: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * other.denominator_val__\n", + " denominator = self.denominator_val__ * other.numerator_val__\n", + "\n", + " return FractionG(numerator, denominator)\n", + "\n", + " def __imul__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionG): Второй множитель.\n", + "\n", + " Returns:\n", + " FractionG: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val__ = self.numerator_val__ * other.numerator_val__\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __itruediv__(self, other: \"FractionG\") -> \"FractionG\":\n", + " \"\"\"\n", + " Вычисляет частное при делении дробей с использованием /=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionG): Делитель.\n", + "\n", + " Returns:\n", + " FractionG: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val__ = self.numerator_val__ * other.denominator_val__\n", + " self.denominator_val__ = self.denominator_val__ * other.numerator_val__\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def reverse(self) -> \"FractionG\":\n", + " \"\"\"Возвращает дробь, обратную текущей.\"\"\"\n", + " return FractionG(self.denominator_val__, self.numerator_val__)" + ] + }, + { + "cell_type": "markdown", + "id": "2b47c9f4", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc032881", + "metadata": {}, + "outputs": [], + "source": [ + "class FractionH:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val__ (int): Числитель.\n", + " denominator_val__ (int): Знаменатель.\n", + " \"\"\"\n", + "\n", + " numerator_val__: int\n", + " denominator_val__: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod = gcd(self.numerator_val__, self.denominator_val__)\n", + "\n", + " self.numerator_val__ = int(self.numerator_val__ / nod)\n", + " self.denominator_val__ = int(self.denominator_val__ / nod)\n", + "\n", + " if self.denominator_val__ < 0 and self.numerator_val__ < 0:\n", + " self.numerator_val__ = abs(self.numerator_val__)\n", + " self.denominator_val__ = abs(self.denominator_val__)\n", + " elif self.denominator_val__ < 0:\n", + " self.numerator_val__ = self.numerator_val__ * (-1)\n", + " self.denominator_val__ = abs(self.denominator_val__)\n", + "\n", + " def __init__(self, arg1: str | int, arg2: int | None = None) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1 (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg2 (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1, str):\n", + " numerator_, denominator_ = map(int, arg1.split(\"/\"))\n", + "\n", + " elif isinstance(arg1, int) and isinstance(arg2, int):\n", + " numerator_, denominator_ = arg1, arg2\n", + " else:\n", + " raise TypeError(\"Invalid args type\")\n", + "\n", + " self.numerator_val__ = numerator_\n", + " self.denominator_val__ = denominator_\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number is None:\n", + " return abs(self.numerator_val__)\n", + "\n", + " self.numerator_val__ = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val__\n", + "\n", + " def denominator(self, number: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number is None:\n", + " return abs(self.denominator_val__)\n", + "\n", + " self.denominator_val__ = number\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val__\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val__}/{self.denominator_val__}\"\n", + "\n", + " return fraction\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction = f\"{self.numerator_val__}/{self.denominator_val__}\"\n", + "\n", + " return f\"Fraction('{fraction}')\"\n", + "\n", + " def __neg__(self) -> \"FractionH\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " FractionH: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * (-1)\n", + "\n", + " return FractionH(numerator, self.denominator_val__)\n", + "\n", + " def __add__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного +.\n", + "\n", + " Args:\n", + " other (FractionH): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionH: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " numerator = numerator1 + numerator2\n", + " denominator = self.denominator_val__ * other.denominator_val__\n", + "\n", + " return FractionH(numerator, denominator)\n", + "\n", + " def __sub__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного -.\n", + "\n", + " Args:\n", + " other (FractionH): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionH: Новая дробь Fraction.\n", + " \"\"\"\n", + " return self + (-other)\n", + "\n", + " def __iadd__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Складывает дроби при использовании +=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionH): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionH: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " self.numerator_val__ = numerator1 + numerator2\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __isub__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании -=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionH): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionH: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " numerator1 = self.numerator_val__ * other.denominator_val__\n", + " numerator2 = other.numerator_val__ * self.denominator_val__\n", + " self.numerator_val__ = numerator1 - numerator2\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __mul__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *.\n", + "\n", + " Args:\n", + " other (FractionH): Второй множитель.\n", + "\n", + " Returns:\n", + " FractionH: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * other.numerator_val__\n", + " denominator = self.denominator_val__ * other.denominator_val__\n", + "\n", + " return FractionH(numerator, denominator)\n", + "\n", + " def __truediv__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Вычисляет частное от деления дробей при использовании /.\n", + "\n", + " Args:\n", + " other (FractionH): Делитель.\n", + "\n", + " Returns:\n", + " FractionH: Новая дробь Fraction.\n", + " \"\"\"\n", + " numerator = self.numerator_val__ * other.denominator_val__\n", + " denominator = self.denominator_val__ * other.numerator_val__\n", + "\n", + " return FractionH(numerator, denominator)\n", + "\n", + " def __imul__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionH): Второй множитель.\n", + "\n", + " Returns:\n", + " FractionH: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val__ = self.numerator_val__ * other.numerator_val__\n", + " self.denominator_val__ = (\n", + " self.denominator_val__ * other.denominator_val__\n", + " )\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __itruediv__(self, other: \"FractionH\") -> \"FractionH\":\n", + " \"\"\"\n", + " Вычисляет частное при делении дробей с использованием /=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionH): Делитель.\n", + "\n", + " Returns:\n", + " FractionH: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val__ = self.numerator_val__ * other.denominator_val__\n", + " self.denominator_val__ = self.denominator_val__ * other.numerator_val__\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def reverse(self) -> \"FractionH\":\n", + " \"\"\"Возвращает дробь, обратную текущей.\"\"\"\n", + " return FractionH(self.denominator_val__, self.numerator_val__)\n", + "\n", + " def __eq__(self, other: object) -> bool:\n", + " \"\"\"\n", + " Проверяет равенство дроби переданному аргументу.\n", + "\n", + " Args:\n", + " other (object): Объект, с которым сравнивается дробь.\n", + "\n", + " Returns:\n", + " bool: Возращает True, если переданный аргумент равен текущей дроби.\n", + " Иначе возвращает False.\n", + " \"\"\"\n", + " if isinstance(other, FractionH):\n", + " is_numerator_equal = self.numerator_val__ == other.numerator_val__\n", + " is_denominator_equal = (\n", + " self.denominator_val__ == other.denominator_val__\n", + " )\n", + "\n", + " return is_numerator_equal and is_denominator_equal\n", + "\n", + " return False\n", + "\n", + " def __lt__(self, other: \"FractionH\") -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь другой дроби.\n", + "\n", + " Args:\n", + " other (FractionH): Другая дробь для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго меньше other. Иначе False.\n", + " \"\"\"\n", + " mul1 = self.numerator_val__ * other.denominator_val__\n", + " mul2 = other.numerator_val__ * self.denominator_val__\n", + "\n", + " return mul1 < mul2\n", + "\n", + " def __le__(self, other: \"FractionH\") -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other (FractionH): Другая дробь для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь меньше или равна other. Иначе False.\n", + " \"\"\"\n", + " return self < other or self == other\n", + "\n", + " def __gt__(self, other: \"FractionH\") -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь другой дроби.\n", + "\n", + " Args:\n", + " other (FractionH): Другая дробь для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго больше other. Иначе False.\n", + " \"\"\"\n", + " return not self < other and not self == other\n", + "\n", + " def __ge__(self, other: \"FractionH\") -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other (FractionH): Другая дробь для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь больше или равна other. Иначе False.\n", + " \"\"\"\n", + " return self > other or self == other\n", + "\n", + " def __ne__(self, other: object) -> bool:\n", + " \"\"\"Определяет, не равны ли два объекта.\n", + "\n", + " Args:\n", + " other (object): Объект для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если объекты не равны. Иначе False.\n", + " \"\"\"\n", + " return not self == other" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_5_2_part_2.py b/Python/yandex-python-handbook/chapter_5_2_part_2.py new file mode 100644 index 00000000..88b0c99c --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_2.py @@ -0,0 +1,653 @@ +# %% +"""Задания к главе 5.2. + +Волшебные методы, переопределение методов. Наследование. + +Хендбук Яндекс "Основы Python". + +Из-за ограничения на количество строк в файле разбит на несколько частей. +""" + +# G + +# %% +from math import gcd + + +class FractionG: + """Рациональная дробь. + + Attributes: + numerator_val__ (int): Числитель. + denominator_val__ (int): Знаменатель. + """ + + numerator_val__: int + denominator_val__: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod = gcd(self.numerator_val__, self.denominator_val__) + + self.numerator_val__ = int(self.numerator_val__ / nod) + self.denominator_val__ = int(self.denominator_val__ / nod) + + if self.denominator_val__ < 0 and self.numerator_val__ < 0: + self.numerator_val__ = abs(self.numerator_val__) + self.denominator_val__ = abs(self.denominator_val__) + elif self.denominator_val__ < 0: + self.numerator_val__ = self.numerator_val__ * (-1) + self.denominator_val__ = abs(self.denominator_val__) + + def __init__(self, arg_1: str | int, arg_2: int | None = None) -> None: + """Создает рациональную дробь. + + Args: + arg_1 (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg_2 (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg_1, str): + numerator, denominator = map(int, arg_1.split("/")) + + self.numerator_val__ = numerator + self.denominator_val__ = denominator + + self._readuction() + + return + + if isinstance(arg_1, int) and isinstance(arg_2, int): + numerator, denominator = arg_1, arg_2 + + self.numerator_val__ = numerator + self.denominator_val__ = denominator + + self._readuction() + + return + + raise TypeError("Invalid args type") + + def numerator(self, number_arg: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number_arg (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number_arg is None: + return abs(self.numerator_val__) + + self.numerator_val__ = number_arg + + self._readuction() + + return self.numerator_val__ + + def denominator(self, number: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number is None: + return abs(self.denominator_val__) + + self.denominator_val__ = number + + self._readuction() + + return self.denominator_val__ + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction = f"{self.numerator_val__}/{self.denominator_val__}" + + return fraction + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction = f"{self.numerator_val__}/{self.denominator_val__}" + + return f"Fraction('{fraction}')" + + def __neg__(self) -> "FractionG": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + FractionG: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * (-1) + + return FractionG(numerator, self.denominator_val__) + + def __add__(self, other: "FractionG") -> "FractionG": + """Складывает дроби при использовании бинарного +. + + Args: + other (FractionG): Прибавляемая дробь. + + Returns: + FractionG: Новая дробь Fraction. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + numerator = numerator1 + numerator2 + denominator = self.denominator_val__ * other.denominator_val__ + + return FractionG(numerator, denominator) + + def __sub__(self, other: "FractionG") -> "FractionG": + """Вычитает дроби при использовании бинарного -. + + Args: + other (FractionG): Вычитаемая дробь. + + Returns: + FractionG: Новая дробь Fraction. + """ + return self + (-other) + + def __iadd__(self, other: "FractionG") -> "FractionG": + """Складывает дроби при использовании +=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionG): Прибавляемая дробь. + + Returns: + FractionG: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + self.numerator_val__ = numerator1 + numerator2 + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __isub__(self, other: "FractionG") -> "FractionG": + """Вычитает дроби при использовании -=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionG): Вычитаемая дробь. + + Returns: + FractionG: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + self.numerator_val__ = numerator1 - numerator2 + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __mul__(self, other: "FractionG") -> "FractionG": + """Умножает дроби при использовании *. + + Args: + other (FractionG): Второй множитель. + + Returns: + FractionG: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * other.numerator_val__ + denominator = self.denominator_val__ * other.denominator_val__ + + return FractionG(numerator, denominator) + + def __truediv__(self, other: "FractionG") -> "FractionG": + """Вычисляет частное от деления дробей при использовании /. + + Args: + other (FractionG): Делитель. + + Returns: + FractionG: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * other.denominator_val__ + denominator = self.denominator_val__ * other.numerator_val__ + + return FractionG(numerator, denominator) + + def __imul__(self, other: "FractionG") -> "FractionG": + """Умножает дроби при использовании *=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionG): Второй множитель. + + Returns: + FractionG: Измененная исходная дробь, переданная слева. + """ + self.numerator_val__ = self.numerator_val__ * other.numerator_val__ + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __itruediv__(self, other: "FractionG") -> "FractionG": + """Вычисляет частное при делении дробей с использованием /=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionG): Делитель. + + Returns: + FractionG: Измененная исходная дробь, переданная слева. + """ + self.numerator_val__ = self.numerator_val__ * other.denominator_val__ + self.denominator_val__ = self.denominator_val__ * other.numerator_val__ + + self._readuction() + + return self + + def reverse(self) -> "FractionG": + """Возвращает дробь, обратную текущей.""" + return FractionG(self.denominator_val__, self.numerator_val__) + + +# H + + +# %% +class FractionH: + """Рациональная дробь. + + Attributes: + numerator_val__ (int): Числитель. + denominator_val__ (int): Знаменатель. + """ + + numerator_val__: int + denominator_val__: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod = gcd(self.numerator_val__, self.denominator_val__) + + self.numerator_val__ = int(self.numerator_val__ / nod) + self.denominator_val__ = int(self.denominator_val__ / nod) + + if self.denominator_val__ < 0 and self.numerator_val__ < 0: + self.numerator_val__ = abs(self.numerator_val__) + self.denominator_val__ = abs(self.denominator_val__) + elif self.denominator_val__ < 0: + self.numerator_val__ = self.numerator_val__ * (-1) + self.denominator_val__ = abs(self.denominator_val__) + + def __init__(self, arg1: str | int, arg2: int | None = None) -> None: + """Создает рациональную дробь. + + Args: + arg1 (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg2 (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1, str): + numerator_, denominator_ = map(int, arg1.split("/")) + + elif isinstance(arg1, int) and isinstance(arg2, int): + numerator_, denominator_ = arg1, arg2 + else: + raise TypeError("Invalid args type") + + self.numerator_val__ = numerator_ + self.denominator_val__ = denominator_ + + self._readuction() + + def numerator(self, number: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number is None: + return abs(self.numerator_val__) + + self.numerator_val__ = number + + self._readuction() + + return self.numerator_val__ + + def denominator(self, number: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number is None: + return abs(self.denominator_val__) + + self.denominator_val__ = number + + self._readuction() + + return self.denominator_val__ + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction = f"{self.numerator_val__}/{self.denominator_val__}" + + return fraction + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction = f"{self.numerator_val__}/{self.denominator_val__}" + + return f"Fraction('{fraction}')" + + def __neg__(self) -> "FractionH": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + FractionH: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * (-1) + + return FractionH(numerator, self.denominator_val__) + + def __add__(self, other: "FractionH") -> "FractionH": + """Складывает дроби при использовании бинарного +. + + Args: + other (FractionH): Прибавляемая дробь. + + Returns: + FractionH: Новая дробь Fraction. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + numerator = numerator1 + numerator2 + denominator = self.denominator_val__ * other.denominator_val__ + + return FractionH(numerator, denominator) + + def __sub__(self, other: "FractionH") -> "FractionH": + """Вычитает дроби при использовании бинарного -. + + Args: + other (FractionH): Вычитаемая дробь. + + Returns: + FractionH: Новая дробь Fraction. + """ + return self + (-other) + + def __iadd__(self, other: "FractionH") -> "FractionH": + """Складывает дроби при использовании +=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionH): Прибавляемая дробь. + + Returns: + FractionH: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + self.numerator_val__ = numerator1 + numerator2 + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __isub__(self, other: "FractionH") -> "FractionH": + """Вычитает дроби при использовании -=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionH): Вычитаемая дробь. + + Returns: + FractionH: Измененная исходная дробь, переданная слева. + """ + numerator1 = self.numerator_val__ * other.denominator_val__ + numerator2 = other.numerator_val__ * self.denominator_val__ + self.numerator_val__ = numerator1 - numerator2 + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __mul__(self, other: "FractionH") -> "FractionH": + """Умножает дроби при использовании *. + + Args: + other (FractionH): Второй множитель. + + Returns: + FractionH: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * other.numerator_val__ + denominator = self.denominator_val__ * other.denominator_val__ + + return FractionH(numerator, denominator) + + def __truediv__(self, other: "FractionH") -> "FractionH": + """Вычисляет частное от деления дробей при использовании /. + + Args: + other (FractionH): Делитель. + + Returns: + FractionH: Новая дробь Fraction. + """ + numerator = self.numerator_val__ * other.denominator_val__ + denominator = self.denominator_val__ * other.numerator_val__ + + return FractionH(numerator, denominator) + + def __imul__(self, other: "FractionH") -> "FractionH": + """Умножает дроби при использовании *=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionH): Второй множитель. + + Returns: + FractionH: Измененная исходная дробь, переданная слева. + """ + self.numerator_val__ = self.numerator_val__ * other.numerator_val__ + self.denominator_val__ = ( + self.denominator_val__ * other.denominator_val__ + ) + + self._readuction() + + return self + + def __itruediv__(self, other: "FractionH") -> "FractionH": + """Вычисляет частное при делении дробей с использованием /=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionH): Делитель. + + Returns: + FractionH: Измененная исходная дробь, переданная слева. + """ + self.numerator_val__ = self.numerator_val__ * other.denominator_val__ + self.denominator_val__ = self.denominator_val__ * other.numerator_val__ + + self._readuction() + + return self + + def reverse(self) -> "FractionH": + """Возвращает дробь, обратную текущей.""" + return FractionH(self.denominator_val__, self.numerator_val__) + + def __eq__(self, other: object) -> bool: + """Проверяет равенство дроби переданному аргументу. + + Args: + other (object): Объект, с которым сравнивается дробь. + + Returns: + bool: Возращает True, если переданный аргумент равен текущей дроби. + Иначе возвращает False. + """ + if isinstance(other, FractionH): + is_numerator_equal = self.numerator_val__ == other.numerator_val__ + is_denominator_equal = ( + self.denominator_val__ == other.denominator_val__ + ) + + return is_numerator_equal and is_denominator_equal + + return False + + def __lt__(self, other: "FractionH") -> bool: + """Определяет, меньше ли текущая дробь другой дроби. + + Args: + other (FractionH): Другая дробь для сравнения. + + Returns: + bool: True, если текущая дробь строго меньше other. Иначе False. + """ + mul1 = self.numerator_val__ * other.denominator_val__ + mul2 = other.numerator_val__ * self.denominator_val__ + + return mul1 < mul2 + + def __le__(self, other: "FractionH") -> bool: + """Определяет, меньше ли текущая дробь или равна другой дроби. + + Args: + other (FractionH): Другая дробь для сравнения. + + Returns: + bool: True, если текущая дробь меньше или равна other. Иначе False. + """ + return self < other or self == other + + def __gt__(self, other: "FractionH") -> bool: + """Определяет, больше ли текущая дробь другой дроби. + + Args: + other (FractionH): Другая дробь для сравнения. + + Returns: + bool: True, если текущая дробь строго больше other. Иначе False. + """ + return not self < other and not self == other + + def __ge__(self, other: "FractionH") -> bool: + """Определяет, больше ли текущая дробь или равна другой дроби. + + Args: + other (FractionH): Другая дробь для сравнения. + + Returns: + bool: True, если текущая дробь больше или равна other. Иначе False. + """ + return self > other or self == other + + def __ne__(self, other: object) -> bool: + """Определяет, не равны ли два объекта. + + Args: + other (object): Объект для сравнения. + + Returns: + bool: True, если объекты не равны. Иначе False. + """ + return not self == other diff --git a/Python/yandex-python-handbook/chapter_5_2_part_3.ipynb b/Python/yandex-python-handbook/chapter_5_2_part_3.ipynb new file mode 100644 index 00000000..20522538 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_3.ipynb @@ -0,0 +1,958 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 5.2.\n", + "\n", + "Волшебные методы, переопределение методов. Наследование.\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\n", + "Из-за ограничения на количество строк в файле разбит на несколько частей.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "from math import gcd\n", + "from typing import Union\n", + "\n", + "\n", + "class FractionI:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val (int): Числитель.\n", + " denominator_val (int): Знаменатель.\n", + " \"\"\"\n", + "\n", + " _OtherArg = Union[\"FractionI\", str, int]\n", + "\n", + " numerator_val: int\n", + " denominator_val: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod_ = gcd(self.numerator_val, self.denominator_val)\n", + "\n", + " self.numerator_val = int(self.numerator_val / nod_)\n", + " self.denominator_val = int(self.denominator_val / nod_)\n", + "\n", + " if self.denominator_val < 0 and self.numerator_val < 0:\n", + " self.numerator_val = abs(self.numerator_val)\n", + " self.denominator_val = abs(self.denominator_val)\n", + " elif self.denominator_val < 0:\n", + " self.numerator_val = self.numerator_val * (-1)\n", + " self.denominator_val = abs(self.denominator_val)\n", + "\n", + " def __init__(self, arg1_: str | int, arg2_: int = 1) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1_ (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg2_ (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1_, str) and \"/\" in arg1_:\n", + " numerator_, denominator_ = map(int, arg1_.split(\"/\"))\n", + "\n", + " # elif isinstance(arg1_, str):\n", + " # numerator_, denominator_ = map(int, arg1_.split(\"/\"))\n", + "\n", + " # elif isinstance(arg1_, int) and isinstance(arg2_, int):\n", + " # numerator_, denominator_ = arg1_, arg2_\n", + " else:\n", + " numerator_, denominator_ = int(arg1_), int(arg2_)\n", + "\n", + " self.numerator_val = numerator_\n", + " self.denominator_val = denominator_\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number_: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number_ (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number_ is None:\n", + " return abs(self.numerator_val)\n", + "\n", + " self.numerator_val = number_\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val\n", + "\n", + " def denominator(self, number_: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number_ (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number_ is None:\n", + " return abs(self.denominator_val)\n", + "\n", + " self.denominator_val = number_\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction_ = f\"{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return fraction_\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction_ = f\"{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return f\"FractionI('{fraction_}')\"\n", + "\n", + " def __neg__(self) -> \"FractionI\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " numerator = self.numerator_val * (-1)\n", + "\n", + " return FractionI(numerator, self.denominator_val)\n", + "\n", + " def __add__(self, other: _OtherArg) -> \"FractionI\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного +.\n", + "\n", + " Args:\n", + " other (FractionI): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " numerator = numerator1 + numerator2\n", + " denominator = self.denominator_val * other.denominator_val\n", + "\n", + " return FractionI(numerator, denominator)\n", + "\n", + " def __iadd__(self, other: _OtherArg) -> \"FractionI\":\n", + " \"\"\"\n", + " Складывает дроби при использовании +=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionI): Прибавляемая дробь.\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " self.numerator_val = numerator1 + numerator2\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __sub__(self, other: _OtherArg) -> \"FractionI\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного -.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " return self + (-other)\n", + "\n", + " def __isub__(self, other: _OtherArg) -> \"FractionI\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании -=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionI): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " self.numerator_val = numerator1 - numerator2\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __mul__(self, other: \"FractionI\") -> \"FractionI\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *.\n", + "\n", + " Args:\n", + " other (FractionI): Второй множитель.\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " numerator = self.numerator_val * other.numerator_val\n", + " denominator = self.denominator_val * other.denominator_val\n", + "\n", + " return FractionI(numerator, denominator)\n", + "\n", + " def __truediv__(self, other: \"FractionI\") -> \"FractionI\":\n", + " \"\"\"\n", + " Вычисляет частное от деления дробей при использовании /.\n", + "\n", + " Args:\n", + " other (FractionI): Делитель.\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " numerator = self.numerator_val * other.denominator_val\n", + " denominator = self.denominator_val * other.numerator_val\n", + "\n", + " return FractionI(numerator, denominator)\n", + "\n", + " def __imul__(self, other: \"FractionI\") -> \"FractionI\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionI): Вычитаемая дробь.\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val = self.numerator_val * other.numerator_val\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __itruediv__(self, other: \"FractionI\") -> \"FractionI\":\n", + " \"\"\"\n", + " Вычисляет частное при делении дробей с использованием /=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (FractionI): Делитель.\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " self.numerator_val = self.numerator_val * other.denominator_val\n", + " self.denominator_val = self.denominator_val * other.numerator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def reverse(self) -> \"FractionI\":\n", + " \"\"\"Возвращает дробь, обратную текущей.\"\"\"\n", + " return FractionI(self.denominator_val, self.numerator_val)\n", + "\n", + " def __eq__(self, other: object) -> bool:\n", + " \"\"\"\n", + " Проверяет равенство дроби переданному аргументу.\n", + "\n", + " Args:\n", + " other (object): Объект, с которым сравнивается дробь.\n", + "\n", + " Returns:\n", + " bool: Возращает True, если переданный аргумент равен текущей дроби.\n", + " Иначе возвращает False.\n", + " \"\"\"\n", + " if isinstance(other, FractionI):\n", + " is_numerators_equal = self.numerator_val == other.numerator_val\n", + " is_denominators_equal = (\n", + " self.denominator_val == other.denominator_val\n", + " )\n", + "\n", + " return is_numerators_equal and is_denominators_equal\n", + "\n", + " return False\n", + "\n", + " def __lt__(self, other: _OtherArg) -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь другого аргумента.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго меньше other. Иначе False.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " mul1_ = self.numerator_val * other.denominator_val\n", + " mul2_ = other.numerator_val * self.denominator_val\n", + "\n", + " return mul1_ < mul2_\n", + "\n", + " def __le__(self, other_: \"FractionI\") -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь меньше или равна other_. Иначе False.\n", + " \"\"\"\n", + " return self < other_ or self == other_\n", + "\n", + " def __gt__(self, other_: \"FractionI\") -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго больше other_. Иначе False.\n", + " \"\"\"\n", + " return not self < other_ and not self == other_\n", + "\n", + " def __ge__(self, other_: \"FractionI\") -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь больше или равна other_. Иначе False.\n", + " \"\"\"\n", + " return self > other_ or self == other_\n", + "\n", + " def __ne__(self, other_: object) -> bool:\n", + " \"\"\"Определяет, не равны ли два объекта.\n", + "\n", + " Args:\n", + " other_ (object): Объект для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если объекты не равны. Иначе False.\n", + " \"\"\"\n", + " return not self == other_\n", + "\n", + " @staticmethod\n", + " def _convert_other_to_fraction(\n", + " arg: _OtherArg,\n", + " ) -> \"FractionI\":\n", + " \"\"\"\n", + " Конвертирует переданный аргумент в дробь.\n", + "\n", + " Args:\n", + " arg (_OtherArg): дробь, число или строка, представляющая дробь или\n", + " число.\n", + "\n", + " Returns:\n", + " FractionI: Дробь, экземпляр класса FractionI\n", + " \"\"\"\n", + " if isinstance(arg, FractionI):\n", + " return arg\n", + "\n", + " return FractionI(arg)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "class FractionJ:\n", + " \"\"\"\n", + " Рациональная дробь.\n", + "\n", + " Attributes:\n", + " numerator_val (int): Числитель.\n", + " denominator_val (int): Знаменатель.\n", + " \"\"\"\n", + "\n", + " _OtherArg = Union[\"FractionJ\", str, int]\n", + "\n", + " numerator_val: int\n", + " denominator_val: int\n", + "\n", + " def _readuction(self) -> None:\n", + " \"\"\"\n", + " Сокращает дробь.\n", + "\n", + " Вычисляет НОД числителя и знаменателя.\n", + " Если НОД не равен 1, уменьшает числитель и знаменатель в НОД раз.\n", + " \"\"\"\n", + " nod_ = gcd(self.numerator_val, self.denominator_val)\n", + "\n", + " self.numerator_val = int(self.numerator_val / nod_)\n", + " self.denominator_val = int(self.denominator_val / nod_)\n", + "\n", + " if self.denominator_val < 0 and self.numerator_val < 0:\n", + " self.numerator_val = abs(self.numerator_val)\n", + " self.denominator_val = abs(self.denominator_val)\n", + " elif self.denominator_val < 0:\n", + " self.numerator_val = self.numerator_val * (-1)\n", + " self.denominator_val = abs(self.denominator_val)\n", + "\n", + " def __init__(self, arg1_: str | int, arg2_: int = 1) -> None:\n", + " \"\"\"\n", + " Создает рациональную дробь.\n", + "\n", + " Args:\n", + " arg1_ (str | int): Строка, в которой значения числителя и\n", + " знаменателя разделены символом /, или целое число -\n", + " значение числителя.\n", + " arg2_ (int | None, optional): Значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Raises:\n", + " TypeError: Ошибка, которая будет проброшена в случае, если\n", + " аргументы имеют некорректный тип.\n", + " \"\"\"\n", + " if isinstance(arg1_, str) and \"/\" in arg1_:\n", + " numerator, denominator = map(int, arg1_.split(\"/\"))\n", + "\n", + " # elif isinstance(arg1_, str):\n", + " # numerator, denominator = map(int, arg1_.split(\"/\"))\n", + "\n", + " # elif isinstance(arg1_, int) and isinstance(arg2_, int):\n", + " # numerator, denominator = arg1_, arg2_\n", + " else:\n", + " numerator, denominator = int(arg1_), int(arg2_)\n", + "\n", + " self.numerator_val = numerator\n", + " self.denominator_val = denominator\n", + "\n", + " self._readuction()\n", + "\n", + " def numerator(self, number_: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает числитель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number_ (int | None, optional): Новое значение числителя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Числитель.\n", + " \"\"\"\n", + " if number_ is None:\n", + " return abs(self.numerator_val)\n", + "\n", + " self.numerator_val = number_\n", + "\n", + " self._readuction()\n", + "\n", + " return self.numerator_val\n", + "\n", + " def denominator(self, number_: int | None = None) -> int:\n", + " \"\"\"\n", + " Возвращает знаменатель.\n", + "\n", + " Если передано значение, то меняет числитель на новое значение,\n", + " при необходимости сокращая дробь.\n", + "\n", + " Args:\n", + " number_ (int | None, optional): Новое значение знаменателя.\n", + " По умолчанию None.\n", + "\n", + " Returns:\n", + " int: Знаменатель.\n", + " \"\"\"\n", + " if number_ is None:\n", + " return abs(self.denominator_val)\n", + "\n", + " self.denominator_val = number_\n", + "\n", + " self._readuction()\n", + "\n", + " return self.denominator_val\n", + "\n", + " def __str__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление дроби.\n", + "\n", + " Returns:\n", + " str: Строковое представление дроби.\n", + " \"\"\"\n", + " fraction_ = f\"{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return fraction_\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает репрезентативное представление дроби.\n", + "\n", + " Returns:\n", + " str: Репрезентативное представление дроби.\n", + " \"\"\"\n", + " fraction_ = f\"{self.numerator_val}/{self.denominator_val}\"\n", + "\n", + " return f\"FractionJ('{fraction_}')\"\n", + "\n", + " def __neg__(self) -> \"FractionJ\":\n", + " \"\"\"\n", + " Создает новую дробь при использовании унарного минуса -.\n", + "\n", + " Новая дробь имеет обратный знак.\n", + "\n", + " Returns:\n", + " FractionJ: Новая дробь FractionJ.\n", + " \"\"\"\n", + " numerator = self.numerator_val * (-1)\n", + "\n", + " return FractionJ(numerator, self.denominator_val)\n", + "\n", + " def __add__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного +.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " numerator = numerator1 + numerator2\n", + " denominator = self.denominator_val * other.denominator_val\n", + "\n", + " return FractionJ(numerator, denominator)\n", + "\n", + " def __radd__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Складывает дроби при использовании бинарного + (справа).\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " return self + other\n", + "\n", + " def __iadd__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Складывает дроби при использовании +=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " self.numerator_val = numerator1 + numerator2\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __sub__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного -.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " return self + (-other)\n", + "\n", + " def __rsub__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании бинарного - (справа).\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Новая дробь FractionI.\n", + " \"\"\"\n", + " return -self + other\n", + "\n", + " def __isub__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычитает дроби при использовании -=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionI: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator1 = self.numerator_val * other.denominator_val\n", + " numerator2 = other.numerator_val * self.denominator_val\n", + " self.numerator_val = numerator1 - numerator2\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __mul__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Новая дробь FractionJ.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator = self.numerator_val * other.numerator_val\n", + " denominator = self.denominator_val * other.denominator_val\n", + "\n", + " return FractionJ(numerator, denominator)\n", + "\n", + " def __rmul__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Умножает дроби при использовании * (справа).\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Новая дробь FractionJ.\n", + " \"\"\"\n", + " return self * other\n", + "\n", + " def __imul__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Умножает дроби при использовании *=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " self.numerator_val = self.numerator_val * other.numerator_val\n", + " self.denominator_val = self.denominator_val * other.denominator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def __truediv__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычисляет частное от деления дробей при использовании /.\n", + "\n", + " Args:\n", + " other (_OtherArg): Делитель - другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Новая дробь FractionJ.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " numerator = self.numerator_val * other.denominator_val\n", + " denominator = self.denominator_val * other.numerator_val\n", + "\n", + " return FractionJ(numerator, denominator)\n", + "\n", + " def __rtruediv__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычисляет частное от деления дробей при использовании / (справа).\n", + "\n", + " Args:\n", + " other (_OtherArg): Делимое - другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Новая дробь FractionJ.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " return self.reverse() * other\n", + "\n", + " def __itruediv__(self, other: _OtherArg) -> \"FractionJ\":\n", + " \"\"\"\n", + " Вычисляет частное при делении дробей с использованием /=.\n", + "\n", + " Изменяет исходную дробь, переданную слева.\n", + "\n", + " Args:\n", + " other (_OtherArg): Делитель - другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " FractionJ: Измененная исходная дробь, переданная слева.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " self.numerator_val = self.numerator_val * other.denominator_val\n", + " self.denominator_val = self.denominator_val * other.numerator_val\n", + "\n", + " self._readuction()\n", + "\n", + " return self\n", + "\n", + " def reverse(self) -> \"FractionJ\":\n", + " \"\"\"Возвращает дробь, обратную текущей.\"\"\"\n", + " return FractionJ(self.denominator_val, self.numerator_val)\n", + "\n", + " def __eq__(self, other: object) -> bool:\n", + " \"\"\"\n", + " Проверяет равенство дроби переданному аргументу.\n", + "\n", + " Args:\n", + " other (object): Объект, с которым сравнивается дробь.\n", + "\n", + " Returns:\n", + " bool: Возращает True, если переданный аргумент равен текущей дроби.\n", + " Иначе возвращает False.\n", + " \"\"\"\n", + " if isinstance(other, FractionJ):\n", + " is_numerators_equal = self.numerator_val == other.numerator_val\n", + " is_denominators_equal = (\n", + " self.denominator_val == other.denominator_val\n", + " )\n", + "\n", + " return is_numerators_equal and is_denominators_equal\n", + "\n", + " return False\n", + "\n", + " def __lt__(self, other: _OtherArg) -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь другого аргумента.\n", + "\n", + " Args:\n", + " other (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго меньше other. Иначе False.\n", + " \"\"\"\n", + " other = self._convert_other_to_fraction(other)\n", + "\n", + " mul1_ = self.numerator_val * other.denominator_val\n", + " mul2_ = other.numerator_val * self.denominator_val\n", + "\n", + " return mul1_ < mul2_\n", + "\n", + " def __le__(self, other_: _OtherArg) -> bool:\n", + " \"\"\"Определяет, меньше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь меньше или равна other_. Иначе False.\n", + " \"\"\"\n", + " other_ = self._convert_other_to_fraction(other_)\n", + "\n", + " return self < other_ or self == other_\n", + "\n", + " def __gt__(self, other_: _OtherArg) -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь строго больше other_. Иначе False.\n", + " \"\"\"\n", + " other_ = self._convert_other_to_fraction(other_)\n", + "\n", + " return not (self < other_ or self == other_)\n", + "\n", + " def __ge__(self, other_: _OtherArg) -> bool:\n", + " \"\"\"Определяет, больше ли текущая дробь или равна другой дроби.\n", + "\n", + " Args:\n", + " other_ (_OtherArg): Другой аргумент\n", + " (дробь, число или строка, представляющая дробь или число).\n", + "\n", + " Returns:\n", + " bool: True, если текущая дробь больше или равна other_. Иначе False.\n", + " \"\"\"\n", + " other_ = self._convert_other_to_fraction(other_)\n", + "\n", + " return self > other_ or self == other_\n", + "\n", + " def __ne__(self, other_: object) -> bool:\n", + " \"\"\"Определяет, не равны ли два объекта.\n", + "\n", + " Args:\n", + " other_ (object): Объект для сравнения.\n", + "\n", + " Returns:\n", + " bool: True, если объекты не равны. Иначе False.\n", + " \"\"\"\n", + " return not self == other_\n", + "\n", + " @staticmethod\n", + " def _convert_other_to_fraction(\n", + " other: _OtherArg,\n", + " ) -> \"FractionJ\":\n", + " \"\"\"\n", + " Конвертирует переданный аргумент в дробь.\n", + "\n", + " Args:\n", + " arg (_OtherArg): дробь, число или строка, представляющая дробь или\n", + " число.\n", + "\n", + " Returns:\n", + " FractionI: Дробь, экземпляр класса FractionI\n", + " \"\"\"\n", + " if isinstance(other, FractionJ):\n", + " return other\n", + "\n", + " return FractionJ(other)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_5_2_part_3.py b/Python/yandex-python-handbook/chapter_5_2_part_3.py new file mode 100644 index 00000000..866e1209 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_2_part_3.py @@ -0,0 +1,861 @@ +# %% +"""Задания к главе 5.2. + +Волшебные методы, переопределение методов. Наследование. + +Хендбук Яндекс "Основы Python". + +Из-за ограничения на количество строк в файле разбит на несколько частей. +""" + +# I + +# %% +from math import gcd +from typing import Union + + +class FractionI: + """Рациональная дробь. + + Attributes: + numerator_val (int): Числитель. + denominator_val (int): Знаменатель. + """ + + _OtherArg = Union["FractionI", str, int] + + numerator_val: int + denominator_val: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod_ = gcd(self.numerator_val, self.denominator_val) + + self.numerator_val = int(self.numerator_val / nod_) + self.denominator_val = int(self.denominator_val / nod_) + + if self.denominator_val < 0 and self.numerator_val < 0: + self.numerator_val = abs(self.numerator_val) + self.denominator_val = abs(self.denominator_val) + elif self.denominator_val < 0: + self.numerator_val = self.numerator_val * (-1) + self.denominator_val = abs(self.denominator_val) + + def __init__(self, arg1_: str | int, arg2_: int = 1) -> None: + """Создает рациональную дробь. + + Args: + arg1_ (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg2_ (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1_, str) and "/" in arg1_: + numerator_, denominator_ = map(int, arg1_.split("/")) + + # elif isinstance(arg1_, str): + # numerator_, denominator_ = map(int, arg1_.split("/")) + + # elif isinstance(arg1_, int) and isinstance(arg2_, int): + # numerator_, denominator_ = arg1_, arg2_ + else: + numerator_, denominator_ = int(arg1_), int(arg2_) + + self.numerator_val = numerator_ + self.denominator_val = denominator_ + + self._readuction() + + def numerator(self, number_: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number_ (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number_ is None: + return abs(self.numerator_val) + + self.numerator_val = number_ + + self._readuction() + + return self.numerator_val + + def denominator(self, number_: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number_ (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number_ is None: + return abs(self.denominator_val) + + self.denominator_val = number_ + + self._readuction() + + return self.denominator_val + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction_ = f"{self.numerator_val}/{self.denominator_val}" + + return fraction_ + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction_ = f"{self.numerator_val}/{self.denominator_val}" + + return f"FractionI('{fraction_}')" + + def __neg__(self) -> "FractionI": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + FractionI: Новая дробь FractionI. + """ + numerator = self.numerator_val * (-1) + + return FractionI(numerator, self.denominator_val) + + def __add__(self, other: _OtherArg) -> "FractionI": + """Складывает дроби при использовании бинарного +. + + Args: + other (FractionI): Прибавляемая дробь. + + Returns: + FractionI: Новая дробь FractionI. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + numerator = numerator1 + numerator2 + denominator = self.denominator_val * other.denominator_val + + return FractionI(numerator, denominator) + + def __iadd__(self, other: _OtherArg) -> "FractionI": + """Складывает дроби при использовании +=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionI): Прибавляемая дробь. + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + self.numerator_val = numerator1 + numerator2 + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __sub__(self, other: _OtherArg) -> "FractionI": + """Вычитает дроби при использовании бинарного -. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Новая дробь FractionI. + """ + other = self._convert_other_to_fraction(other) + + return self + (-other) + + def __isub__(self, other: _OtherArg) -> "FractionI": + """Вычитает дроби при использовании -=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionI): Вычитаемая дробь. + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + self.numerator_val = numerator1 - numerator2 + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __mul__(self, other: "FractionI") -> "FractionI": + """Умножает дроби при использовании *. + + Args: + other (FractionI): Второй множитель. + + Returns: + FractionI: Новая дробь FractionI. + """ + numerator = self.numerator_val * other.numerator_val + denominator = self.denominator_val * other.denominator_val + + return FractionI(numerator, denominator) + + def __truediv__(self, other: "FractionI") -> "FractionI": + """Вычисляет частное от деления дробей при использовании /. + + Args: + other (FractionI): Делитель. + + Returns: + FractionI: Новая дробь FractionI. + """ + numerator = self.numerator_val * other.denominator_val + denominator = self.denominator_val * other.numerator_val + + return FractionI(numerator, denominator) + + def __imul__(self, other: "FractionI") -> "FractionI": + """Умножает дроби при использовании *=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionI): Вычитаемая дробь. + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + self.numerator_val = self.numerator_val * other.numerator_val + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __itruediv__(self, other: "FractionI") -> "FractionI": + """Вычисляет частное при делении дробей с использованием /=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (FractionI): Делитель. + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + self.numerator_val = self.numerator_val * other.denominator_val + self.denominator_val = self.denominator_val * other.numerator_val + + self._readuction() + + return self + + def reverse(self) -> "FractionI": + """Возвращает дробь, обратную текущей.""" + return FractionI(self.denominator_val, self.numerator_val) + + def __eq__(self, other: object) -> bool: + """Проверяет равенство дроби переданному аргументу. + + Args: + other (object): Объект, с которым сравнивается дробь. + + Returns: + bool: Возращает True, если переданный аргумент равен текущей дроби. + Иначе возвращает False. + """ + if isinstance(other, FractionI): + is_numerators_equal = self.numerator_val == other.numerator_val + is_denominators_equal = ( + self.denominator_val == other.denominator_val + ) + + return is_numerators_equal and is_denominators_equal + + return False + + def __lt__(self, other: _OtherArg) -> bool: + """Определяет, меньше ли текущая дробь другого аргумента. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь строго меньше other. Иначе False. + """ + other = self._convert_other_to_fraction(other) + + mul1_ = self.numerator_val * other.denominator_val + mul2_ = other.numerator_val * self.denominator_val + + return mul1_ < mul2_ + + def __le__(self, other_: "FractionI") -> bool: + """Определяет, меньше ли текущая дробь или равна другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь меньше или равна other_. Иначе False. + """ + return self < other_ or self == other_ + + def __gt__(self, other_: "FractionI") -> bool: + """Определяет, больше ли текущая дробь другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь строго больше other_. Иначе False. + """ + return not self < other_ and not self == other_ + + def __ge__(self, other_: "FractionI") -> bool: + """Определяет, больше ли текущая дробь или равна другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь больше или равна other_. Иначе False. + """ + return self > other_ or self == other_ + + def __ne__(self, other_: object) -> bool: + """Определяет, не равны ли два объекта. + + Args: + other_ (object): Объект для сравнения. + + Returns: + bool: True, если объекты не равны. Иначе False. + """ + return not self == other_ + + @staticmethod + def _convert_other_to_fraction( + arg: _OtherArg, + ) -> "FractionI": + """Конвертирует переданный аргумент в дробь. + + Args: + arg (_OtherArg): дробь, число или строка, представляющая дробь или + число. + + Returns: + FractionI: Дробь, экземпляр класса FractionI + """ + if isinstance(arg, FractionI): + return arg + + return FractionI(arg) + + +# J + + +# %% +class FractionJ: + """Рациональная дробь. + + Attributes: + numerator_val (int): Числитель. + denominator_val (int): Знаменатель. + """ + + _OtherArg = Union["FractionJ", str, int] + + numerator_val: int + denominator_val: int + + def _readuction(self) -> None: + """Сокращает дробь. + + Вычисляет НОД числителя и знаменателя. Если НОД не равен 1, уменьшает числитель и + знаменатель в НОД раз. + """ + nod_ = gcd(self.numerator_val, self.denominator_val) + + self.numerator_val = int(self.numerator_val / nod_) + self.denominator_val = int(self.denominator_val / nod_) + + if self.denominator_val < 0 and self.numerator_val < 0: + self.numerator_val = abs(self.numerator_val) + self.denominator_val = abs(self.denominator_val) + elif self.denominator_val < 0: + self.numerator_val = self.numerator_val * (-1) + self.denominator_val = abs(self.denominator_val) + + def __init__(self, arg1_: str | int, arg2_: int = 1) -> None: + """Создает рациональную дробь. + + Args: + arg1_ (str | int): Строка, в которой значения числителя и + знаменателя разделены символом /, или целое число - + значение числителя. + arg2_ (int | None, optional): Значение знаменателя. + По умолчанию None. + + Raises: + TypeError: Ошибка, которая будет проброшена в случае, если + аргументы имеют некорректный тип. + """ + if isinstance(arg1_, str) and "/" in arg1_: + numerator, denominator = map(int, arg1_.split("/")) + + # elif isinstance(arg1_, str): + # numerator, denominator = map(int, arg1_.split("/")) + + # elif isinstance(arg1_, int) and isinstance(arg2_, int): + # numerator, denominator = arg1_, arg2_ + else: + numerator, denominator = int(arg1_), int(arg2_) + + self.numerator_val = numerator + self.denominator_val = denominator + + self._readuction() + + def numerator(self, number_: int | None = None) -> int: + """Возвращает числитель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number_ (int | None, optional): Новое значение числителя. + По умолчанию None. + + Returns: + int: Числитель. + """ + if number_ is None: + return abs(self.numerator_val) + + self.numerator_val = number_ + + self._readuction() + + return self.numerator_val + + def denominator(self, number_: int | None = None) -> int: + """Возвращает знаменатель. + + Если передано значение, то меняет числитель на новое значение, + при необходимости сокращая дробь. + + Args: + number_ (int | None, optional): Новое значение знаменателя. + По умолчанию None. + + Returns: + int: Знаменатель. + """ + if number_ is None: + return abs(self.denominator_val) + + self.denominator_val = number_ + + self._readuction() + + return self.denominator_val + + def __str__(self) -> str: + """Возвращает строковое представление дроби. + + Returns: + str: Строковое представление дроби. + """ + fraction_ = f"{self.numerator_val}/{self.denominator_val}" + + return fraction_ + + def __repr__(self) -> str: + """Возвращает репрезентативное представление дроби. + + Returns: + str: Репрезентативное представление дроби. + """ + fraction_ = f"{self.numerator_val}/{self.denominator_val}" + + return f"FractionJ('{fraction_}')" + + def __neg__(self) -> "FractionJ": + """Создает новую дробь при использовании унарного минуса -. + + Новая дробь имеет обратный знак. + + Returns: + FractionJ: Новая дробь FractionJ. + """ + numerator = self.numerator_val * (-1) + + return FractionJ(numerator, self.denominator_val) + + def __add__(self, other: _OtherArg) -> "FractionJ": + """Складывает дроби при использовании бинарного +. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Новая дробь FractionI. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + numerator = numerator1 + numerator2 + denominator = self.denominator_val * other.denominator_val + + return FractionJ(numerator, denominator) + + def __radd__(self, other: _OtherArg) -> "FractionJ": + """Складывает дроби при использовании бинарного + (справа). + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Новая дробь FractionI. + """ + other = self._convert_other_to_fraction(other) + + return self + other + + def __iadd__(self, other: _OtherArg) -> "FractionJ": + """Складывает дроби при использовании +=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + self.numerator_val = numerator1 + numerator2 + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __sub__(self, other: _OtherArg) -> "FractionJ": + """Вычитает дроби при использовании бинарного -. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Новая дробь FractionI. + """ + other = self._convert_other_to_fraction(other) + + return self + (-other) + + def __rsub__(self, other: _OtherArg) -> "FractionJ": + """ + Вычитает дроби при использовании бинарного - (справа). + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Новая дробь FractionI. + """ + return -self + other + + def __isub__(self, other: _OtherArg) -> "FractionJ": + """Вычитает дроби при использовании -=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionI: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + numerator1 = self.numerator_val * other.denominator_val + numerator2 = other.numerator_val * self.denominator_val + self.numerator_val = numerator1 - numerator2 + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __mul__(self, other: _OtherArg) -> "FractionJ": + """Умножает дроби при использовании *. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Новая дробь FractionJ. + """ + other = self._convert_other_to_fraction(other) + + numerator = self.numerator_val * other.numerator_val + denominator = self.denominator_val * other.denominator_val + + return FractionJ(numerator, denominator) + + def __rmul__(self, other: _OtherArg) -> "FractionJ": + """Умножает дроби при использовании * (справа). + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Новая дробь FractionJ. + """ + return self * other + + def __imul__(self, other: _OtherArg) -> "FractionJ": + """Умножает дроби при использовании *=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + self.numerator_val = self.numerator_val * other.numerator_val + self.denominator_val = self.denominator_val * other.denominator_val + + self._readuction() + + return self + + def __truediv__(self, other: _OtherArg) -> "FractionJ": + """Вычисляет частное от деления дробей при использовании /. + + Args: + other (_OtherArg): Делитель - другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Новая дробь FractionJ. + """ + other = self._convert_other_to_fraction(other) + + numerator = self.numerator_val * other.denominator_val + denominator = self.denominator_val * other.numerator_val + + return FractionJ(numerator, denominator) + + def __rtruediv__(self, other: _OtherArg) -> "FractionJ": + """Вычисляет частное от деления дробей при использовании / (справа). + + Args: + other (_OtherArg): Делимое - другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Новая дробь FractionJ. + """ + other = self._convert_other_to_fraction(other) + + return self.reverse() * other + + def __itruediv__(self, other: _OtherArg) -> "FractionJ": + """Вычисляет частное при делении дробей с использованием /=. + + Изменяет исходную дробь, переданную слева. + + Args: + other (_OtherArg): Делитель - другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + FractionJ: Измененная исходная дробь, переданная слева. + """ + other = self._convert_other_to_fraction(other) + + self.numerator_val = self.numerator_val * other.denominator_val + self.denominator_val = self.denominator_val * other.numerator_val + + self._readuction() + + return self + + def reverse(self) -> "FractionJ": + """Возвращает дробь, обратную текущей.""" + return FractionJ(self.denominator_val, self.numerator_val) + + def __eq__(self, other: object) -> bool: + """Проверяет равенство дроби переданному аргументу. + + Args: + other (object): Объект, с которым сравнивается дробь. + + Returns: + bool: Возращает True, если переданный аргумент равен текущей дроби. + Иначе возвращает False. + """ + if isinstance(other, FractionJ): + is_numerators_equal = self.numerator_val == other.numerator_val + is_denominators_equal = ( + self.denominator_val == other.denominator_val + ) + + return is_numerators_equal and is_denominators_equal + + return False + + def __lt__(self, other: _OtherArg) -> bool: + """Определяет, меньше ли текущая дробь другого аргумента. + + Args: + other (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь строго меньше other. Иначе False. + """ + other = self._convert_other_to_fraction(other) + + mul1_ = self.numerator_val * other.denominator_val + mul2_ = other.numerator_val * self.denominator_val + + return mul1_ < mul2_ + + def __le__(self, other_: _OtherArg) -> bool: + """Определяет, меньше ли текущая дробь или равна другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь меньше или равна other_. Иначе False. + """ + other_ = self._convert_other_to_fraction(other_) + + return self < other_ or self == other_ + + def __gt__(self, other_: _OtherArg) -> bool: + """Определяет, больше ли текущая дробь другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь строго больше other_. Иначе False. + """ + other_ = self._convert_other_to_fraction(other_) + + return not (self < other_ or self == other_) + + def __ge__(self, other_: _OtherArg) -> bool: + """Определяет, больше ли текущая дробь или равна другой дроби. + + Args: + other_ (_OtherArg): Другой аргумент + (дробь, число или строка, представляющая дробь или число). + + Returns: + bool: True, если текущая дробь больше или равна other_. Иначе False. + """ + other_ = self._convert_other_to_fraction(other_) + + return self > other_ or self == other_ + + def __ne__(self, other_: object) -> bool: + """Определяет, не равны ли два объекта. + + Args: + other_ (object): Объект для сравнения. + + Returns: + bool: True, если объекты не равны. Иначе False. + """ + return not self == other_ + + @staticmethod + def _convert_other_to_fraction( + other: _OtherArg, + ) -> "FractionJ": + """Конвертирует переданный аргумент в дробь. + + Args: + arg (_OtherArg): дробь, число или строка, представляющая дробь или + число. + + Returns: + FractionI: Дробь, экземпляр класса FractionI + """ + if isinstance(other, FractionJ): + return other + + return FractionJ(other) diff --git a/Python/yandex-python-handbook/chapter_5_3.ipynb b/Python/yandex-python-handbook/chapter_5_3.ipynb new file mode 100644 index 00000000..f0be0ece --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_3.ipynb @@ -0,0 +1,488 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 5.3.\n", + "\n", + "Модель исключений Python. Try, except, else, finally. Модули\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "from hashlib import sha256\n", + "from re import fullmatch\n", + "from typing import Callable\n", + "\n", + "\n", + "def func(*args: object) -> None:\n", + " \"\"\"Функция-заглушка.\"\"\"\n", + " print(args)\n", + "\n", + "\n", + "try:\n", + " func()\n", + "except ValueError:\n", + " print(ValueError.__name__)\n", + "except TypeError:\n", + " print(TypeError.__name__)\n", + "except SystemError:\n", + " print(SystemError.__name__)\n", + "else:\n", + " print(\"No Exceptions\")" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "# Предложите вызов функции, который гарантированно породит ошибку внутри функции.\n", + "func(1, None)" + ] + }, + { + "cell_type": "markdown", + "id": "c44ac8ab", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8006a6c", + "metadata": {}, + "outputs": [], + "source": [ + "class EmptyClass:\n", + " \"\"\"Класс-заглушка.\n", + "\n", + " Генерирует ошибку при попытке приведения к строке\n", + " \"\"\"\n", + "\n", + " def __repr__(self) -> str:\n", + " \"\"\"\n", + " Возвращает строковое представление объекта.\n", + "\n", + " Raises:\n", + " TypeError: Всегда вызывается, чтобы сигнализировать, что метод\n", + " не реализован в текущем классе.\n", + " \"\"\"\n", + " raise TypeError()\n", + "\n", + "\n", + "obj = EmptyClass()\n", + "func(obj)" + ] + }, + { + "cell_type": "markdown", + "id": "bd2004d4", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1287244b", + "metadata": {}, + "outputs": [], + "source": [ + "def only_positive_even_sum(num1: int, num2: int) -> int:\n", + " \"\"\"Функция возвращает сумму двух переданных аргументов.\n", + "\n", + " Ожидает целые положительные четные числа.\n", + "\n", + " Args:\n", + " num1 (int): Целое число.\n", + " num2 (int): Целое число.\n", + "\n", + " Returns:\n", + " int: Сумма аргументов.\n", + "\n", + " Raises:\n", + " TypeError: Если один из параметров не является целым числом.\n", + " ValueError: Если один из параметров не является положительным\n", + " чётным числом.\n", + " \"\"\"\n", + " args = [num1, num2]\n", + "\n", + " if any(not isinstance(arg, int) for arg in args):\n", + " raise TypeError()\n", + "\n", + " if any(arg % 2 == 1 or arg <= 0 for arg in args):\n", + " raise ValueError()\n", + "\n", + " return num1 + num2" + ] + }, + { + "cell_type": "markdown", + "id": "a14327ea", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "246e3cd1", + "metadata": {}, + "outputs": [], + "source": [ + "def merge(tuple1: tuple[int, ...], tuple2: tuple[int, ...]) -> tuple[int, ...]:\n", + " \"\"\"Объединяет два отсортированных кортежа в один отсортированный.\n", + "\n", + " Args:\n", + " tuple1 (tuple[int, ...]): Первый кортеж отсортированных чисел.\n", + " tuple1 (tuple[int, ...]): Второй кортеж отсортированных чисел.\n", + "\n", + " Returns:\n", + " tuple[int, ...]: Отсортированный по возрастанию Кортеж целых чисел,\n", + " содержащий все числа из первого и второго кортежа\n", + "\n", + " Raises:\n", + " StopIteration: Если один из параметров не является итерируемым\n", + " объектом.\n", + " TypeError: Если значения входных параметров содержат «неоднородные»\n", + " данные.\n", + " ValueError: Если один из параметров не отсортирован.\n", + " \"\"\"\n", + " args = [tuple1, tuple2]\n", + " if any(not hasattr(arg, \"__iter__\") for arg in args):\n", + " raise StopIteration()\n", + "\n", + " if any(sorted(arg) != list(arg) for arg in args):\n", + " raise ValueError()\n", + "\n", + " list_1 = list(tuple1)\n", + " list2 = list(tuple2)\n", + "\n", + " result: list[int] = []\n", + "\n", + " while list_1 and list2:\n", + " if list_1[0] < list2[0]:\n", + " result.append(list_1.pop(0))\n", + " else:\n", + " result.append(list2.pop(0))\n", + "\n", + " result.extend(list_1)\n", + " result.extend(list2)\n", + "\n", + " expected_type = type(result[0])\n", + " if any(not isinstance(item, expected_type) for item in result):\n", + " raise TypeError()\n", + "\n", + " return tuple(result)" + ] + }, + { + "cell_type": "markdown", + "id": "2df858d8", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d71637b0", + "metadata": {}, + "outputs": [], + "source": [ + "class NoSolutionsError(Exception):\n", + " \"\"\"Ошибка отсутствия корней квадратного уравнения.\"\"\"\n", + "\n", + "\n", + "class InfiniteSolutionsError(Exception):\n", + " \"\"\"Ошибка: бесконечное количество корней квадратного уравнения.\"\"\"\n", + "\n", + "\n", + "def find_roots(\n", + " a_coef: float | int,\n", + " b_coef: float | int,\n", + " c_coef: float | int,\n", + ") -> tuple[float, ...]:\n", + " \"\"\"Функция для поиска корней квадратного уравнения.\n", + "\n", + " Принимает коэффициенты квадратного уравнения.\n", + "\n", + " Args:\n", + " a_coef (float | int): коэффициент a.\n", + " b_coef (float | int): коэффициент b.\n", + " c_coef (float | int): коэффициент c.\n", + "\n", + " Raises:\n", + " TypeError: Если переданные коэффициенты не являются рациональными\n", + " числами.\n", + " InfiniteSolutionsError: Бесконечное количества решений.\n", + " NoSolutionsError: Отсутствие решений.\n", + "\n", + " Returns:\n", + " tuple[float, ...]: Отсортированный кортеж корней квадратного уравнения.\n", + " \"\"\"\n", + " args = [a_coef, b_coef, c_coef]\n", + " if not all(type(arg) in (int, float) for arg in args):\n", + " raise TypeError()\n", + "\n", + " if a_coef == b_coef == c_coef == 0:\n", + " raise InfiniteSolutionsError()\n", + "\n", + " if a_coef == 0 and b_coef != 0 and c_coef != 0:\n", + " root = -c_coef / b_coef\n", + "\n", + " return (root, root)\n", + "\n", + " discriminant = b_coef**2 - 4 * a_coef * c_coef\n", + "\n", + " if discriminant < 0 or a_coef == b_coef == 0:\n", + " raise NoSolutionsError()\n", + "\n", + " if discriminant == 0:\n", + " root = -b_coef / (2 * a_coef)\n", + "\n", + " return (root, root)\n", + "\n", + " result1 = (-b_coef + discriminant**0.5) / (2 * a_coef)\n", + " result2 = (-b_coef - discriminant**0.5) / (2 * a_coef)\n", + "\n", + " min_result = min(result1, result2)\n", + " max_result = max(result1, result2)\n", + "\n", + " return (min_result, max_result)" + ] + }, + { + "cell_type": "markdown", + "id": "719a93df", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cca8ef95", + "metadata": {}, + "outputs": [], + "source": [ + "class CyrillicError(Exception):\n", + " \"\"\"Ошибка: если строка состоит не только из кириллицы.\"\"\"\n", + "\n", + "\n", + "class CapitalError(Exception):\n", + " \"\"\"Ошибка: заглавной буквы.\n", + "\n", + " Если значение не начинается с заглавной буквы или найдена заглавная буква\n", + " не в начале значения.\n", + " \"\"\"\n", + "\n", + "\n", + "def name_validation(value: str) -> str:\n", + " \"\"\"Функция валидации имени пользователя.\"\"\"\n", + " if not isinstance(value, str):\n", + " raise TypeError\n", + "\n", + " if not fullmatch(r\"[а-яА-ЯёЁ]+\", value):\n", + " raise CyrillicError\n", + "\n", + " if not value[0].isupper() or any(char.isupper() for char in value[1::]):\n", + " raise CapitalError\n", + "\n", + " return value" + ] + }, + { + "cell_type": "markdown", + "id": "3317df58", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb48aa02", + "metadata": {}, + "outputs": [], + "source": [ + "class BadCharacterError(Exception):\n", + " \"\"\"Ошибка: неправильный символ.\n", + "\n", + " Если значение состоит не только из латинских букв, цифр и символа нижнего\n", + " подчёркивания.\n", + " \"\"\"\n", + "\n", + "\n", + "class StartsWithDigitError(Exception):\n", + " \"\"\"Ошибка: если значение начинается с цифры.\"\"\"\n", + "\n", + "\n", + "def username_validation(value: str) -> str:\n", + " \"\"\"Функция валидации имени пользователя.\"\"\"\n", + " if not isinstance(value, str):\n", + " raise TypeError\n", + "\n", + " if not fullmatch(r\"[a-zA-Z0-9_]+$\", value):\n", + " raise BadCharacterError\n", + "\n", + " if value[0].isdigit():\n", + " raise StartsWithDigitError\n", + "\n", + " return value" + ] + }, + { + "cell_type": "markdown", + "id": "44a2f213", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "342d1f05", + "metadata": {}, + "outputs": [], + "source": [ + "valid_keys = [\"last_name\", \"first_name\", \"username\"]\n", + "\n", + "\n", + "def user_validation(**kwargs: str) -> object:\n", + " \"\"\"Функция валидации данных пользователя.\"\"\"\n", + " if sorted(valid_keys) != sorted(kwargs.keys()):\n", + " raise KeyError\n", + "\n", + " if not all(kwargs.values()):\n", + " raise KeyError\n", + "\n", + " name_validation(kwargs[\"last_name\"])\n", + " name_validation(kwargs[\"first_name\"])\n", + " username_validation(kwargs[\"username\"])\n", + "\n", + " return kwargs" + ] + }, + { + "cell_type": "markdown", + "id": "2574d9c7", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87e43170", + "metadata": {}, + "outputs": [], + "source": [ + "class MinLengthError(Exception):\n", + " \"\"\"Ошибка: если пароль меньше заданной длины.\"\"\"\n", + "\n", + "\n", + "class PossibleCharError(Exception):\n", + " \"\"\"Ошибка: в пароле используется недопустимый символ.\"\"\"\n", + "\n", + "\n", + "class NeedCharError(Exception):\n", + " \"\"\"Ошибка: в пароле не найдено ни одного обязательного символа.\"\"\"\n", + "\n", + "\n", + "default_valid_chars = \"abcdefzhiklmnopqrstvxABCDEFZHIKLMNOPQRSTVX0123456789\"\n", + "\n", + "\n", + "def password_validation(\n", + " password: str,\n", + " min_length: int = 8,\n", + " possible_chars: str = default_valid_chars,\n", + " at_least_one: Callable[[str], bool] = str.isdigit,\n", + ") -> str:\n", + " \"\"\"Функция валидации пароля пользователя.\"\"\"\n", + " if not isinstance(password, str):\n", + " raise TypeError\n", + "\n", + " if len(password) < min_length:\n", + " raise MinLengthError\n", + "\n", + " if any(char not in possible_chars for char in password):\n", + " raise PossibleCharError\n", + "\n", + " if all(not at_least_one(char) for char in password):\n", + " raise NeedCharError\n", + "\n", + " hash_object = sha256()\n", + " hash_object.update(password.encode())\n", + " hex_hash = hash_object.hexdigest()\n", + "\n", + " return hex_hash\n", + "\n", + "\n", + "print(password_validation(\"Hello12345\"))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_5_3.py b/Python/yandex-python-handbook/chapter_5_3.py new file mode 100644 index 00000000..b5039d91 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_5_3.py @@ -0,0 +1,345 @@ +"""Задания к главе 5.3. + +Модель исключений Python. Try, except, else, finally. Модули + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from hashlib import sha256 +from re import fullmatch +from typing import Callable + + +def func(*args: object) -> None: + """Функция-заглушка.""" + print(args) + + +try: + func() +except ValueError: + print(ValueError.__name__) +except TypeError: + print(TypeError.__name__) +except SystemError: + print(SystemError.__name__) +else: + print("No Exceptions") +# - + +# B + +# Предложите вызов функции, который гарантированно породит ошибку внутри функции. +func(1, None) + + +# C + + +# + +class EmptyClass: + """Класс-заглушка. + + Генерирует ошибку при попытке приведения к строке + """ + + def __repr__(self) -> str: + """Возвращает строковое представление объекта. + + Raises: + TypeError: Всегда вызывается, чтобы сигнализировать, что метод + не реализован в текущем классе. + """ + raise TypeError() + + +obj = EmptyClass() +func(obj) + + +# - + +# D + + +def only_positive_even_sum(num1: int, num2: int) -> int: + """Функция возвращает сумму двух переданных аргументов. + + Ожидает целые положительные четные числа. + + Args: + num1 (int): Целое число. + num2 (int): Целое число. + + Returns: + int: Сумма аргументов. + + Raises: + TypeError: Если один из параметров не является целым числом. + ValueError: Если один из параметров не является положительным + чётным числом. + """ + args = [num1, num2] + + if any(not isinstance(arg, int) for arg in args): + raise TypeError() + + if any(arg % 2 == 1 or arg <= 0 for arg in args): + raise ValueError() + + return num1 + num2 + + +# E + + +def merge(tuple1: tuple[int, ...], tuple2: tuple[int, ...]) -> tuple[int, ...]: + """Объединяет два отсортированных кортежа в один отсортированный. + + Args: + tuple1 (tuple[int, ...]): Первый кортеж отсортированных чисел. + tuple1 (tuple[int, ...]): Второй кортеж отсортированных чисел. + + Returns: + tuple[int, ...]: Отсортированный по возрастанию Кортеж целых чисел, + содержащий все числа из первого и второго кортежа + + Raises: + StopIteration: Если один из параметров не является итерируемым + объектом. + TypeError: Если значения входных параметров содержат «неоднородные» + данные. + ValueError: Если один из параметров не отсортирован. + """ + args = [tuple1, tuple2] + if any(not hasattr(arg, "__iter__") for arg in args): + raise StopIteration() + + if any(sorted(arg) != list(arg) for arg in args): + raise ValueError() + + list_1 = list(tuple1) + list2 = list(tuple2) + + result: list[int] = [] + + while list_1 and list2: + if list_1[0] < list2[0]: + result.append(list_1.pop(0)) + else: + result.append(list2.pop(0)) + + result.extend(list_1) + result.extend(list2) + + expected_type = type(result[0]) + if any(not isinstance(item, expected_type) for item in result): + raise TypeError() + + return tuple(result) + + +# F + + +# + +class NoSolutionsError(Exception): + """Ошибка отсутствия корней квадратного уравнения.""" + + +class InfiniteSolutionsError(Exception): + """Ошибка: бесконечное количество корней квадратного уравнения.""" + + +def find_roots( + a_coef: float | int, + b_coef: float | int, + c_coef: float | int, +) -> tuple[float, ...]: + """Функция для поиска корней квадратного уравнения. + + Принимает коэффициенты квадратного уравнения. + + Args: + a_coef (float | int): коэффициент a. + b_coef (float | int): коэффициент b. + c_coef (float | int): коэффициент c. + + Raises: + TypeError: Если переданные коэффициенты не являются рациональными + числами. + InfiniteSolutionsError: Бесконечное количества решений. + NoSolutionsError: Отсутствие решений. + + Returns: + tuple[float, ...]: Отсортированный кортеж корней квадратного уравнения. + """ + args = [a_coef, b_coef, c_coef] + if not all(type(arg) in (int, float) for arg in args): + raise TypeError() + + if a_coef == b_coef == c_coef == 0: + raise InfiniteSolutionsError() + + if a_coef == 0 and b_coef != 0 and c_coef != 0: + root = -c_coef / b_coef + + return (root, root) + + discriminant = b_coef**2 - 4 * a_coef * c_coef + + if discriminant < 0 or a_coef == b_coef == 0: + raise NoSolutionsError() + + if discriminant == 0: + root = -b_coef / (2 * a_coef) + + return (root, root) + + result1 = (-b_coef + discriminant**0.5) / (2 * a_coef) + result2 = (-b_coef - discriminant**0.5) / (2 * a_coef) + + min_result = min(result1, result2) + max_result = max(result1, result2) + + return (min_result, max_result) + + +# - + +# G + + +# + +class CyrillicError(Exception): + """Ошибка: если строка состоит не только из кириллицы.""" + + +class CapitalError(Exception): + """Ошибка: заглавной буквы. + + Если значение не начинается с заглавной буквы или найдена заглавная буква не в начале + значения. + """ + + +def name_validation(value: str) -> str: + """Функция валидации имени пользователя.""" + if not isinstance(value, str): + raise TypeError + + if not fullmatch(r"[а-яА-ЯёЁ]+", value): + raise CyrillicError + + if not value[0].isupper() or any(char.isupper() for char in value[1::]): + raise CapitalError + + return value + + +# - + +# H + + +# + +class BadCharacterError(Exception): + """Ошибка: неправильный символ. + + Если значение состоит не только из латинских букв, цифр и символа нижнего + подчёркивания. + """ + + +class StartsWithDigitError(Exception): + """Ошибка: если значение начинается с цифры.""" + + +def username_validation(value: str) -> str: + """Функция валидации имени пользователя.""" + if not isinstance(value, str): + raise TypeError + + if not fullmatch(r"[a-zA-Z0-9_]+$", value): + raise BadCharacterError + + if value[0].isdigit(): + raise StartsWithDigitError + + return value + + +# - + +# I + +# + +valid_keys = ["last_name", "first_name", "username"] + + +def user_validation(**kwargs: str) -> object: + """Функция валидации данных пользователя.""" + if sorted(valid_keys) != sorted(kwargs.keys()): + raise KeyError + + if not all(kwargs.values()): + raise KeyError + + name_validation(kwargs["last_name"]) + name_validation(kwargs["first_name"]) + username_validation(kwargs["username"]) + + return kwargs + + +# - + +# J + + +# + +class MinLengthError(Exception): + """Ошибка: если пароль меньше заданной длины.""" + + +class PossibleCharError(Exception): + """Ошибка: в пароле используется недопустимый символ.""" + + +class NeedCharError(Exception): + """Ошибка: в пароле не найдено ни одного обязательного символа.""" + + +default_valid_chars = "abcdefzhiklmnopqrstvxABCDEFZHIKLMNOPQRSTVX0123456789" + + +def password_validation( + password: str, + min_length: int = 8, + possible_chars: str = default_valid_chars, + at_least_one: Callable[[str], bool] = str.isdigit, +) -> str: + """Функция валидации пароля пользователя.""" + if not isinstance(password, str): + raise TypeError + + if len(password) < min_length: + raise MinLengthError + + if any(char not in possible_chars for char in password): + raise PossibleCharError + + if all(not at_least_one(char) for char in password): + raise NeedCharError + + hash_object = sha256() + hash_object.update(password.encode()) + hex_hash = hash_object.hexdigest() + + return hex_hash + + +print(password_validation("Hello12345")) diff --git a/Python/yandex-python-handbook/chapter_6_1.ipynb b/Python/yandex-python-handbook/chapter_6_1.ipynb new file mode 100644 index 00000000..d5c31083 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_1.ipynb @@ -0,0 +1,311 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 6.1.\n", + "\n", + "Модули math и numpy\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import sys\n", + "from typing import Literal\n", + "\n", + "import numpy as np\n", + "from numpy.typing import NDArray # type: ignore\n", + "\n", + "x_var = float(input())\n", + "\n", + "a_el = math.log(math.pow(x_var, 3 / 16), 32)\n", + "b_el = math.pow(x_var, math.cos(math.pi * x_var / (2 * math.e)))\n", + "c_el = math.pow(math.sin(x_var / math.pi), 2)\n", + "\n", + "result = a_el + b_el - c_el\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "for row in sys.stdin:\n", + " array_b = map(int, row[0:-1].split(\" \"))\n", + " result = math.gcd(*array_b)\n", + " print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "c44ac8ab", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8006a6c", + "metadata": {}, + "outputs": [], + "source": [ + "members, seats = map(int, input().split(\" \"))\n", + "\n", + "combinations = math.comb(members, seats)\n", + "in_place_combinations = combinations * seats // members\n", + "\n", + "print(in_place_combinations, combinations)" + ] + }, + { + "cell_type": "markdown", + "id": "bd2004d4", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1287244b", + "metadata": {}, + "outputs": [], + "source": [ + "array_d = list(map(float, input().split(\" \")))\n", + "\n", + "print(math.pow(math.prod(array_d), 1 / len(array_d)))" + ] + }, + { + "cell_type": "markdown", + "id": "a14327ea", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "246e3cd1", + "metadata": {}, + "outputs": [], + "source": [ + "x1, y1 = map(float, input().split(\" \"))\n", + "\n", + "p_x2, p_y2 = map(float, input().split(\" \"))\n", + "\n", + "x2 = p_x2 * math.cos(p_y2)\n", + "y2 = p_x2 * math.sin(p_y2)\n", + "\n", + "distance = math.dist((x1, y2), (x2, y2))\n", + "\n", + "print(distance)" + ] + }, + { + "cell_type": "markdown", + "id": "2df858d8", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d71637b0", + "metadata": {}, + "outputs": [], + "source": [ + "def multiplication_matrix(size: int) -> NDArray[np.int_]:\n", + " \"\"\"Построение таблицы умножения.\"\"\"\n", + " numbers = np.arange(1, size + 1)\n", + " matrix: NDArray[np.int_] = np.outer(numbers, numbers)\n", + "\n", + " return matrix" + ] + }, + { + "cell_type": "markdown", + "id": "719a93df", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cca8ef95", + "metadata": {}, + "outputs": [], + "source": [ + "def make_board(size: int) -> NDArray[np.int8]:\n", + " \"\"\"Создает шахматную доску размера size * size.\n", + "\n", + " Белые клетки - 1, чернные - 0.\n", + " В левом верхнем углу белая клетка.\n", + " \"\"\"\n", + " board = np.zeros((size, size), dtype=np.int8)\n", + "\n", + " board[::2, ::2] = 1\n", + " board[1::2, 1::2] = 1\n", + "\n", + " return board\n" + ] + }, + { + "cell_type": "markdown", + "id": "3317df58", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb48aa02", + "metadata": {}, + "outputs": [], + "source": [ + "def snake(\n", + " width: int, height: int, direction: Literal[\"H\", \"V\"] = \"H\"\n", + ") -> NDArray[np.int16]:\n", + " \"\"\"Создает змейку размера width * height.\"\"\"\n", + " matrix = np.zeros((height, width), dtype=np.int16)\n", + "\n", + " if direction == \"H\":\n", + " for index in range(height):\n", + " matrix[index] = np.arange(\n", + " index * width + 1, index * width + width + 1\n", + " )\n", + "\n", + " matrix[1::2, ::] = matrix[1::2, ::-1]\n", + " else:\n", + " for index in range(height):\n", + " matrix[index] = np.arange(index + 1, width * height + 1, height)\n", + "\n", + " matrix[::, 1::2] = matrix[::-1, 1::2]\n", + "\n", + " return matrix" + ] + }, + { + "cell_type": "markdown", + "id": "44a2f213", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "342d1f05", + "metadata": {}, + "outputs": [], + "source": [ + "def rotate(matrix: NDArray[np.int_], degrees: int) -> NDArray[np.int_]:\n", + " \"\"\"Поворачивает матрицу на заданное число градусов.\n", + "\n", + " Число градусов должно быть кратно 90.\n", + " Поворот происходит по часовой стрелке.\n", + " \"\"\"\n", + " rotated_matrix: NDArray[np.int_] = np.rot90(\n", + " matrix, k=(360 - degrees) // 90\n", + " )\n", + "\n", + " return rotated_matrix" + ] + }, + { + "cell_type": "markdown", + "id": "2574d9c7", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87e43170", + "metadata": {}, + "outputs": [], + "source": [ + "def stairs(array: NDArray[np.int_]) -> NDArray[np.int_]:\n", + " \"\"\"Создает матрицу по вектору.\n", + "\n", + " Каждая строка является копией вектора со смещение.\n", + " \"\"\"\n", + " size = array.size\n", + " matrix: NDArray[np.int_] = np.tile(array, (size, 1))\n", + "\n", + " for index in range(1, size):\n", + " matrix[index] = np.roll(matrix[index], index)\n", + "\n", + " return matrix" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_6_1.py b/Python/yandex-python-handbook/chapter_6_1.py new file mode 100644 index 00000000..15914c77 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_1.py @@ -0,0 +1,154 @@ +"""Задания к главе 6.1. + +Модули math и numpy + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +import math +import sys +from typing import Literal + +import numpy as np +from numpy.typing import NDArray # type: ignore + +x_var = float(input()) + +a_el = math.log(math.pow(x_var, 3 / 16), 32) +b_el = math.pow(x_var, math.cos(math.pi * x_var / (2 * math.e))) +c_el = math.pow(math.sin(x_var / math.pi), 2) + +result = a_el + b_el - c_el + +print(result) +# - + +# B + +for row in sys.stdin: + array_b = map(int, row[0:-1].split(" ")) + result = math.gcd(*array_b) + print(result) + +# C + +# + +members, seats = map(int, input().split(" ")) + +combinations = math.comb(members, seats) +in_place_combinations = combinations * seats // members + +print(in_place_combinations, combinations) +# - + +# D + +# + +array_d = list(map(float, input().split(" "))) + +print(math.pow(math.prod(array_d), 1 / len(array_d))) +# - + +# E + +# + +x1, y1 = map(float, input().split(" ")) + +p_x2, p_y2 = map(float, input().split(" ")) + +x2 = p_x2 * math.cos(p_y2) +y2 = p_x2 * math.sin(p_y2) + +distance = math.dist((x1, y2), (x2, y2)) + +print(distance) + + +# - + +# F + + +def multiplication_matrix(size: int) -> NDArray[np.int_]: + """Построение таблицы умножения.""" + numbers = np.arange(1, size + 1) + matrix: NDArray[np.int_] = np.outer(numbers, numbers) + + return matrix + + +# G + + +def make_board(size: int) -> NDArray[np.int8]: + """Создает шахматную доску размера size * size. + + Белые клетки - 1, чернные - 0. + В левом верхнем углу белая клетка. + """ + board = np.zeros((size, size), dtype=np.int8) + + board[::2, ::2] = 1 + board[1::2, 1::2] = 1 + + return board + + +# H + + +def snake( + width: int, height: int, direction: Literal["H", "V"] = "H" +) -> NDArray[np.int16]: + """Создает змейку размера width * height.""" + matrix = np.zeros((height, width), dtype=np.int16) + + if direction == "H": + for index in range(height): + matrix[index] = np.arange( + index * width + 1, index * width + width + 1 + ) + + matrix[1::2, ::] = matrix[1::2, ::-1] + else: + for index in range(height): + matrix[index] = np.arange(index + 1, width * height + 1, height) + + matrix[::, 1::2] = matrix[::-1, 1::2] + + return matrix + + +# I + + +def rotate(matrix: NDArray[np.int_], degrees: int) -> NDArray[np.int_]: + """Поворачивает матрицу на заданное число градусов. + + Число градусов должно быть кратно 90. Поворот происходит по часовой стрелке. + """ + rotated_matrix: NDArray[np.int_] = np.rot90( + matrix, k=(360 - degrees) // 90 + ) + + return rotated_matrix + + +# J + + +def stairs(array: NDArray[np.int_]) -> NDArray[np.int_]: + """Создает матрицу по вектору. + + Каждая строка является копией вектора со смещение. + """ + size = array.size + matrix: NDArray[np.int_] = np.tile(array, (size, 1)) + + for index in range(1, size): + matrix[index] = np.roll(matrix[index], index) + + return matrix diff --git a/Python/yandex-python-handbook/chapter_6_2.ipynb b/Python/yandex-python-handbook/chapter_6_2.ipynb new file mode 100644 index 00000000..9fc119f4 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_2.ipynb @@ -0,0 +1,346 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 6.2.\n", + "\n", + "Модуль pandas\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Callable\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "def length_stats_a(text: str) -> pd.Series: # type: ignore\n", + " \"\"\"Функция преобразует текст в Series с длинами слов.\"\"\"\n", + " words = \"\".join(char for char in text if char.isalpha() or char.isspace())\n", + "\n", + " sorted_words = sorted(set(words.lower().split()))\n", + "\n", + " return pd.Series((len(word) for word in sorted_words), index=sorted_words)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "def length_stats_b(text: str) -> tuple[pd.Series, pd.Series]: # type: ignore\n", + " \"\"\"Функция преобразует текст в Series с длинами слов.\n", + "\n", + " Возвращает два объекта: с четными и нечетными длинами слов.\n", + " \"\"\"\n", + " words = \"\".join(char for char in text if char.isalpha() or char.isspace())\n", + "\n", + " sorted_words = sorted(set(words.lower().split()))\n", + "\n", + " even: dict[str, int] = {}\n", + " odd: dict[str, int] = {}\n", + "\n", + " for word in sorted_words:\n", + " length = len(word)\n", + "\n", + " if length % 2 == 0:\n", + " even[word] = length\n", + " else:\n", + " odd[word] = length\n", + "\n", + " return (pd.Series(odd, dtype=\"int64\"), pd.Series(even, dtype=\"int64\"))" + ] + }, + { + "cell_type": "markdown", + "id": "c44ac8ab", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8006a6c", + "metadata": {}, + "outputs": [], + "source": [ + "# mypy: ignore-errors\n", + "\n", + "\n", + "def cheque(price_list: pd.Series, **kwargs: int) -> pd.DataFrame:\n", + " \"\"\"Формирует DataFrame с данными о продуктах.\"\"\"\n", + " products: list[str] = sorted(name for name in kwargs)\n", + " prices: list[int] = [price_list[name].item() for name in products]\n", + " numbers: list[int] = [kwargs[name] for name in products]\n", + " costs: list[int] = [\n", + " prices[index] * numbers[index] for index in range(len(products))\n", + " ]\n", + "\n", + " return pd.DataFrame(\n", + " {\n", + " \"product\": products,\n", + " \"price\": prices,\n", + " \"number\": numbers,\n", + " \"cost\": costs,\n", + " }\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "bd2004d4", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1287244b", + "metadata": {}, + "outputs": [], + "source": [ + "def discount(products: pd.DataFrame) -> pd.DataFrame:\n", + " \"\"\"Применяет скидку, если товаров больше двух.\"\"\"\n", + " copy = products.copy()\n", + "\n", + " mask = copy[\"number\"] > 2\n", + " copy.loc[mask, \"cost\"] = copy.loc[mask, \"cost\"] / 2\n", + "\n", + " return copy" + ] + }, + { + "cell_type": "markdown", + "id": "a14327ea", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "246e3cd1", + "metadata": {}, + "outputs": [], + "source": [ + "# fmt: off\n", + "def get_long(\n", + " words: pd.Series, # type: ignore\n", + " min_length: int = 5\n", + ") -> pd.Series: # type: ignore\n", + " \"\"\"Возвращает Series со словами не короче min_length.\"\"\"\n", + " return words[words >= min_length]\n", + "# fmt: on" + ] + }, + { + "cell_type": "markdown", + "id": "2df858d8", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d71637b0", + "metadata": {}, + "outputs": [], + "source": [ + "def best(journal: pd.DataFrame) -> pd.DataFrame:\n", + " \"\"\"Функция отфильтровывает ударников из переданного DataFrame.\"\"\"\n", + " columns = [\"maths\", \"physics\", \"computer science\"]\n", + "\n", + " mask = (journal[columns] > 3).all(axis=1)\n", + " return journal[mask]" + ] + }, + { + "cell_type": "markdown", + "id": "719a93df", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cca8ef95", + "metadata": {}, + "outputs": [], + "source": [ + "def need_to_work_better(journal: pd.DataFrame) -> pd.DataFrame:\n", + " \"\"\"Функция отфильтровывает тех, у кого есть 2.\"\"\"\n", + " columns = [\"maths\", \"physics\", \"computer science\"]\n", + "\n", + " mask = (journal[columns] == 2).any(axis=1)\n", + " return journal.loc[mask]" + ] + }, + { + "cell_type": "markdown", + "id": "3317df58", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb48aa02", + "metadata": {}, + "outputs": [], + "source": [ + "def update(journal: pd.DataFrame) -> pd.DataFrame:\n", + " \"\"\"Считает средний балл и сортирует студентов.\"\"\"\n", + " columns = [\"maths\", \"physics\", \"computer science\"]\n", + "\n", + " copy = journal.copy()\n", + "\n", + " copy[\"average\"] = copy[columns].mean(axis=1)\n", + "\n", + " return copy.sort_values([\"average\", \"name\"], ascending=(False, True))" + ] + }, + { + "cell_type": "markdown", + "id": "44a2f213", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "342d1f05", + "metadata": {}, + "outputs": [], + "source": [ + "x1, y1 = map(int, input().split())\n", + "x2, y2 = map(int, input().split())\n", + "\n", + "max_x = x2\n", + "max_y = y1\n", + "min_x = x1\n", + "min_y = y2\n", + "\n", + "shots = pd.read_csv(\"data.csv\")\n", + "\n", + "filtered = shots[\n", + " all(\n", + " [\n", + " (shots[\"x\"] <= max_x),\n", + " (shots[\"x\"] >= min_x),\n", + " (shots[\"y\"] <= max_y),\n", + " (shots[\"y\"] >= min_y),\n", + " ]\n", + " )\n", + "]\n", + "\n", + "print(filtered)" + ] + }, + { + "cell_type": "markdown", + "id": "2574d9c7", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87e43170", + "metadata": {}, + "outputs": [], + "source": [ + "def values(\n", + " func: Callable[[float], float],\n", + " start: float,\n", + " end: float,\n", + " step: float,\n", + ") -> pd.Series: # type: ignore\n", + " \"\"\"Возвращает Series значений функции в точках диапазона.\"\"\"\n", + " indexes = np.arange(start, end + step, step)\n", + "\n", + " stpes = (func(index) for index in indexes)\n", + "\n", + " return pd.Series(stpes, indexes)\n", + "\n", + "\n", + "def min_extremum(data: pd.Series) -> float: # type: ignore\n", + " \"\"\"Возвращает минимум из Series.\"\"\"\n", + " return float(data.idxmin())\n", + "\n", + "\n", + "def max_extremum(data: pd.Series) -> float: # type: ignore\n", + " \"\"\"Возвращает максимум из Series.\"\"\"\n", + " return data[data == data.max()].index.max()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_6_2.py b/Python/yandex-python-handbook/chapter_6_2.py new file mode 100644 index 00000000..54fc2f67 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_2.py @@ -0,0 +1,196 @@ +"""Задания к главе 6.2. + +Модуль pandas + +Хендбук Яндекс "Основы Python". +""" + +# A + +# + +from typing import Callable + +import numpy as np +import pandas as pd + + +def length_stats_a(text: str) -> pd.Series: # type: ignore + """Функция преобразует текст в Series с длинами слов.""" + words = "".join(char for char in text if char.isalpha() or char.isspace()) + + sorted_words = sorted(set(words.lower().split())) + + return pd.Series((len(word) for word in sorted_words), index=sorted_words) + + +# - + +# B + + +def length_stats_b(text: str) -> tuple[pd.Series, pd.Series]: # type: ignore + """Функция преобразует текст в Series с длинами слов. + + Возвращает два объекта: с четными и нечетными длинами слов. + """ + words = "".join(char for char in text if char.isalpha() or char.isspace()) + + sorted_words = sorted(set(words.lower().split())) + + even: dict[str, int] = {} + odd: dict[str, int] = {} + + for word in sorted_words: + length = len(word) + + if length % 2 == 0: + even[word] = length + else: + odd[word] = length + + return (pd.Series(odd, dtype="int64"), pd.Series(even, dtype="int64")) + + +# C + +# + +# mypy: ignore-errors + + +def cheque(price_list: pd.Series, **kwargs: int) -> pd.DataFrame: + """Формирует DataFrame с данными о продуктах.""" + products: list[str] = sorted(name for name in kwargs) + prices: list[int] = [price_list[name].item() for name in products] + numbers: list[int] = [kwargs[name] for name in products] + costs: list[int] = [ + prices[index] * numbers[index] for index in range(len(products)) + ] + + return pd.DataFrame( + { + "product": products, + "price": prices, + "number": numbers, + "cost": costs, + } + ) + + +# - + +# D + + +def discount(products: pd.DataFrame) -> pd.DataFrame: + """Применяет скидку, если товаров больше двух.""" + copy = products.copy() + + mask = copy["number"] > 2 + copy.loc[mask, "cost"] = copy.loc[mask, "cost"] / 2 + + return copy + + +# E + +# fmt: off +def get_long( + words: pd.Series, # type: ignore + min_length: int = 5 +) -> pd.Series: # type: ignore + """Возвращает Series со словами не короче min_length.""" + return words[words >= min_length] +# fmt: on + + +# F + + +def best(journal: pd.DataFrame) -> pd.DataFrame: + """Функция отфильтровывает ударников из переданного DataFrame.""" + columns = ["maths", "physics", "computer science"] + + mask = (journal[columns] > 3).all(axis=1) + return journal[mask] + + +# G + + +def need_to_work_better(journal: pd.DataFrame) -> pd.DataFrame: + """Функция отфильтровывает тех, у кого есть 2.""" + columns = ["maths", "physics", "computer science"] + + mask = (journal[columns] == 2).any(axis=1) + return journal.loc[mask] + + +# H + + +def update(journal: pd.DataFrame) -> pd.DataFrame: + """Считает средний балл и сортирует студентов.""" + columns = ["maths", "physics", "computer science"] + + copy = journal.copy() + + copy["average"] = copy[columns].mean(axis=1) + + return copy.sort_values(["average", "name"], ascending=(False, True)) + + +# I + +# + +x1, y1 = map(int, input().split()) +x2, y2 = map(int, input().split()) + +max_x = x2 +max_y = y1 +min_x = x1 +min_y = y2 + +shots = pd.read_csv("data.csv") + +filtered = shots[ + all( + [ + (shots["x"] <= max_x), + (shots["x"] >= min_x), + (shots["y"] <= max_y), + (shots["y"] >= min_y), + ] + ) +] + +print(filtered) + + +# - + +# J + + +# + +def values( + func: Callable[[float], float], + start: float, + end: float, + step: float, +) -> pd.Series: # type: ignore + """Возвращает Series значений функции в точках диапазона.""" + indexes = np.arange(start, end + step, step) + + stpes = (func(index) for index in indexes) + + return pd.Series(stpes, indexes) + + +def min_extremum(data: pd.Series) -> float: # type: ignore + """Возвращает минимум из Series.""" + return float(data.idxmin()) + + +def max_extremum(data: pd.Series) -> float: # type: ignore + """Возвращает максимум из Series.""" + return data[data == data.max()].index.max() diff --git a/Python/yandex-python-handbook/chapter_6_3.ipynb b/Python/yandex-python-handbook/chapter_6_3.ipynb new file mode 100644 index 00000000..2bf6d481 --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_3.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "144d3bac", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "Задания к главе 6.3.\n", + "\n", + "Модуль requests\n", + "\n", + "Хендбук Яндекс \"Основы Python\".\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "d9028306", + "metadata": {}, + "source": [ + "A" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd002ff", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "from requests import delete, get, post, put\n", + "\n", + "base_host = \"http://127.0.0.1:5000\"\n", + "\n", + "response = get(base_host, timeout=5000)\n", + "\n", + "message = response.content.decode(\"utf-8\")\n", + "\n", + "print(message)" + ] + }, + { + "cell_type": "markdown", + "id": "942bcb6f", + "metadata": {}, + "source": [ + "B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24d58461", + "metadata": {}, + "outputs": [], + "source": [ + "base_host = f\"http://{input()}\"\n", + "\n", + "result = 0\n", + "\n", + "while response_b := int(get(base_host, timeout=5000).text):\n", + " result += response_b\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "c44ac8ab", + "metadata": {}, + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8006a6c", + "metadata": {}, + "outputs": [], + "source": [ + "base_host = f\"http://{input()}\"\n", + "\n", + "response_c = get(base_host, timeout=5000).json()\n", + "\n", + "sum_c = 0\n", + "for item in response_c:\n", + " sum_c += item if isinstance(item, int) else 0\n", + "\n", + "print(sum_c)" + ] + }, + { + "cell_type": "markdown", + "id": "bd2004d4", + "metadata": {}, + "source": [ + "D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1287244b", + "metadata": {}, + "outputs": [], + "source": [ + "base_host = f\"http://{input()}\"\n", + "key = input()\n", + "\n", + "response_d = get(base_host, timeout=5000).json()\n", + "\n", + "print(response_d.get(key, \"No data\"))" + ] + }, + { + "cell_type": "markdown", + "id": "a14327ea", + "metadata": {}, + "source": [ + "E" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "246e3cd1", + "metadata": {}, + "outputs": [], + "source": [ + "host, *paths = map(str.strip, sys.stdin)\n", + "\n", + "results = [get(f\"http://{host}{path}\", timeout=5000).json() for path in paths]\n", + "\n", + "print(sum(sum(nums) for nums in results))" + ] + }, + { + "cell_type": "markdown", + "id": "2df858d8", + "metadata": {}, + "source": [ + "F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d71637b0", + "metadata": {}, + "outputs": [], + "source": [ + "host = f\"http://{input()}\"\n", + "\n", + "users = get(f\"{host}/users\", timeout=5000).json()\n", + "\n", + "names = list(\n", + " map(lambda user: f\"{user['last_name']} {user['first_name']}\", users)\n", + ")\n", + "\n", + "print(\"\\n\".join(sorted(names)))" + ] + }, + { + "cell_type": "markdown", + "id": "719a93df", + "metadata": {}, + "source": [ + "G" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cca8ef95", + "metadata": {}, + "outputs": [], + "source": [ + "host = f\"http://{input()}\"\n", + "user_id = input()\n", + "\n", + "text = \"\".join(string for string in sys.stdin)\n", + "\n", + "\n", + "try:\n", + " user = get(f\"{host}/users/{user_id}\", timeout=5000).json()\n", + "except ValueError:\n", + " print(\"Пользователь не найден\")\n", + "else:\n", + " for key in user:\n", + " text = text.replace(f\"{{{key}}}\", str(user[key]))\n", + " print(text)" + ] + }, + { + "cell_type": "markdown", + "id": "bccc767d", + "metadata": {}, + "source": [ + "H" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eac9e370", + "metadata": {}, + "outputs": [], + "source": [ + "keys = [\"username\", \"last_name\", \"first_name\", \"email\"]\n", + "\n", + "host, *data = map(str.strip, sys.stdin)\n", + "\n", + "user_dict: dict[str, str] = dict(zip(keys, data))\n", + "\n", + "post(f\"http://{host}/users/\", json=user_dict, timeout=5000)" + ] + }, + { + "cell_type": "markdown", + "id": "44a2f213", + "metadata": {}, + "source": [ + "I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "342d1f05", + "metadata": {}, + "outputs": [], + "source": [ + "host, user_id, *updated_data = map(str.strip, sys.stdin)\n", + "\n", + "updated_data_dict: dict[str, str] = dict(\n", + " map(lambda item: item.split(\"=\"), updated_data)\n", + ")\n", + "\n", + "print(updated_data_dict)\n", + "put(f\"http://{host}/users/{user_id}\", json=updated_data_dict, timeout=5000)" + ] + }, + { + "cell_type": "markdown", + "id": "2574d9c7", + "metadata": {}, + "source": [ + "J" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87e43170", + "metadata": {}, + "outputs": [], + "source": [ + "host, user_id = map(str.strip, sys.stdin)\n", + "\n", + "delete(f\"http://{host}/users/{user_id}\", timeout=5000)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Python/yandex-python-handbook/chapter_6_3.py b/Python/yandex-python-handbook/chapter_6_3.py new file mode 100644 index 00000000..5baf121e --- /dev/null +++ b/Python/yandex-python-handbook/chapter_6_3.py @@ -0,0 +1,127 @@ +# %% +"""Задания к главе 6.3. + +Модуль requests + +Хендбук Яндекс "Основы Python". +""" + +# A + +# %% +import sys + +from requests import delete, get, post, put + +base_host = "http://127.0.0.1:5000" + +response = get(base_host, timeout=5000) + +message = response.content.decode("utf-8") + +print(message) + +# B + +# %% +base_host = f"http://{input()}" + +result = 0 + +while response_b := int(get(base_host, timeout=5000).text): + result += response_b + +print(result) + +# C + +# %% +base_host = f"http://{input()}" + +response_c = get(base_host, timeout=5000).json() + +sum_c = 0 +for item in response_c: + sum_c += item if isinstance(item, int) else 0 + +print(sum_c) + +# D + +# %% +base_host = f"http://{input()}" +key = input() + +response_d = get(base_host, timeout=5000).json() + +print(response_d.get(key, "No data")) + +# E + +# %% +host, *paths = map(str.strip, sys.stdin) + +results = [get(f"http://{host}{path}", timeout=5000).json() for path in paths] + +print(sum(sum(nums) for nums in results)) + +# F + +# %% +host = f"http://{input()}" + +users = get(f"{host}/users", timeout=5000).json() + +names = list( + map(lambda user: f"{user['last_name']} {user['first_name']}", users) +) + +print("\n".join(sorted(names))) + +# G + +# %% +host = f"http://{input()}" +user_id = input() + +text = "".join(string for string in sys.stdin) + + +try: + user = get(f"{host}/users/{user_id}", timeout=5000).json() +except ValueError: + print("Пользователь не найден") +else: + for key in user: + text = text.replace(f"{{{key}}}", str(user[key])) + print(text) + +# H + +# %% +keys = ["username", "last_name", "first_name", "email"] + +host, *data = map(str.strip, sys.stdin) + +user_dict: dict[str, str] = dict(zip(keys, data)) + +post(f"http://{host}/users/", json=user_dict, timeout=5000) + +# I + +# %% +host, user_id, *updated_data = map(str.strip, sys.stdin) + +updated_data_dict: dict[str, str] = dict( + map(lambda item: item.split("="), updated_data) +) + +print(updated_data_dict) +put(f"http://{host}/users/{user_id}", json=updated_data_dict, timeout=5000) + +# J + +# %% +host, user_id = map(str.strip, sys.stdin) + +delete(f"http://{host}/users/{user_id}", timeout=5000) diff --git a/README.md b/README.md index edcf18ea..22f91dfb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ # Data-Science-For-Beginners-from-scratch-SENATOROV -Командный репозиторий. + +Учебный проект + +``` +project_root/ +├── requirements.txt # зависимости проекта +├── setup.py # (если проект - пакет) +├── README.md # описание проекта +└── LICENSE # лицензия +``` diff --git a/config.cfg b/config.cfg index bfead2ca..43d7a3a1 100644 --- a/config.cfg +++ b/config.cfg @@ -1,2 +1,2 @@ [flake8] -max-line-length = 79 +max-line-length = 90 diff --git a/github/opensource.ipynb b/github/opensource.ipynb new file mode 100644 index 00000000..cdee54f2 --- /dev/null +++ b/github/opensource.ipynb @@ -0,0 +1,90 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6ad48f15", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Issue #8 Контрибьютинг в Open Source.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/8\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "6b9f76f1", + "metadata": {}, + "source": [ + "1. Есть ли у него лицензия? Обычно в корне репозитория находится файл LICENSE.\n", + "Да\n", + "\n", + "2. Напишите название понравившейся компании и ссылку на репозиторий\n", + "First Contributions\n", + "https://github.com/firstcontributions/first-contributions\n", + "\n", + "3. Проект активно принимает стороннюю помощь?\n", + "Да\n", + "\n", + "4. Напишите второе улучшение которое вы сделали\n", + "Внес себя в список контрибьюторов.\n", + "\n", + "5. Посмотрите на коммиты в основной ветке, напишите общее количество\n", + "12065\n", + "\n", + "6. Когда был последний коммит?\n", + "1 час назад\n", + "\n", + "7. Сколько контрибьюторов у проекта?\n", + "Более 5000\n", + "\n", + "8. Как часто люди коммитят в репозиторий? (На GitHub выяснить это можно, кликнув по ссылке «Commits» в верхней панели.)\n", + "Несколько коммитов в час\n", + "\n", + "9. Сколько сейчас открытых ишью?\n", + "56\n", + "\n", + "10. Быстро ли мейнтейнеры реагируют на ишью после того, когда они открываются?\n", + "Быстро\n", + "\n", + "11. Ведётся ли активное обсуждение ишью?\n", + "При необходимости\n", + "\n", + "12. Есть ли недавно созданные ишью?\n", + "Да\n", + "\n", + "13. Есть ли закрытые ишью? (На странице Issues GitHub-репозитория щелкните на вкладку «Closed», чтобы увидеть закрытые ишью.)\n", + "Да\n", + "\n", + "14. Сколько сейчас открытых пул-реквестов?\n", + "53\n", + "\n", + "15. Быстро ли мейнтейнеры реагируют на пул-реквесты после их открытия?\n", + "Мой влили через несколько минут\n", + "\n", + "16. Ведётся ли активное обсуждение пул-реквестов?\n", + "Нет\n", + "\n", + "17. Есть ли недавно отправленные пул-реквесты?\n", + "Да\n", + "\n", + "18. Как давно были объединены пул-реквесты? (На странице Pull Request GitHub-репозитория щелкните на вкладку «Closed», чтобы увидеть закрытые пул-реквесты.)\n", + "Час назад" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/github/opensource.py b/github/opensource.py new file mode 100644 index 00000000..f3c1a5f3 --- /dev/null +++ b/github/opensource.py @@ -0,0 +1,59 @@ +"""Issue #8 Контрибьютинг в Open Source. + +https://github.com/SENATOROVAI/intro-cs/issues/8 +""" + +# 1. Есть ли у него лицензия? Обычно в корне репозитория находится файл LICENSE. +# Да +# +# 2. Напишите название понравившейся компании и ссылку на репозиторий +# First Contributions +# https://github.com/firstcontributions/first-contributions +# +# 3. Проект активно принимает стороннюю помощь? +# Да +# +# 4. Напишите второе улучшение которое вы сделали +# Внес себя в список контрибьюторов. +# +# 5. Посмотрите на коммиты в основной ветке, напишите общее количество +# 12065 +# +# 6. Когда был последний коммит? +# 1 час назад +# +# 7. Сколько контрибьюторов у проекта? +# Более 5000 +# +# 8. Как часто люди коммитят в репозиторий? (На GitHub выяснить это можно, кликнув по ссылке «Commits» в верхней панели.) +# Несколько коммитов в час +# +# 9. Сколько сейчас открытых ишью? +# 56 +# +# 10. Быстро ли мейнтейнеры реагируют на ишью после того, когда они открываются? +# Быстро +# +# 11. Ведётся ли активное обсуждение ишью? +# При необходимости +# +# 12. Есть ли недавно созданные ишью? +# Да +# +# 13. Есть ли закрытые ишью? (На странице Issues GitHub-репозитория щелкните на вкладку «Closed», чтобы увидеть закрытые ишью.) +# Да +# +# 14. Сколько сейчас открытых пул-реквестов? +# 53 +# +# 15. Быстро ли мейнтейнеры реагируют на пул-реквесты после их открытия? +# Мой влили через несколько минут +# +# 16. Ведётся ли активное обсуждение пул-реквестов? +# Нет +# +# 17. Есть ли недавно отправленные пул-реквесты? +# Да +# +# 18. Как давно были объединены пул-реквесты? (На странице Pull Request GitHub-репозитория щелкните на вкладку «Closed», чтобы увидеть закрытые пул-реквесты.) +# Час назад diff --git a/github/quiz.ipynb b/github/quiz.ipynb new file mode 100644 index 00000000..971ca29e --- /dev/null +++ b/github/quiz.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6b04467c", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Issue #8 Контрибьютинг в Open Source.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/8\n", + "\"\"\"" + ] + }, + { + "attachments": { + "image.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "5ebeb9d9", + "metadata": {}, + "source": [ + "1.1. Что такое GitHub?\n", + "Портал для совместной разработки с использованием системы контроля версий Git.\n", + "\n", + "1.2. Как GitHub связан с Git?\n", + "GitHub построен на основе Git.\n", + "\n", + "1.3. Чем отличается fork репозитория от его клонирования (clone)?\n", + "При fork создается копия начального репозитория в аккаунте пользователя, полностью подконтрольная пользователю. При этом сохраняется связь с оригинальным репозиторием.\n", + "При clone происходит копирование удаленного репозитория на компьютер.\n", + "\n", + "1.4. Зачем нужны и как работают pull requests?\n", + "Используются для того, чтобы \"слить\" изменения из одной ветки в другую.\n", + "В том числе для внесения изменений в оригинальный репозиторий при fork.\n", + "\n", + "1.5. GitHub использует ваш почтовый адрес для привязки ваших Git коммитов к вашей учётной записи?\n", + "Да\n", + "\n", + "1.6 Какая команда генерирует SSH ключ для Доступа по SSH к репозиторию\n", + "ssh-keygen -o\n", + "\n", + "2.1 Вставьте сюда ссылку на ваше ответвление\n", + "https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV\n", + "\n", + "2.2 вставьте сюда ссылку на вашу ветку dev\n", + "https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/tree/dev\n", + "\n", + "2.4 вставьте номер из пункта 2\n", + "416\n", + "\n", + "2.6 Выполните Merge pull request (Рисунок 116), вставьте сюда ссылку на ваш пул реквест\n", + "https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/pull/1\n", + "\n", + "2.7 Вставьте сюда ссылку на закрытые пул реквесты\n", + "https://github.com/SENATOROVAI/Data-Science-For-Beginners-from-scratch-SENATOROV/pulls?q=is%3Apr+is%3Aclosed\n", + "\n", + "2.8 Как посмотреть какие файлы были в репозитории на момент определенного коммита?\n", + "Переходим в список коммитов -> Выбираем нужный коммит -> Browse files\n", + "\n", + "Вставьте сюда ссылку на любой коммит\n", + "https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/commit/66a964b0227bc1989009974e6bed40e6c33a80f3\n", + "\n", + "2.9 как открыть запрос слияния, указывающий на другой запрос слияния и зачем это нужно?\n", + "На странице ПР кнопка Edit. Затем изменить целевую ветку.\n", + "\n", + "3. Напишите 8 пунктов, которые нужно сделать, чтобы внести вклад в чужой проект.\n", + "\t1. Создаем fork репозитория, в который хотим внести вклад.\n", + "\t2. Клонируем форкнутый репозиторий на локальный компьютер.\n", + "\t3. Находим ошибку в коде / место, которое можно улучшить.\n", + "\t4. Создаем Issue в оригинальном репозитории с понятным тайтлом, детальным описанием, по возможности прикладываем скриншоты, коды ошибок, трассировку и пр.\n", + "\t5. Вносим изменения в код.\n", + "\t6. Создаем коммит, используя ключевые слова closes и пр со ссылкой на issue.\n", + "\t7. Отправляем коммиты в форкнутый репозиторий.\n", + "\t8. Создаем ПР в оригинальный репозиторий.\n", + "\n", + "3.1. Какие практики принято соблюдать при создании Pull Request чтобы закрыть автоматический issues?\n", + "Какие практики принято соблюдать при создании commit чтобы закрыть автоматический issues?\n", + "В тайтле указываем ссылку на issue или номер issue\n", + "В описании используем ключевые слова со ссылкой на issue или с номером issue:\n", + "-close\n", + "-closes\n", + "-closed\n", + "-fix\n", + "-fixes\n", + "-fixed\n", + "-resolve\n", + "-resolves\n", + "-resolved\n", + "\n", + "3.2 Как отклонить/закрыть пул реквест? (предоставьте скриншот где это в гитхабе)\n", + "Кнопка Close pull request\t\n", + "![image.png](attachment:image.png)\n", + "\n", + "3.3 Перед отправкой пул реквеста нужно ли создавать ишьюс?\n", + "Можно создать ПР и без issue, но для поддержания прозрачного и понятного процесса разработки как правило заранее создают issue, над исправлением которого работают.\n", + "\n", + "3.4 В какой вкладке можно посмотреть список изменений который был в пул реквесте?\n", + "Files changed\n", + "\n", + "3.5 В какой вкладке находится страница обсуждений пул реквеста?\n", + "Conversation\n", + "\n", + "4 Можно ли открыть пул реквест, если вы ничего не вносили в FORK?\n", + "Нет\n", + "\n", + "4.1 Что нужно сделать чтобы открыть пул реквест?\n", + "Запушать изменения в Github. Затем нажать кнопку New pull request\n", + "\n", + "4.2 Что нужно сделать Если ваш Форк устарел?\n", + "Обновить форк.\n", + "Добавляем оригинальный репозиторий в remote\n", + "git remote add upstream https://github.com/оригинал-автор/имя-репозитория.git\n", + "\n", + "После этого в подтягиваем изменения из оригинального репозитории:\n", + "git fetch upstream\n", + "\n", + "И вливаем изменения в свою ветку:\n", + "git merge upstream/main\n", + "\n", + "4.3 Что нужно сделать если в пул реквесте имеются конфликты слияния?\n", + "Обновить локальной свою ветку, подтянув изменения из оригинального репозитория.\n", + "Разрешить конфликты.\n", + "Отправить изменения в Github.\n", + "\n", + "Второй вариант - перебазировать свою ветку.\n", + "\n", + "5. Что нужно сделать Для добавления отрывка кода в комментарии к ишьюсу?\n", + "Использовать ```...```. В идеале с указанием языка, на котором написан код.\n", + "\n", + "5.1 На какую клавишу нажать клавишу чтобы выделенный текст был включён как цитата в ваш комментарий?\n", + ">\n", + "\n", + "5.2 Как вставить картинку в ишьюс?\n", + "Перетащить картинку на поле ввода комментария. Или в поле ввода вставить картинку из буфера.\n", + "\n", + "6. Как понять что ваш форк устарел?\n", + "В вебинтерфейсе появится уведомление \"This branch is X commits behind [оригинальный репозиторий]:main.\"\n", + "Или вручную обновить remote оригинального репозитория и с помощью git log посмотреть коммиты на ветке оригинального репозитория.\n", + "\n", + "6.1 Как обновить форк?\n", + "Добавляем оригинальный репозиторий в remote\n", + "git remote add upstream https://github.com/оригинал-автор/имя-репозитория.git\n", + "\n", + "После этого в подтягиваем изменения из оригинального репозитории:\n", + "git fetch upstream\n", + "\n", + "И вливаем изменения в свою ветку:\n", + "git merge upstream/main\n", + "\n", + "7. Как добавить участников в ваш репозиторий, чтобы команда могла работать над одним репозиторием?\n", + "Settings -> Collaborators\n", + "\n", + "8. Какой символ нужен для упоминания кого-либо?\n", + "@\n", + "\n", + "8.1 Где находится Центр уведомлений, напишите ссылку\n", + "https://github.com/notifications\n", + "\n", + "9. Что такое и зачем нужен файл README\n", + "Содержит описание проекта. А так же инструкции по установке зависимостей, запуску и использованию\n", + "\n", + "9.1 Что такое и зачем нужен файл CONTRIBUTING\n", + "Файл, описывающий правила и стандарты для open source работы над проектом\n", + "\n", + "10. Как изменить основную ветку?\n", + "Settings -> General -> Default branch\n", + "\n", + "10.1 Как передать проект? какая кнопка?\n", + "Кнопка \"Transfer ownership\" в Settings -> General\n", + "\n", + "10.2 Что такое файл .gitignore?\n", + "Файл, который содержит перечень файлов и папок (в том числе в виде шаблонов), которые не должны отслеживаться." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/github/quiz.py b/github/quiz.py new file mode 100644 index 00000000..eacbf98f --- /dev/null +++ b/github/quiz.py @@ -0,0 +1,156 @@ +"""Issue #8 Контрибьютинг в Open Source. + +https://github.com/SENATOROVAI/intro-cs/issues/8 +""" + +# 1.1. Что такое GitHub? +# Портал для совместной разработки с использованием системы контроля версий Git. +# +# 1.2. Как GitHub связан с Git? +# GitHub построен на основе Git. +# +# 1.3. Чем отличается fork репозитория от его клонирования (clone)? +# При fork создается копия начального репозитория в аккаунте пользователя, полностью подконтрольная пользователю. При этом сохраняется связь с оригинальным репозиторием. +# При clone происходит копирование удаленного репозитория на компьютер. +# +# 1.4. Зачем нужны и как работают pull requests? +# Используются для того, чтобы "слить" изменения из одной ветки в другую. +# В том числе для внесения изменений в оригинальный репозиторий при fork. +# +# 1.5. GitHub использует ваш почтовый адрес для привязки ваших Git коммитов к вашей учётной записи? +# Да +# +# 1.6 Какая команда генерирует SSH ключ для Доступа по SSH к репозиторию +# ssh-keygen -o +# +# 2.1 Вставьте сюда ссылку на ваше ответвление +# https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV +# +# 2.2 вставьте сюда ссылку на вашу ветку dev +# https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/tree/dev +# +# 2.4 вставьте номер из пункта 2 +# 416 +# +# 2.6 Выполните Merge pull request (Рисунок 116), вставьте сюда ссылку на ваш пул реквест +# https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/pull/1 +# +# 2.7 Вставьте сюда ссылку на закрытые пул реквесты +# https://github.com/SENATOROVAI/Data-Science-For-Beginners-from-scratch-SENATOROV/pulls?q=is%3Apr+is%3Aclosed +# +# 2.8 Как посмотреть какие файлы были в репозитории на момент определенного коммита? +# Переходим в список коммитов -> Выбираем нужный коммит -> Browse files +# +# Вставьте сюда ссылку на любой коммит +# https://github.com/rizespbya/Data-Science-For-Beginners-from-scratch-SENATOROV/commit/66a964b0227bc1989009974e6bed40e6c33a80f3 +# +# 2.9 как открыть запрос слияния, указывающий на другой запрос слияния и зачем это нужно? +# На странице ПР кнопка Edit. Затем изменить целевую ветку. +# +# 3. Напишите 8 пунктов, которые нужно сделать, чтобы внести вклад в чужой проект. +# 1. Создаем fork репозитория, в который хотим внести вклад. +# 2. Клонируем форкнутый репозиторий на локальный компьютер. +# 3. Находим ошибку в коде / место, которое можно улучшить. +# 4. Создаем Issue в оригинальном репозитории с понятным тайтлом, детальным описанием, по возможности прикладываем скриншоты, коды ошибок, трассировку и пр. +# 5. Вносим изменения в код. +# 6. Создаем коммит, используя ключевые слова closes и пр со ссылкой на issue. +# 7. Отправляем коммиты в форкнутый репозиторий. +# 8. Создаем ПР в оригинальный репозиторий. +# +# 3.1. Какие практики принято соблюдать при создании Pull Request чтобы закрыть автоматический issues? +# Какие практики принято соблюдать при создании commit чтобы закрыть автоматический issues? +# В тайтле указываем ссылку на issue или номер issue +# В описании используем ключевые слова со ссылкой на issue или с номером issue: +# -close +# -closes +# -closed +# -fix +# -fixes +# -fixed +# -resolve +# -resolves +# -resolved +# +# 3.2 Как отклонить/закрыть пул реквест? (предоставьте скриншот где это в гитхабе) +# Кнопка Close pull request +# ![image.png](attachment:image.png) +# +# 3.3 Перед отправкой пул реквеста нужно ли создавать ишьюс? +# Можно создать ПР и без issue, но для поддержания прозрачного и понятного процесса разработки как правило заранее создают issue, над исправлением которого работают. +# +# 3.4 В какой вкладке можно посмотреть список изменений который был в пул реквесте? +# Files changed +# +# 3.5 В какой вкладке находится страница обсуждений пул реквеста? +# Conversation +# +# 4 Можно ли открыть пул реквест, если вы ничего не вносили в FORK? +# Нет +# +# 4.1 Что нужно сделать чтобы открыть пул реквест? +# Запушать изменения в Github. Затем нажать кнопку New pull request +# +# 4.2 Что нужно сделать Если ваш Форк устарел? +# Обновить форк. +# Добавляем оригинальный репозиторий в remote +# git remote add upstream https://github.com/оригинал-автор/имя-репозитория.git +# +# После этого в подтягиваем изменения из оригинального репозитории: +# git fetch upstream +# +# И вливаем изменения в свою ветку: +# git merge upstream/main +# +# 4.3 Что нужно сделать если в пул реквесте имеются конфликты слияния? +# Обновить локальной свою ветку, подтянув изменения из оригинального репозитория. +# Разрешить конфликты. +# Отправить изменения в Github. +# +# Второй вариант - перебазировать свою ветку. +# +# 5. Что нужно сделать Для добавления отрывка кода в комментарии к ишьюсу? +# Использовать ```...```. В идеале с указанием языка, на котором написан код. +# +# 5.1 На какую клавишу нажать клавишу чтобы выделенный текст был включён как цитата в ваш комментарий? +# > +# +# 5.2 Как вставить картинку в ишьюс? +# Перетащить картинку на поле ввода комментария. Или в поле ввода вставить картинку из буфера. +# +# 6. Как понять что ваш форк устарел? +# В вебинтерфейсе появится уведомление "This branch is X commits behind [оригинальный репозиторий]:main." +# Или вручную обновить remote оригинального репозитория и с помощью git log посмотреть коммиты на ветке оригинального репозитория. +# +# 6.1 Как обновить форк? +# Добавляем оригинальный репозиторий в remote +# git remote add upstream https://github.com/оригинал-автор/имя-репозитория.git +# +# После этого в подтягиваем изменения из оригинального репозитории: +# git fetch upstream +# +# И вливаем изменения в свою ветку: +# git merge upstream/main +# +# 7. Как добавить участников в ваш репозиторий, чтобы команда могла работать над одним репозиторием? +# Settings -> Collaborators +# +# 8. Какой символ нужен для упоминания кого-либо? +# @ +# +# 8.1 Где находится Центр уведомлений, напишите ссылку +# https://github.com/notifications +# +# 9. Что такое и зачем нужен файл README +# Содержит описание проекта. А так же инструкции по установке зависимостей, запуску и использованию +# +# 9.1 Что такое и зачем нужен файл CONTRIBUTING +# Файл, описывающий правила и стандарты для open source работы над проектом +# +# 10. Как изменить основную ветку? +# Settings -> General -> Default branch +# +# 10.1 Как передать проект? какая кнопка? +# Кнопка "Transfer ownership" в Settings -> General +# +# 10.2 Что такое файл .gitignore? +# Файл, который содержит перечень файлов и папок (в том числе в виде шаблонов), которые не должны отслеживаться. diff --git a/log.ipynb b/log.ipynb new file mode 100644 index 00000000..17bc0f8f --- /dev/null +++ b/log.ipynb @@ -0,0 +1,57 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f543d276", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"Логирование уроков.\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "62ec06cd", + "metadata": {}, + "source": [ + "28/06 - 10/07\n", + "\n", + "1. Завел необходимые аккаунты (git, kaggle, ods, canva)\n", + "2. Настраивал VSCode (долго не запускался mypy)\n", + "3. Настроил ВПН" + ] + }, + { + "cell_type": "markdown", + "id": "1e35fdf9", + "metadata": {}, + "source": [ + "11/07\n", + "\n", + "1. Изучал систему отправки отчетов" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/log.py b/log.py new file mode 100644 index 00000000..e9dc1b63 --- /dev/null +++ b/log.py @@ -0,0 +1,11 @@ +"""Логирование уроков.""" + +# 28/06 - 10/07 +# +# 1. Завел необходимые аккаунты (git, kaggle, ods, canva) +# 2. Настраивал VSCode (долго не запускался mypy) +# 3. Настроил ВПН + +# 11/07 +# +# 1. Изучал систему отправки отчетов diff --git a/pyproject.toml b/pyproject.toml index 94f5ffc6..cc9c4c66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,3 +2,6 @@ formats = "ipynb,py:light" cell_metadata_filter = "-all" notebook_metadata_filter = "-all" + +[tool.black] +line-length = 79 diff --git a/tests/quiz/dir b/tests/quiz/dir deleted file mode 100644 index 8b137891..00000000 --- a/tests/quiz/dir +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/quiz/quiz.ipynb b/tests/quiz/quiz.ipynb new file mode 100644 index 00000000..30fac097 --- /dev/null +++ b/tests/quiz/quiz.ipynb @@ -0,0 +1,289 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "374fe137", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Квиз ишью Quiz #6.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/6\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "a9093402", + "metadata": {}, + "source": [ + "Список вопросов к видео https://youtu.be/hW_7hodGxVU?si=tCEVs00xGt2q61eW (АЛГОРИТМ ПРИНЯТИЯ И ОТДАЧИ ДОМАШКИ):\n", + " \n", + "(По желанию )В ответе подробно всё опишите и обязательно нужно указывать тайм код из видео где я это сказал, по желанию, дополнительно прикладываем скриншот из видео.\n", + "Если вы знаете ответы на вопросы из Вашего опыта, то таймкоды из видео не надо указывать и т.д.\n", + "\n", + "1) Как понять, что домашка пришла?\n", + "Чат HomeWork. Сообщение вида:\n", + "\"@ник прими пул\"\n", + "00:05\n", + "\n", + "2) Как принять домашку?\n", + "В Github Desktop делаем fetch origin\n", + "Смотрим подтянувшиеся коммиты (последний или несколько последних).\n", + "Затем \"Open in VSCode\".\n", + "00:17\n", + "\n", + "3) Зачем нужна кнопка history и какие функции появляются при нажатии правой кнопки мыши на коммит?\n", + "Кнопка history открывает историю коммитов. При клике правой кнопкой на коммит открывается контекстное меню, в котором доступны операции с коммитом: Reset, Revert, Amend, View on Github и пр.\n", + "00:33\n", + "\n", + "3.1) Где брать ссылку на коммит? куда её отправлять? \n", + "Ссылку на коммит берем в github на странице коммита.\n", + "Скидываем в чат HomeWork с припиской \"Коммит отдал\"\n", + "03:59\n", + "\n", + "4) Что такое файл лога?\n", + "Файл log.ipynb надо заполнять в конце каждого урока. Заносить информацию о том, что было сделано.\n", + "\n", + "4.1) Когда нужно его пушить?\n", + "Понедельник, среда, пятница\n", + "\n", + "5) Что такое интерпретатор? \n", + "Программа, которая построчно выполняет код\n", + "02:49\n", + "\n", + "6) Где можно выбрать интерпретатор?\n", + "При первом запуске ячейки вверху появляется список интерпретаторов, из которого нужно выбрать нужный (anaconda)\n", + "Так же интерпретатор можно выбрать кликом на кнопку в правом верхнем углу \"Select Kernel\"\n", + "02:55\n", + "\n", + "7) Что такое модуль?\n", + "В контексте Python это файл *.py или *.ipynb\n", + "08:50\n", + "\n", + "8) Как создать и отправить коммит?\n", + "Github Desktop -> Changes -> Заполняем заголовок коммита и описание -> Commit to main -> Push origin\n", + "03:13\n", + "\n", + "9) Как посмотреть что коммит точно отправлен и находится в github?\n", + "History -> Правой кнопкой на коммит -> View on Github\n", + "03:50\n", + "\n", + "10) Какая команда показывает что код не прошёл проверки на ошибки? \n", + "pre-commit run --all-files\n", + "05:55\n", + "\n", + "10.1) Напишите список линтеров которые используются для проверки кода и дайте их краткую характеристику.\n", + "docformatter - форматирует docstrings.\n", + "Add trailing commas - добавляет запятые в конце списков/словарей/аргументов.\n", + "black - автоматический форматтер кода.\n", + "nbqa-black - применяет black для файлов *.ipynb.\n", + "pyupgrade - обновляет синтаксис Python до более современного.\n", + "nbqu-pyupgrade - применяет pyupgrade для файлов *.ipynb.\n", + "isort - сортирует и группирует импорты.\n", + "nbqu-isort - применяет isort для файлов *.ipynb.\n", + "blacken-docs - применяет Black к коду внутри документации.\n", + "trim trailing whitespaces - удаляет пробелы в конце строк.\n", + "check yaml - проверяет синтаксис YAML-файлов.\n", + "debug statements (python) - ищет оставленные print(), pdb.set_trace(), breakpoint() и т.п.\n", + "python tests naming - проверяет, что тесты названы по соглашению (например, test_*.py, Test*, test_*())\n", + "fix requirements.txt - нормализует/исправляет requirements.txt.\n", + "codespell - орфографические ищет ошибки в коде и комментариях.\n", + "flake8 - проверяет стиль кода и ошибки.\n", + "nbqa-flake8 - применяет flake8 для файлов *.ipynb.\n", + "mypy - статический анализ типов.\n", + "nbqa-mypy - применяет mypy для файлов *.ipynb.\n", + "pylint - мощный линтер, который проверяет стиль, ошибки, архитектура, дублирование кода.\n", + "nbqa-pylint - применяет pylint для файлов *.ipynb.\n", + "nbqa-pydocstyle - проверяет соответствие docstrings стандарту PEP 257 для файлов *.ipynb.\n", + "05:20\n", + "\n", + "11) Как узнать какой именно линтер не прошёл проверку?\n", + "В логах в Терминале после выполнения команды pre-commit run --all-files.\n", + "06:30\n", + "\n", + "12) Линтер Pylint видит markdown?\n", + "Нет\n", + "07:10\n", + "\n", + "13) Номер ячейки в терминале и номер ячейки в vs code может отличаться? в каком случае?\n", + "Может. Если в файле ipynb есть ячейки маркдаун, линтер их игнорирует.\n", + "\n", + "14) Где посмотреть номер ячейки в vscode?\n", + "В нижней панели VSCode\n", + "07:20\n", + "\n", + "15) В каком формате ipynb отправляется в гитхаб? причём здесь JSON?\n", + "В формате JSON\n", + "\n", + "16) Где посмотреть в какой ячейке ошибка?\n", + "В логах в Терминале после выполнения команды pre-commit run --all-files\n", + "06:39\n", + "\n", + "17) Как запустить терминал?\n", + "В VSCode: Terminal -> New Terminal\n", + "05:55\n", + "\n", + "18) Что такое линтер?\n", + "Статический анализатор кода, который проверяет наш код на ошибки.\n", + "06:13\n", + "\n", + "19) В какой сайт нужно вставлять код ошибки если ошибка связана с pylint?\n", + "https://pylint.readthedocs.io/\n", + "08:13\n", + "\n", + "20) Секция pydocstyle в большинстве случае автоматический закрывается после исправления ошибок в каком линтере?\n", + "Да\n", + "10:13\n", + "\n", + "\n", + "21) Что такое описание модуля? Оно должно отражать информацию о том что находится в модуле?\n", + "Да, это информация о том, что находится в модуле.\n", + "08:45\n", + "\n", + "21.1) С какой git команды начинается утро программиста?\n", + "git pull / git fetch\n", + "\n", + "22) После внесения изменений в файлах, кнопка open in vs code пропадает в кошке, как по другому открыть vs code из кошки?\n", + "Правкой кнопкой мыши на Current repository -> Open in VSCode\n", + "14:13\n", + "\n", + "23) Что такое stash? \n", + "Это \"тайник\", куда мы временно можем спрятать изменения, сделанные в файлах. При этом файлы откатываются до состояния предыдущего коммита.\n", + "\n", + "23.1) Как сохранить стэш?\n", + " Вначале добавляем файлы в Stage (например, git add .)\n", + " Затем:\n", + " git stash - сохранить в stash\n", + " git stash save \"NAME_STASH\" - сохранить в stash с указанием алиаса\n", + "\n", + " Кнопка в vs code:\n", + " Вкладка \"Source Control\" -> Кнопка \"Стрелка с +\" Stash All Changes.\n", + " Или три точки -> Контекстное меню -> Пункт Stash\n", + "\n", + "23.2) Как восстановить стэш(подсказка: https://t.me/c/1937296927/3602/25747)?:\n", + " git stash apply \"NUMBER_STASH\" - извлечь stash по указанному имени без его удаления из stash\n", + " git stash pop - извлекает stash и удаляет его из stash\n", + " \n", + "\n", + "23.3) Различие между стэшем и коммитом. \n", + " Коммит - фиксирует изменения в истории. Можно отправить в удаленный репозиторий и поделиться с коллегами.\n", + " Stash - временно \"скрывает\" незакоммиченные локальные изменения\n", + "\n", + "23.4) Как просмотреть список сохраненных стэшей? \n", + "git stash list\n", + "\n", + "23.5) Как удалить стэш? \n", + "git stash pop\n", + "git stash clear\n", + "\n", + "23.6) Практические примеры использования стэша. \n", + " Чаще всего используется, чтобы сделать git pull и обновить ветку. Реже для быстрого отката незакомиченных изменений.\n", + "\n", + "24) Где посмотреть что есть конфликт в файлах?\n", + "В Github Desktop красный треугольник с восклицательным знаком. Или в VSCode во вкладке Source Control\n", + "16:17\n", + "\n", + "24.1) Когда он появляется?\n", + "Он появляется во время ПР или во время подтягивания изменений в ветку.\n", + "16:17\n", + "\n", + "25) Как решить конфликт в файлах?\n", + "Accept Current Change\n", + "Accept Incoming Change\n", + "Accept Both Changes\n", + "\n", + "Или редактирование вручную\n", + "16:40\n", + "\n", + "26) Напишиие правильное утверждение\n", + "-Зелёное то что пришло с гитхаба и синее локальные изменения или синее то что пришло с гитхаба и зелёное это локальные изменения \n", + "Зелёное то что пришло с гитхаба и синее локальные изменения\n", + "16:33\n", + "\n", + "27) Если мы работаем в одном файле, можно ли принять pull после того как вы спрячете в стэш свои изменения? \n", + "Да\n", + "\n", + "27.1) Что может произойти когда stash восстановите после принятия pull?\n", + "Может произойти Merge Conflict\n", + "\n", + "28) Сколько способов решения конфликтов было показано в видео? Напишите ЧИСЛО и укажите их способы.\n", + "Accept Current Change\n", + "Accept Incoming Change\n", + "Accept Both Changes\n", + "\n", + "Или редактирование вручную\n", + "16:40\n", + "\n", + "29) Что делает кнопка complete merge?\n", + "Завершает merge после разрешения merge-конфликтов\n", + "\n", + "30) В какой чат нужно писать если остались вопросы?\n", + "HELP ME\n", + "18:31\n", + "\n", + "31) Что такое FORK? Зачем его делают? \n", + "Копирование к себе чужого репозитория\n", + "19:05\n", + "\n", + "32) Как скачать форкнутый репозиторий на локальный компьютер?\n", + "Через Github Desktop -> Clone repository\n", + "Или через командную строку git clone\n", + "19:40\n", + "\n", + "33) С какой вероятностью ваши ошибки были уже решены? и кто их решил?\n", + "Скорее всего, уже были решены. Если речь о стороннем коде - другими разработчиками. Если речь об ошибках во время обучения - другими студентами, Русланом.\n", + "\n", + "34) Как создать файл в vs code?\n", + "Меню File -> New File\n", + "Кнопка New File в VSCode (листок с +)\n", + "\n", + "35) Файл лога нужно заполнять в конце каждого урока?\n", + "Да\n", + "==================\n", + "\n", + "Дополнительные вопросы:\n", + "1)Какая команда конвертирует файл в py из ipynb? \n", + "jupytext notebook.ipynb --to py\n", + "\n", + "2) Что такое пакетный менеджер? Вы пользуетесь пакетным менеджером conda или pip? Какой лучше использовать для дата сайнс?\n", + "Это программа для менеджмента зависимостей. Использую pip. Лучше pip\n", + "\n", + "3) Почему расширение py лучше чем ipynb?\n", + "Более чистый и читаемый код.\n", + "Возможность сразу импортировать и использовать в другом коде.\n", + "\n", + "4) Что такое pep8? \n", + "Руководство по стилю кода Python.\n", + "\n", + "4.1) линтеры проверяют на соблюдение pep8?\n", + "Да\n", + "\n", + "4.2) Какая нотация используется для создания переменных? \n", + "snake_case\n", + "\n", + "4.3) Может ли переменная состоять из одной буквы например андерскор \"_\" ?\n", + "Да\n", + "\n", + "4.4) Зачем и где мы используем андерскор _\n", + "Для неиспользуемых переменных / неиспользуемых позиционных аргументов.\n", + "\n", + "4.5) По PEP8 допустима переменная в одну букву?\n", + "Да\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/quiz/quiz.py b/tests/quiz/quiz.py new file mode 100644 index 00000000..aebca721 --- /dev/null +++ b/tests/quiz/quiz.py @@ -0,0 +1,259 @@ +"""Квиз ишью Quiz #6. + +https://github.com/SENATOROVAI/intro-cs/issues/6 +""" + +# Список вопросов к видео https://youtu.be/hW_7hodGxVU?si=tCEVs00xGt2q61eW (АЛГОРИТМ ПРИНЯТИЯ И ОТДАЧИ ДОМАШКИ): +# +# (По желанию )В ответе подробно всё опишите и обязательно нужно указывать тайм код из видео где я это сказал, по желанию, дополнительно прикладываем скриншот из видео. +# Если вы знаете ответы на вопросы из Вашего опыта, то таймкоды из видео не надо указывать и т.д. +# +# 1) Как понять, что домашка пришла? +# Чат HomeWork. Сообщение вида: +# "@ник прими пул" +# 00:05 +# +# 2) Как принять домашку? +# В Github Desktop делаем fetch origin +# Смотрим подтянувшиеся коммиты (последний или несколько последних). +# Затем "Open in VSCode". +# 00:17 +# +# 3) Зачем нужна кнопка history и какие функции появляются при нажатии правой кнопки мыши на коммит? +# Кнопка history открывает историю коммитов. При клике правой кнопкой на коммит открывается контекстное меню, в котором доступны операции с коммитом: Reset, Revert, Amend, View on Github и пр. +# 00:33 +# +# 3.1) Где брать ссылку на коммит? куда её отправлять? +# Ссылку на коммит берем в github на странице коммита. +# Скидываем в чат HomeWork с припиской "Коммит отдал" +# 03:59 +# +# 4) Что такое файл лога? +# Файл log.ipynb надо заполнять в конце каждого урока. Заносить информацию о том, что было сделано. +# +# 4.1) Когда нужно его пушить? +# Понедельник, среда, пятница +# +# 5) Что такое интерпретатор? +# Программа, которая построчно выполняет код +# 02:49 +# +# 6) Где можно выбрать интерпретатор? +# При первом запуске ячейки вверху появляется список интерпретаторов, из которого нужно выбрать нужный (anaconda) +# Так же интерпретатор можно выбрать кликом на кнопку в правом верхнем углу "Select Kernel" +# 02:55 +# +# 7) Что такое модуль? +# В контексте Python это файл *.py или *.ipynb +# 08:50 +# +# 8) Как создать и отправить коммит? +# Github Desktop -> Changes -> Заполняем заголовок коммита и описание -> Commit to main -> Push origin +# 03:13 +# +# 9) Как посмотреть что коммит точно отправлен и находится в github? +# History -> Правой кнопкой на коммит -> View on Github +# 03:50 +# +# 10) Какая команда показывает что код не прошёл проверки на ошибки? +# pre-commit run --all-files +# 05:55 +# +# 10.1) Напишите список линтеров которые используются для проверки кода и дайте их краткую характеристику. +# docformatter - форматирует docstrings. +# Add trailing commas - добавляет запятые в конце списков/словарей/аргументов. +# black - автоматический форматтер кода. +# nbqa-black - применяет black для файлов *.ipynb. +# pyupgrade - обновляет синтаксис Python до более современного. +# nbqu-pyupgrade - применяет pyupgrade для файлов *.ipynb. +# isort - сортирует и группирует импорты. +# nbqu-isort - применяет isort для файлов *.ipynb. +# blacken-docs - применяет Black к коду внутри документации. +# trim trailing whitespaces - удаляет пробелы в конце строк. +# check yaml - проверяет синтаксис YAML-файлов. +# debug statements (python) - ищет оставленные print(), pdb.set_trace(), breakpoint() и т.п. +# python tests naming - проверяет, что тесты названы по соглашению (например, test_*.py, Test*, test_*()) +# fix requirements.txt - нормализует/исправляет requirements.txt. +# codespell - орфографические ищет ошибки в коде и комментариях. +# flake8 - проверяет стиль кода и ошибки. +# nbqa-flake8 - применяет flake8 для файлов *.ipynb. +# mypy - статический анализ типов. +# nbqa-mypy - применяет mypy для файлов *.ipynb. +# pylint - мощный линтер, который проверяет стиль, ошибки, архитектура, дублирование кода. +# nbqa-pylint - применяет pylint для файлов *.ipynb. +# nbqa-pydocstyle - проверяет соответствие docstrings стандарту PEP 257 для файлов *.ipynb. +# 05:20 +# +# 11) Как узнать какой именно линтер не прошёл проверку? +# В логах в Терминале после выполнения команды pre-commit run --all-files. +# 06:30 +# +# 12) Линтер Pylint видит markdown? +# Нет +# 07:10 +# +# 13) Номер ячейки в терминале и номер ячейки в vs code может отличаться? в каком случае? +# Может. Если в файле ipynb есть ячейки маркдаун, линтер их игнорирует. +# +# 14) Где посмотреть номер ячейки в vscode? +# В нижней панели VSCode +# 07:20 +# +# 15) В каком формате ipynb отправляется в гитхаб? причём здесь JSON? +# В формате JSON +# +# 16) Где посмотреть в какой ячейке ошибка? +# В логах в Терминале после выполнения команды pre-commit run --all-files +# 06:39 +# +# 17) Как запустить терминал? +# В VSCode: Terminal -> New Terminal +# 05:55 +# +# 18) Что такое линтер? +# Статический анализатор кода, который проверяет наш код на ошибки. +# 06:13 +# +# 19) В какой сайт нужно вставлять код ошибки если ошибка связана с pylint? +# https://pylint.readthedocs.io/ +# 08:13 +# +# 20) Секция pydocstyle в большинстве случае автоматический закрывается после исправления ошибок в каком линтере? +# Да +# 10:13 +# +# +# 21) Что такое описание модуля? Оно должно отражать информацию о том что находится в модуле? +# Да, это информация о том, что находится в модуле. +# 08:45 +# +# 21.1) С какой git команды начинается утро программиста? +# git pull / git fetch +# +# 22) После внесения изменений в файлах, кнопка open in vs code пропадает в кошке, как по другому открыть vs code из кошки? +# Правкой кнопкой мыши на Current repository -> Open in VSCode +# 14:13 +# +# 23) Что такое stash? +# Это "тайник", куда мы временно можем спрятать изменения, сделанные в файлах. При этом файлы откатываются до состояния предыдущего коммита. +# +# 23.1) Как сохранить стэш? +# Вначале добавляем файлы в Stage (например, git add .) +# Затем: +# git stash - сохранить в stash +# git stash save "NAME_STASH" - сохранить в stash с указанием алиаса +# +# Кнопка в vs code: +# Вкладка "Source Control" -> Кнопка "Стрелка с +" Stash All Changes. +# Или три точки -> Контекстное меню -> Пункт Stash +# +# 23.2) Как восстановить стэш(подсказка: https://t.me/c/1937296927/3602/25747)?: +# git stash apply "NUMBER_STASH" - извлечь stash по указанному имени без его удаления из stash +# git stash pop - извлекает stash и удаляет его из stash +# +# +# 23.3) Различие между стэшем и коммитом. +# Коммит - фиксирует изменения в истории. Можно отправить в удаленный репозиторий и поделиться с коллегами. +# Stash - временно "скрывает" незакоммиченные локальные изменения +# +# 23.4) Как просмотреть список сохраненных стэшей? +# git stash list +# +# 23.5) Как удалить стэш? +# git stash pop +# git stash clear +# +# 23.6) Практические примеры использования стэша. +# Чаще всего используется, чтобы сделать git pull и обновить ветку. Реже для быстрого отката незакомиченных изменений. +# +# 24) Где посмотреть что есть конфликт в файлах? +# В Github Desktop красный треугольник с восклицательным знаком. Или в VSCode во вкладке Source Control +# 16:17 +# +# 24.1) Когда он появляется? +# Он появляется во время ПР или во время подтягивания изменений в ветку. +# 16:17 +# +# 25) Как решить конфликт в файлах? +# Accept Current Change +# Accept Incoming Change +# Accept Both Changes +# +# Или редактирование вручную +# 16:40 +# +# 26) Напишиие правильное утверждение +# -Зелёное то что пришло с гитхаба и синее локальные изменения или синее то что пришло с гитхаба и зелёное это локальные изменения +# Зелёное то что пришло с гитхаба и синее локальные изменения +# 16:33 +# +# 27) Если мы работаем в одном файле, можно ли принять pull после того как вы спрячете в стэш свои изменения? +# Да +# +# 27.1) Что может произойти когда stash восстановите после принятия pull? +# Может произойти Merge Conflict +# +# 28) Сколько способов решения конфликтов было показано в видео? Напишите ЧИСЛО и укажите их способы. +# Accept Current Change +# Accept Incoming Change +# Accept Both Changes +# +# Или редактирование вручную +# 16:40 +# +# 29) Что делает кнопка complete merge? +# Завершает merge после разрешения merge-конфликтов +# +# 30) В какой чат нужно писать если остались вопросы? +# HELP ME +# 18:31 +# +# 31) Что такое FORK? Зачем его делают? +# Копирование к себе чужого репозитория +# 19:05 +# +# 32) Как скачать форкнутый репозиторий на локальный компьютер? +# Через Github Desktop -> Clone repository +# Или через командную строку git clone +# 19:40 +# +# 33) С какой вероятностью ваши ошибки были уже решены? и кто их решил? +# Скорее всего, уже были решены. Если речь о стороннем коде - другими разработчиками. Если речь об ошибках во время обучения - другими студентами, Русланом. +# +# 34) Как создать файл в vs code? +# Меню File -> New File +# Кнопка New File в VSCode (листок с +) +# +# 35) Файл лога нужно заполнять в конце каждого урока? +# Да +# ================== +# +# Дополнительные вопросы: +# 1)Какая команда конвертирует файл в py из ipynb? +# jupytext notebook.ipynb --to py +# +# 2) Что такое пакетный менеджер? Вы пользуетесь пакетным менеджером conda или pip? Какой лучше использовать для дата сайнс? +# Это программа для менеджмента зависимостей. Использую pip. Лучше pip +# +# 3) Почему расширение py лучше чем ipynb? +# Более чистый и читаемый код. +# Возможность сразу импортировать и использовать в другом коде. +# +# 4) Что такое pep8? +# Руководство по стилю кода Python. +# +# 4.1) линтеры проверяют на соблюдение pep8? +# Да +# +# 4.2) Какая нотация используется для создания переменных? +# snake_case +# +# 4.3) Может ли переменная состоять из одной буквы например андерскор "_" ? +# Да +# +# 4.4) Зачем и где мы используем андерскор _ +# Для неиспользуемых переменных / неиспользуемых позиционных аргументов. +# +# 4.5) По PEP8 допустима переменная в одну букву? +# Да +# diff --git a/tests/quiz/quiz2.ipynb b/tests/quiz/quiz2.ipynb new file mode 100644 index 00000000..cd3d7702 --- /dev/null +++ b/tests/quiz/quiz2.ipynb @@ -0,0 +1,198 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "b286b6de", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Квиз2 ишью Quiz #6.\n", + "\n", + "https://github.com/SENATOROVAI/intro-cs/issues/6\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "f7ab0a33", + "metadata": {}, + "source": [ + "Список вопросов к видео https://youtu.be/Si9MfV8uJ-0?si=JXHe-tsgOEwSTI5E (НАСТРОЙКА VSCODE, перенос строк, линтеры, работа с ячейками):\n", + "\n", + "(По желанию)В ответе подробно всё опишите и обязательно нужно указывать тайм код из видео где я это сказал, по желанию, дополнительно прикладываем скриншот из видео.\n", + "Если вы знаете ответы на вопросы из Вашего опыта, то таймкоды из видео не надо указывать и т.д.\n", + "\n", + "1. Как включить автосохранение данных в VSCODE?\n", + "File -> Auto Save\n", + "00:09\n", + "\n", + "2. Как настроить перенос строки? \n", + "Preferences -> Settings -> В поиск пишем \"wrap\"\n", + "Word Wrap выставляем on\n", + "Word Wrap Column выставляем 79\n", + "00:15\n", + "\n", + "Если надо изменить длину строки только для языка Python, то переходим в json с настройками VSCode (иконка в правом верхнем углу Open Settings (JSON)). И добавляем в настройки блок кода:\n", + " \"[python]\": {\n", + " \"editor.wordWrap\": \"wordWrapColumn\",\n", + " \"editor.wordWrapColumn\": 79,\n", + " },\n", + "\n", + "\n", + "3. Сколько символов по pep8 разрешено на строке?\n", + "79\n", + "00:23\n", + "\n", + "4. Какие способы переноса строк показаны в видео:\n", + "01:40\n", + "\n", + "Показаны способы:\n", + "4.6 Сложение строк с помощью +\n", + "03:40\n", + "\n", + "4.1 Строки с использованием обратного слэша (\\)\n", + "\n", + "string_continued = \"This is a long string that we want to \" \\\n", + " \"split across multiple lines.\"\n", + "print(string_continued)\n", + "\n", + "4.2 Тройные кавычки (''' или \"\"\") \n", + "\n", + "multi_line_string = \"\"\"This is a string that spans\n", + "multiple lines. You can write freely\n", + "and it will keep the line breaks.\"\"\"\n", + "print(multi_line_string)\n", + "\n", + "4.3 Создание списка строк и объединение с помощью join\n", + "\n", + "strings = [\n", + " \"This is the first line.\",\n", + " \"This is the second line.\",\n", + " \"This is the third line.\"\n", + "]\n", + "result = \"\\n\".join(strings) # Используем перенос строк '\\n'\n", + "print(result)\n", + "\n", + "4.4 Использование круглых скобок для продолжения строки\n", + "long_string = (\n", + " \"This is a very long string that I would like to \"\n", + " \"continue on the next line.\"\n", + ")\n", + "print(long_string)\n", + "\n", + "4.5 Форматированные строки (f-строки) с использованием скобок\n", + "letter_a = 5\n", + "letter_b = 6\n", + "product_ab = letter_a * letter_b\n", + "\n", + "message = (\n", + " f\"when {letter_a} is multiplied by {letter_b}, \"\n", + " f\"the result is {product_ab}\"\n", + ")\n", + "print(message)\n", + "\n", + "4.6 Сложение строк с помощью +\n", + "string_part1 = \"This is the first part, \"\n", + "string_part2 = \"and this is the second part.\"\n", + "full_string = string_part1 + string_part2\n", + "print(full_string)\n", + "\n", + "5. Проверка на ошибки c помощью кнопки problems, где она находится?\n", + "Вкладка Problems в терминале\n", + "04:50\n", + "\n", + "6. Где в vscode находится клиент гита? как в нём отправить коммит? как принять домашку?\n", + "На панели слева кнопка Source Control (три кружочка)\n", + "06:23\n", + "\n", + "\n", + "7. Что такое GIT? он локальный? В нём можно посмотреть историю изменений файлов и вернуться к любому коммиту?\n", + "Система контроля версий. Гит работает локально. Но с использованием сервисов Github, Gitlab, Bitbacket и др. позволяет организовать командную работу над проектом. В нем можно посмотреть историю изменений файлов и вернуться к любому коммиту.\n", + "06:59\n", + "\n", + "8. Как вставить картинку в маркдаун?\n", + "\n", + "07:57\n", + "\n", + "9. Где посмотреть длину строки в vs code?\n", + "Скопировать строку в любой файл с расширением *.py, выделить строку и посмотреть количество выделенных символов на нижней панели VSCode\n", + "01:30\n", + "\n", + "10. Как поменять тип ячейки с питона на маркдаун?\n", + "Кликнуть в правом нижнем углу ячейки.\n", + "07:44\n", + "\n", + "11. Как запустить сразу все ячейки в юпитере?\n", + "Кнопка Run All\n", + "08:29\n", + "\n", + "12. Как изменить размер картинки в юпитере? Нужно для этого знать HTML?\n", + "Нужно знать теги HTML. Изменить размер картинки можно с помощью аттрибутов width и height. Или аттрибут sizes\n", + "08:11\n", + "\n", + "13. Какой хоткей чтобы запустить ячейку с смещением на следующую?\n", + "Shift + Enter\n", + "08:43\n", + "\n", + "14. Как включить отображение номеров строк в юпитере(Cell line numbers)?\n", + "Кнопка справа вверху \"три точки\" -> пункт в контекстном меню (через три точки) \"Show Cell Line Numbers\".\n", + "08:59\n", + "\n", + "15. Что такое \"Go To\" чем это полезно? Как перейти сразу на ошибочную ячейку?\n", + "С помощью кнопки \"Go To\" можно перейти сразу на ошибочную ячейку.\n", + "09:20\n", + "\n", + "16. Как очистить вывод ячеек которые уже запущены?\n", + "Кнопка \"Clear All Outputs\"\n", + "10:59\n", + "\n", + "17. Как работать одновременно в нескольких файлах в VSCODE? Что такое SPLIT?\n", + "Кнопка \"Split Editor Right\" в верхней правой части VSCode.\n", + "Разделяет рабочую область на две или более частей, что дает возможность держать открытыми сразу несколько файлов.\n", + "10:53\n", + "\n", + "18. Каким сочетанием убирается левый сайдбар?\n", + "Ctrl + B\n", + "11:21\n", + "\n", + "19. Кнопка два листочка это наши локальные файлы?\n", + "Кнопка с двумя листочками на левой панели\n", + "07:19\n", + "\n", + "20. Какая ошибка появилась в трассировке при запуске всех ячеек DICT или LIST?\n", + "NameError: name 'Dict' is not defined\n", + "10:07\n", + "\n", + "21. Вы ознакомились с https://t.me/c/1937296927/832/19307? и https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet?\n", + "Да\n", + "\n", + "22. Что такое валидация?\n", + "Проверка корректности введенных данных.\n", + "09:49\n", + "\n", + "23. Что такое трассировка ошибки?\n", + "Визуализация ошибки. Выявление содержания ошибки с помощью логов.\n", + "10:00\n", + "\n", + "24. Что значит отвалился интерпритатор?\n", + "Ошибка в коде. Программа не может продолжать выполнение.\n", + "10:27\n", + "\n", + "Отвечаете на вопросы в вашем редакторе кода." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/quiz/quiz2.py b/tests/quiz/quiz2.py new file mode 100644 index 00000000..001342f7 --- /dev/null +++ b/tests/quiz/quiz2.py @@ -0,0 +1,167 @@ +"""Квиз2 ишью Quiz #6. + +https://github.com/SENATOROVAI/intro-cs/issues/6 +""" + +# Список вопросов к видео https://youtu.be/Si9MfV8uJ-0?si=JXHe-tsgOEwSTI5E (НАСТРОЙКА VSCODE, перенос строк, линтеры, работа с ячейками): +# +# (По желанию)В ответе подробно всё опишите и обязательно нужно указывать тайм код из видео где я это сказал, по желанию, дополнительно прикладываем скриншот из видео. +# Если вы знаете ответы на вопросы из Вашего опыта, то таймкоды из видео не надо указывать и т.д. +# +# 1. Как включить автосохранение данных в VSCODE? +# File -> Auto Save +# 00:09 +# +# 2. Как настроить перенос строки? +# Preferences -> Settings -> В поиск пишем "wrap" +# Word Wrap выставляем on +# Word Wrap Column выставляем 79 +# 00:15 +# +# Если надо изменить длину строки только для языка Python, то переходим в json с настройками VSCode (иконка в правом верхнем углу Open Settings (JSON)). И добавляем в настройки блок кода: +# "[python]": { +# "editor.wordWrap": "wordWrapColumn", +# "editor.wordWrapColumn": 79, +# }, +# +# +# 3. Сколько символов по pep8 разрешено на строке? +# 79 +# 00:23 +# +# 4. Какие способы переноса строк показаны в видео: +# 01:40 +# +# Показаны способы: +# 4.6 Сложение строк с помощью + +# 03:40 +# +# 4.1 Строки с использованием обратного слэша (\) +# +# string_continued = "This is a long string that we want to " \ +# "split across multiple lines." +# print(string_continued) +# +# 4.2 Тройные кавычки (''' или """) +# +# multi_line_string = """This is a string that spans +# multiple lines. You can write freely +# and it will keep the line breaks.""" +# print(multi_line_string) +# +# 4.3 Создание списка строк и объединение с помощью join +# +# strings = [ +# "This is the first line.", +# "This is the second line.", +# "This is the third line." +# ] +# result = "\n".join(strings) # Используем перенос строк '\n' +# print(result) +# +# 4.4 Использование круглых скобок для продолжения строки +# long_string = ( +# "This is a very long string that I would like to " +# "continue on the next line." +# ) +# print(long_string) +# +# 4.5 Форматированные строки (f-строки) с использованием скобок +# letter_a = 5 +# letter_b = 6 +# product_ab = letter_a * letter_b +# +# message = ( +# f"when {letter_a} is multiplied by {letter_b}, " +# f"the result is {product_ab}" +# ) +# print(message) +# +# 4.6 Сложение строк с помощью + +# string_part1 = "This is the first part, " +# string_part2 = "and this is the second part." +# full_string = string_part1 + string_part2 +# print(full_string) +# +# 5. Проверка на ошибки c помощью кнопки problems, где она находится? +# Вкладка Problems в терминале +# 04:50 +# +# 6. Где в vscode находится клиент гита? как в нём отправить коммит? как принять домашку? +# На панели слева кнопка Source Control (три кружочка) +# 06:23 +# +# +# 7. Что такое GIT? он локальный? В нём можно посмотреть историю изменений файлов и вернуться к любому коммиту? +# Система контроля версий. Гит работает локально. Но с использованием сервисов Github, Gitlab, Bitbacket и др. позволяет организовать командную работу над проектом. В нем можно посмотреть историю изменений файлов и вернуться к любому коммиту. +# 06:59 +# +# 8. Как вставить картинку в маркдаун? +# +# 07:57 +# +# 9. Где посмотреть длину строки в vs code? +# Скопировать строку в любой файл с расширением *.py, выделить строку и посмотреть количество выделенных символов на нижней панели VSCode +# 01:30 +# +# 10. Как поменять тип ячейки с питона на маркдаун? +# Кликнуть в правом нижнем углу ячейки. +# 07:44 +# +# 11. Как запустить сразу все ячейки в юпитере? +# Кнопка Run All +# 08:29 +# +# 12. Как изменить размер картинки в юпитере? Нужно для этого знать HTML? +# Нужно знать теги HTML. Изменить размер картинки можно с помощью аттрибутов width и height. Или аттрибут sizes +# 08:11 +# +# 13. Какой хоткей чтобы запустить ячейку с смещением на следующую? +# Shift + Enter +# 08:43 +# +# 14. Как включить отображение номеров строк в юпитере(Cell line numbers)? +# Кнопка справа вверху "три точки" -> пункт в контекстном меню (через три точки) "Show Cell Line Numbers". +# 08:59 +# +# 15. Что такое "Go To" чем это полезно? Как перейти сразу на ошибочную ячейку? +# С помощью кнопки "Go To" можно перейти сразу на ошибочную ячейку. +# 09:20 +# +# 16. Как очистить вывод ячеек которые уже запущены? +# Кнопка "Clear All Outputs" +# 10:59 +# +# 17. Как работать одновременно в нескольких файлах в VSCODE? Что такое SPLIT? +# Кнопка "Split Editor Right" в верхней правой части VSCode. +# Разделяет рабочую область на две или более частей, что дает возможность держать открытыми сразу несколько файлов. +# 10:53 +# +# 18. Каким сочетанием убирается левый сайдбар? +# Ctrl + B +# 11:21 +# +# 19. Кнопка два листочка это наши локальные файлы? +# Кнопка с двумя листочками на левой панели +# 07:19 +# +# 20. Какая ошибка появилась в трассировке при запуске всех ячеек DICT или LIST? +# NameError: name 'Dict' is not defined +# 10:07 +# +# 21. Вы ознакомились с https://t.me/c/1937296927/832/19307? и https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet? +# Да +# +# 22. Что такое валидация? +# Проверка корректности введенных данных. +# 09:49 +# +# 23. Что такое трассировка ошибки? +# Визуализация ошибки. Выявление содержания ошибки с помощью логов. +# 10:00 +# +# 24. Что значит отвалился интерпритатор? +# Ошибка в коде. Программа не может продолжать выполнение. +# 10:27 +# +# Отвечаете на вопросы в вашем редакторе кода.