edu/main.ipynb
2022-11-11 13:37:52 +00:00

12 KiB

Learning materials, sources, references

Python-specific

typing

Even a dynamically typed language can sometimes, at least, declare types. Python has built-in support for that via annotations.

In [5]:
import typing

Declaring a type

For a variable

In [4]:
num0: int
num0 = 1
num1: int = 1

For a function

In [7]:
def fn0(x: int) -> int:
    return x


fn1: typing.Callable[[int], int]
def fn1(x):
    return x

Of a generic

In [8]:
list0: list[int] = [0, 1]
dict0: dict[str, int] = {'0': 0, '1': 1}

Of a union

In [9]:
optional0: int | None = 0
optional1: typing.Union[int, None] = None
optional2: typing.Optional[int] = None
optional2 = -1

example with match

though annotations don't usually influence anything during runtime, they make programming easier

In [10]:
def join(a: list[int] | set[str] | None) -> str | None:
    match a:
        case list():
            return ''.join(map(str, a))
        case set():
            return ''.join(a)
        case _:
            return None

result: tuple[str | None, str | None, str | None] = join([1, 2]), join({"1", "2"}), join(None)
result
Out[10]:
('12', '12', None)

Working with files

There are a lot of ways, and directly using open+.write/.read is probably not fit for you task.

pathlib.Path

One of the most overlooked python's modules is pathlib. Even more rare is the knowledge that Path objects have .open, .write_* and .read_* methods.

In [9]:
import pathlib
tmp = pathlib.Path('tmp/')
tmp.mkdir(exist_ok=True)
example_txt = tmp / 'example.txt'
example_txt.touch(exist_ok=True)
example_txt.write_text(f'{2+3}\n{2*3}\n')
print(example_txt.read_text())
5
6

File(-like) objects

Returned by open(...) and pathlib.Path(...).open(...).

In [12]:
# avoid using `open` outside `with` (`with` better guarantees file's closure)
with open(example_txt, 'w') as f:
    f.write(f'{2+3}\n{2*3}\n')
with open(example_txt) as f:
    print(f.read())
with open(example_txt) as f:
    for line in f:
        print(line)
5
6

5

6

asyncio

If you do anything with IO, you should probably do it asynchronously
https://docs.python.org/3/library/asyncio.html

In [2]:
import asyncio

Background

The most prominent use of async IO is undoubtably anything that has to do with JavaScript (the web programming language).
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.
All IO in JS is forced to be async.
In browsers that is to prevent incompetent or malicious actors from killing your browser (or parts of it).
In Node that is to prevent obviously unnecessary blocking of code to achieve better performance.
Ryan Dahl on Node.js:
https://youtu.be/ztspvPYybIY
https://youtu.be/jo_B4LTHi3I

Level 0: working inside async framework

If you've ever worked with aiohttp or discord.py/disnake, you've probably encountered async/await syntax.
It may seems like these frameworks just make you put this magic words here and there.
But there are reasons for both these magic words.

async def -- define an asynchronous function (coroutine)

async here signals to python that the function might use awaits inside it.
Statements inside coroutines are guaranteed to happen in sequence with each other

await -- virtually block on a future or on another coroutine and return result

While coroutine awaits, other coroutines can be (and, probably, are) doing their own stuff.
Any code between awaits happens sequentially with all the other code, i.e. unlike threading, async does not mix statements unpredictably.
You always have the exclusive and direct control of data in code fragments where you don't await.
Expression (await f(...)) where f is defined as async def f(...): ... with return x evaluates to that x.
Expression (await future) where f is asyncio.Future that has someone call .set_result(x) evaluates to that x.

async with -- asynchronous context management

Same as with, but async def __aenter__/async def __aexit__ instead of def __enter__/def __exit__.

Level 1: using high-level APIs

Last section, so the empty cell does not get stuck with previous sections

In [ ]: