12 KiB
Learning materials, sources, references¶
Python-specific¶
- https://docs.python.org/3/
there's a lot of important stuff posted there, actually (e.g.
match
keyword)- https://docs.python.org/3/tutorial/
read through this - https://docs.python.org/3/library/
before you start implementing something or looking for pip packages- https://docs.python.org/3/library/ctypes.html
to load C-like DLLs
- https://docs.python.org/3/library/ctypes.html
- https://docs.python.org/3/extending/
if you need C - https://docs.python.org/3/whatsnew/
- https://docs.python.org/3/reference/
in-depth about python itself the most important part from that reference
- https://docs.python.org/3/tutorial/
- https://wiki.python.org/moin/FrontPage
at the moment of writing, haven't read much of it
typing¶
Even a dynamically typed language can sometimes, at least, declare types. Python has built-in support for that via annotations.
import typing
Declaring a type¶
For a variable¶
num0: int num0 = 1 num1: int = 1
For a function¶
def fn0(x: int) -> int: return x fn1: typing.Callable[[int], int] def fn1(x): return x
Of a generic¶
list0: list[int] = [0, 1] dict0: dict[str, int] = {'0': 0, '1': 1}
Of a union¶
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
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
('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.
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(...)
.
# 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
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 await
s 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 await
s, other coroutines can be (and, probably, are) doing their own stuff.
Any code between await
s 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__
.