« first day (1089 days earlier)      last day (1296 days later) » 

19:23
Recently learned about how python's asyncio works by using yield, which is incredibly cool.
Basically yield is similar to return except it produces values you can loop over and it's lazily evaluated.
def foo():
    yield 0
    yield 1
    yield 2

for x in foo():
    print(x)
On top of this, there's a yield from that acts similarly to a bulk yield:
def foo():
    yield from [0, 1, 2]
You can also get the return value from the yield from statement on the same line.
def foo():
    yield from [0, 1, 2]
    return 0

def bar():
    print(yield from foo())

for x in bar():
    print(x)
@SimplyBeautifulArt So the fancy thing about it is that you can use next to only request one value. If it's at the end, it'll raise StopIteration(return_value).
Then you can make code like this:
def foo():
    for i in range(1_000_000):
        yield
    return 0

def bar():
    generator = foo()
    try:
        while True:
            next(generator)
    except StopIteration as e:
        return e.args[0]
Now the fancy thing is that you can create a loop with multiple of these things.
And then you can call next on random generators every time.
The result is that you can get concurrently running code.
And that's basically how asyncio works.
19:52
import random

def gather(*tasks):
    # Store them in dicts by index to return the results in the right order.
    pending_tasks = dict(enumerate(tasks))
    finished_tasks = dict()
    # Keep evaluating tasks until every task is finished.
    while len(pending_tasks) > 0:
        # Randomly select a pending task.
        index, task = random.choice(list(pending_tasks.items()))
        try:
            # Work on the pending task.
            yield next(task)
        except StopIteration as e:
            # If finished, save the result and remove it from the pending tasks.
This is an example implementation of asyncio.gather to run things concurrently.
The await keyword is then basically a yield from.
def run(task):
    try:
        while True:
            next(task)
    except StopIteration as e:
        return e.args[0]
This is an implementation of asyncio.run.
Combined, you can write code like this:
def foo(n):
    for i in range(n):
        print(i)
        yield
    return 0

run(gather(foo(10), foo(20), foo(30)))
The output could then be something like this:
0
1
0
0
2
1
3
4
1
2
2
3
5
6
3
4
7
4
5
8
6
5
7
8
6
9
7
8
9
9
10
11
10
11
12
12
13
13
14
15
16
17
14
18
15
16
19
20
17
21
18
19
22
23
24
25
26
27
28
29
[0, 0, 0]
20:10
@SimplyBeautifulArt Realistic uses for this kind of concurrency are usually for IO bound tasks. You can do something like this for example:
def query_data():
    submit_query()
    while not finished_query():
        yield
    return get_query()
Of course, one would not use yield, but rather await and all the fancy asyncio stuff that python has to offer.

« first day (1089 days earlier)      last day (1296 days later) »