Skip to content

Commit d58dce0

Browse files
committed
Coroutine section now covers asyncio instead of generator based coroutines
1 parent e4d95c5 commit d58dce0

File tree

3 files changed

+129
-87
lines changed

3 files changed

+129
-87
lines changed

README.md

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Contents
1313
**   ** **3. Syntax:** **         ** **[`Args`](#arguments)**__,__ **[`Inline`](#inline)**__,__ **[`Closure`](#closure)**__,__ **[`Decorator`](#decorator)**__,__ **[`Class`](#class)**__,__ **[`Duck_Types`](#duck-types)**__,__ **[`Enum`](#enum)**__,__ **[`Exceptions`](#exceptions)**__.__
1414
**   ** **4. System:** **        ** **[`Print`](#print)**__,__ **[`Input`](#input)**__,__ **[`Command_Line_Arguments`](#command-line-arguments)**__,__ **[`Open`](#open)**__,__ **[`Path`](#path)**__,__ **[`Command_Execution`](#oscommands)**__.__
1515
**   ** **5. Data:** **             ** **[`JSON`](#json)**__,__ **[`Pickle`](#pickle)**__,__ **[`CSV`](#csv)**__,__ **[`SQLite`](#sqlite)**__,__ **[`Bytes`](#bytes)**__,__ **[`Struct`](#struct)**__,__ **[`Array`](#array)**__,__ **[`MemoryView`](#memory-view)**__,__ **[`Deque`](#deque)**__.__
16-
**   ** **6. Advanced:** **   ** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Introspection`](#introspection)**__,__ **[`Metaprograming`](#metaprograming)**__,__ **[`Eval`](#eval)**__,__ **[`Coroutine`](#coroutine)**__.__
16+
**   ** **6. Advanced:** **   ** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Introspection`](#introspection)**__,__ **[`Metaprograming`](#metaprograming)**__,__ **[`Eval`](#eval)**__,__ **[`Coroutine`](#coroutines)**__.__
1717
**   ** **7. Libraries:** **      ** **[`Progress_Bar`](#progress-bar)**__,__ **[`Plot`](#plot)**__,__ **[`Table`](#table)**__,__ **[`Curses`](#curses)**__,__ **[`Logging`](#logging)**__,__ **[`Scraping`](#scraping)**__,__ **[`Web`](#web)**__,__ **[`Profile`](#profiling)**__,__
1818
**                                 ** **[`NumPy`](#numpy)**__,__ **[`Image`](#image)**__,__ **[`Animation`](#animation)**__,__ **[`Audio`](#audio)**__.__
1919

@@ -1209,10 +1209,15 @@ class MyIterable:
12091209
```
12101210

12111211
```python
1212-
>>> z = MyIterable([1, 2, 3])
1213-
>>> iter(z)
1214-
<generator object MyIterable.__iter__>
1215-
>>> 1 in z
1212+
>>> obj = MyIterable([1, 2, 3])
1213+
>>> itr = iter(obj)
1214+
>>> [next(itr), next(itr), next(itr)]
1215+
[1, 2, 3]
1216+
>>> [el for el in iter(obj)]
1217+
[1, 2, 3]
1218+
>>> [el for el in obj]
1219+
[1, 2, 3]
1220+
>>> 1 in obj
12161221
True
12171222
```
12181223

@@ -2095,10 +2100,10 @@ from operator import itemgetter, attrgetter, methodcaller
20952100

20962101
```python
20972102
import operator as op
2098-
elementwise_sum = map(op.add, list_a, list_b)
2099-
product_of_elems = functools.reduce(op.mul, <collection>)
21002103
sorted_by_second = sorted(<collection>, key=op.itemgetter(1))
21012104
sorted_by_both = sorted(<collection>, key=op.itemgetter(1, 0))
2105+
product_of_elems = functools.reduce(op.mul, <collection>)
2106+
elementwise_sum = map(op.add, list_a, list_b)
21022107
LogicOp = enum.Enum('LogicOp', {'AND': op.and_, 'OR' : op.or_})
21032108
last_el = op.methodcaller('pop')(<list>)
21042109
```
@@ -2238,49 +2243,64 @@ ValueError: malformed node or string
22382243
```
22392244

22402245

2241-
Coroutine
2242-
---------
2243-
* **Any function that contains a `'(yield)'` expression returns a coroutine.**
2244-
* **Coroutines are similar to iterators, but data needs to be pulled out of an iterator by calling `'next(<iter>)'`, while we push data into the coroutine by calling `'<coroutine>.send(<el>)'`.**
2245-
* **Coroutines provide more powerful data routing possibilities than iterators.**
2246+
Coroutines
2247+
----------
2248+
* **Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t use as much memory.**
2249+
* **Coroutine definition starts with `'async'` and its call with `'await'`.**
2250+
* **`'asyncio.run(<coroutine>)'` is the main entry point for asynchronous programs.**
2251+
* **Fucntions wait(), gather() and as_completed() can be used when multiple coroutines need to be started at the same time.**
22462252

2247-
### Helper Decorator
2248-
* **All coroutines must first be "primed" by calling `'next(<coroutine>)'`.**
2249-
* **Remembering to call next() is easy to forget.**
2250-
* **Solved by wrapping coroutine functions with the following decorator:**
2253+
#### Starts a terminal game where you control an asterisk that must avoid numbers:
22512254

22522255
```python
2253-
def coroutine(func):
2254-
def out(*args, **kwargs):
2255-
cr = func(*args, **kwargs)
2256-
next(cr)
2257-
return cr
2258-
return out
2259-
```
2256+
import asyncio, collections, curses, enum, random
22602257

2261-
### Pipeline Example
2262-
```python
2263-
def reader(target):
2264-
for i in range(10):
2265-
target.send(i)
2266-
target.close()
2258+
P = collections.namedtuple('P', 'x y') # Position
2259+
D = enum.Enum('D', 'n e s w') # Direction
22672260

2268-
@coroutine
2269-
def adder(target):
2261+
def main(screen):
2262+
curses.curs_set(0) # Makes cursor invisible.
2263+
screen.nodelay(True) # Makes getch() non-blocking.
2264+
asyncio.run(main_coroutine(screen)) # Starts running asyncio code.
2265+
2266+
async def main_coroutine(screen):
2267+
state = {'*': P(0, 0), **{id_: P(30, 10) for id_ in range(10)}}
2268+
moves = asyncio.Queue()
2269+
coros = (*(random_controller(id_, moves) for id_ in range(10)),
2270+
human_controller(screen, moves),
2271+
model(moves, state, *screen.getmaxyx()),
2272+
view(state, screen))
2273+
await asyncio.wait(coros, return_when=asyncio.FIRST_COMPLETED)
2274+
2275+
async def random_controller(id_, moves):
22702276
while True:
2271-
value = (yield)
2272-
target.send(value + 100)
2273-
2274-
@coroutine
2275-
def printer():
2277+
moves.put_nowait((id_, random.choice(list(D))))
2278+
await asyncio.sleep(random.random() / 2)
2279+
2280+
async def human_controller(screen, moves):
2281+
while (ch := screen.getch()) != 27:
2282+
key_mappings = {259: D.n, 261: D.e, 258: D.s, 260: D.w}
2283+
if ch in key_mappings:
2284+
moves.put_nowait(('*', key_mappings[ch]))
2285+
await asyncio.sleep(0.01)
2286+
2287+
async def model(moves, state, height, width):
2288+
while state['*'] not in {p for id_, p in state.items() if id_ != '*'}:
2289+
id_, d = await moves.get()
2290+
p = state[id_]
2291+
deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
2292+
new_p = P(*[sum(a) for a in zip(p, deltas[d])])
2293+
if 0 <= new_p.x < width-1 and 0 <= new_p.y < height:
2294+
state[id_] = new_p
2295+
2296+
async def view(state, screen):
22762297
while True:
2277-
value = (yield)
2278-
print(value, end=' ')
2279-
```
2298+
screen.clear()
2299+
for id_, p in state.items():
2300+
screen.addstr(p.y, p.x, str(id_))
2301+
await asyncio.sleep(0.01)
22802302

2281-
```python
2282-
>>> reader(adder(printer()))
2283-
100 101 102 103 104 105 106 107 108 109
2303+
curses.wrapper(main)
22842304
```
22852305
<br>
22862306

@@ -2329,7 +2349,7 @@ with open('test.csv', encoding='utf-8', newline='') as file:
23292349

23302350
Curses
23312351
------
2332-
#### Clears the terminal, prints a message and waits for an ESC key press:
2352+
#### Clears the terminal, prints a message and waits for the ESC key press:
23332353
```python
23342354
from curses import wrapper, curs_set, ascii
23352355
from curses import KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT

0 commit comments

Comments
 (0)