{ "cells": [ { "cell_type": "markdown", "id": "9fdeb776-f17c-42a5-84fb-765c19d5c6c3", "metadata": { "tags": [] }, "source": [ "# Learning materials, sources, references" ] }, { "cell_type": "markdown", "id": "b3969792-c3ea-490b-8f5a-463da33ce0c4", "metadata": { "tags": [] }, "source": [ "## Python-specific" ] }, { "cell_type": "markdown", "id": "4ada8a3b-89e3-41ca-8748-9c8746b0b563", "metadata": {}, "source": [ "* https://docs.python.org/3/\n", " * https://docs.python.org/3/tutorial/ read through this\n", " * https://docs.python.org/3/library/ before you start implementing something or looking for pip packages\n", " * https://docs.python.org/3/library/ctypes.html to load C-like DLLs\n", " * https://docs.python.org/3/extending/ if you need C\n", " * https://docs.python.org/3/whatsnew/ there's a lot of important stuff posted there, actually (e.g. `match` keyword)\n", " * https://docs.python.org/3/reference/ in-depth about python itself\n", " * https://docs.python.org/3/reference/datamodel.html the most important part from that reference\n", "* https://wiki.python.org/moin/FrontPage at the moment of writing, haven't read much of it" ] }, { "cell_type": "markdown", "id": "07390193-fdde-4bee-aec4-f5b3227dea06", "metadata": { "tags": [] }, "source": [ "# typing\n", "Even a dynamically typed language can sometimes, at least, declare types. Python has built-in support for that via annotations." ] }, { "cell_type": "code", "execution_count": 5, "id": "5477e321-dc9a-4c9a-a4b6-a2549c0c0d37", "metadata": {}, "outputs": [], "source": [ "import typing" ] }, { "cell_type": "markdown", "id": "990298fd-f154-45c7-b9a7-af63ae6e7c4e", "metadata": { "jp-MarkdownHeadingCollapsed": true, "tags": [] }, "source": [ "## Declaring a type" ] }, { "cell_type": "markdown", "id": "de9d52c9-6df3-4427-b316-587b56347cd4", "metadata": { "tags": [] }, "source": [ "### For a variable" ] }, { "cell_type": "code", "execution_count": 4, "id": "0e87b508-7845-4678-8134-884643f52213", "metadata": {}, "outputs": [], "source": [ "num0: int\n", "num0 = 1\n", "num1: int = 1" ] }, { "cell_type": "markdown", "id": "ccde8fee-3003-4842-bec8-8f118e95b17e", "metadata": {}, "source": [ "### For a function" ] }, { "cell_type": "code", "execution_count": 7, "id": "0a72a4e0-0e01-498f-b770-e48116d5a97b", "metadata": {}, "outputs": [], "source": [ "def fn0(x: int) -> int:\n", " return x\n", "\n", "\n", "fn1: typing.Callable[[int], int]\n", "def fn1(x):\n", " return x" ] }, { "cell_type": "markdown", "id": "36c05f51-5422-41c7-a461-7f766c04b088", "metadata": {}, "source": [ "### Of a generic" ] }, { "cell_type": "code", "execution_count": 8, "id": "267accf3-6ab5-421a-a349-04d21669e586", "metadata": {}, "outputs": [], "source": [ "list0: list[int] = [0, 1]\n", "dict0: dict[str, int] = {'0': 0, '1': 1}" ] }, { "cell_type": "markdown", "id": "a52cf44c-aa01-40c9-9adc-e838da385b28", "metadata": {}, "source": [ "### Of a union" ] }, { "cell_type": "code", "execution_count": 9, "id": "eef4b91f-9a86-4d40-a049-d54f6a6f230f", "metadata": {}, "outputs": [], "source": [ "optional0: int | None = 0\n", "optional1: typing.Union[int, None] = None\n", "optional2: typing.Optional[int] = None\n", "optional2 = -1" ] }, { "cell_type": "markdown", "id": "82158316-a752-4bcf-bd6f-c4cde017096b", "metadata": {}, "source": [ "### example with match\n", "though annotations don't usually influence anything during runtime, they make programming easier" ] }, { "cell_type": "code", "execution_count": 10, "id": "de6fd23d-1a81-47cc-ba84-b2f97ebe2cb3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('12', '12', None)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def join(a: list[int] | set[str] | None) -> str | None:\n", " match a:\n", " case list():\n", " return ''.join(map(str, a))\n", " case set():\n", " return ''.join(a)\n", " case _:\n", " return None\n", "\n", "result: tuple[str | None, str | None, str | None] = join([1, 2]), join({\"1\", \"2\"}), join(None)\n", "result" ] }, { "cell_type": "markdown", "id": "c726cf29-f81e-4e52-95f1-128057fe4d0a", "metadata": { "tags": [] }, "source": [ "# Data model\n", "https://docs.python.org/3/reference/datamodel.html" ] }, { "cell_type": "markdown", "id": "5a56ce30-37b4-463f-a446-895198dc0db8", "metadata": { "tags": [] }, "source": [ "# Working with files\n", "There are a lot of ways, and directly using `open`+`.write`/`.read` is probably not fit for you task." ] }, { "cell_type": "markdown", "id": "daec179e-49a7-441e-95b4-d1b5d2c489fb", "metadata": { "tags": [] }, "source": [ "## `pathlib.Path`\n", "One of the most overlooked python's modules is `pathlib`.\n", "Even more rare is the knowledge that `Path` objects have `.open`, `.write_*` and `.read_*` methods." ] }, { "cell_type": "code", "execution_count": 9, "id": "b25fe82a-f87e-425d-81b4-bf1c0c2baf0f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "6\n", "\n" ] } ], "source": [ "import pathlib\n", "tmp = pathlib.Path('tmp/')\n", "tmp.mkdir(exist_ok=True)\n", "example_txt = tmp / 'example.txt'\n", "example_txt.touch(exist_ok=True)\n", "example_txt.write_text(f'{2+3}\\n{2*3}\\n')\n", "print(example_txt.read_text())" ] }, { "cell_type": "markdown", "id": "4e39ce1a-3396-45c9-bcad-19784326c32e", "metadata": { "tags": [] }, "source": [ "## File(-like) objects\n", "Returned by `open(...)` and `pathlib.Path(...).open(...)`." ] }, { "cell_type": "code", "execution_count": 12, "id": "c59f89f6-d222-4382-8459-dd72c00f746e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "6\n", "\n", "5\n", "\n", "6\n", "\n" ] } ], "source": [ "# avoid using `open` outside `with` (`with` better guarantees file's closure)\n", "with open(example_txt, 'w') as f:\n", " f.write(f'{2+3}\\n{2*3}\\n')\n", "with open(example_txt) as f:\n", " print(f.read())\n", "with open(example_txt) as f:\n", " for line in f:\n", " print(line)" ] }, { "cell_type": "markdown", "id": "6519c846-90fd-4828-9861-3cac9954880f", "metadata": { "tags": [] }, "source": [ "# asyncio\n", "If you do anything with IO, you should probably do it **asynchronously** \n", "https://docs.python.org/3/library/asyncio.html" ] }, { "cell_type": "code", "execution_count": 2, "id": "ffc18b3f-f63a-4556-bc24-d3a7d69756ac", "metadata": {}, "outputs": [], "source": [ "import asyncio" ] }, { "cell_type": "markdown", "id": "c0fe1271-d784-4c45-9294-795a6b01b785", "metadata": { "tags": [] }, "source": [ "## Background\n", "The most prominent use of async IO is undoubtably anything that has to do with JavaScript (the web programming language). \n", "Due to \\{1) limitations in performance 2) single-threaded environment (that can't allow any freezes) 3) constantly working with high-latency IO (because internet, client- and server-side) of high frequency (and high (number-wise) loads for servers)\\}, JS kind of has to resort to using an event loop. \n", "All IO in JS is forced to be async. \n", "In browsers that is to prevent incompetent or malicious actors from killing your browser (or parts of it). \n", "In Node that is to prevent obviously unnecessary blocking of code to achieve better performance. \n", "Ryan Dahl on Node.js: \n", "https://youtu.be/ztspvPYybIY \n", "https://youtu.be/jo_B4LTHi3I" ] }, { "cell_type": "markdown", "id": "f06ca023-f840-4488-b9b2-cfd302193858", "metadata": { "tags": [] }, "source": [ "## Level 0: working inside async framework\n", "If you've ever worked with `aiohttp` or `discord.py`/`disnake`, you've probably encountered `async`/`await` syntax. \n", "It may seems like these frameworks just make you put this magic words here and there. \n", "But there are reasons for both these magic words." ] }, { "cell_type": "markdown", "id": "d46018f9-8609-4408-ab97-48dbea0bfd7c", "metadata": { "tags": [] }, "source": [ "### `async def` -- define an asynchronous function (coroutine)\n", "`async` here signals to python that the function might use `await`s inside it. \n", "Statements *inside* coroutines are guaranteed to happen in sequence with each other" ] }, { "cell_type": "markdown", "id": "5205f839-7b22-4144-9e7c-29093b05e2ca", "metadata": { "tags": [] }, "source": [ "### `await` -- virtually block on a future or on another coroutine and return result\n", "While coroutine `await`s, other coroutines can be (and, probably, are) doing their own stuff. \n", "Any code between `await`s happens sequentially with all the other code, i.e. unlike `threading`, `async` does not mix statements unpredictably. \n", "You always have the exclusive and direct control of data in code fragments where you don't `await`. \n", "Expression `(await f(...))` where `f` is defined as `async def f(...): ...` with `return x` evaluates to that `x`. \n", "Expression `(await future)` where `f` is `asyncio.Future` that has someone call `.set_result(x)` evaluates to that `x`. " ] }, { "cell_type": "markdown", "id": "f9cf426c-94fa-4907-b182-a759d0934080", "metadata": {}, "source": [ "### `async with` -- asynchronous context management\n", "Same as `with`, but `async def __aenter__`/`async def __aexit__` instead of `def __enter__`/`def __exit__`." ] }, { "cell_type": "markdown", "id": "21f08324-0b14-4b35-bc6e-7a7329520ed9", "metadata": {}, "source": [ "## Level 1: using high-level APIs" ] }, { "cell_type": "markdown", "id": "fe6dcf16-522a-4b20-99b6-54b92d59e19e", "metadata": { "jp-MarkdownHeadingCollapsed": true, "tags": [] }, "source": [ "# Last section, so the empty cell does not get stuck with previous sections" ] }, { "cell_type": "code", "execution_count": null, "id": "8274518c-661b-4907-b249-3da832753646", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.4" } }, "nbformat": 4, "nbformat_minor": 5 }