Eli Benderskyの記事を読んだ後 Python coroutines)を介してステートマシンを実装する場合 私は...
私は最初の部分(を実行することに成功しましたが、async def
sまたはyield from
sを使用せずに、基本的にコードを移植しただけなので、どんな改善も歓迎されます)。
しかし、コルーチンの型注釈についていくつかの助けが必要です。
#!/usr/bin/env python3
from typing import Callable, Generator
def unwrap_protocol(header: int=0x61,
footer: int=0x62,
dle: int=0xAB,
after_dle_func: Callable[[int], int]=lambda x: x,
target: Generator=None) -> Generator:
""" Simplified protocol unwrapping co-routine."""
#
# Outer loop looking for a frame header
#
while True:
byte = (yield)
frame = [] # type: List[int]
if byte == header:
#
# Capture the full frame
#
while True:
byte = (yield)
if byte == footer:
target.send(frame)
break
Elif byte == dle:
byte = (yield)
frame.append(after_dle_func(byte))
else:
frame.append(byte)
def frame_receiver() -> Generator:
""" A simple co-routine "sink" for receiving full frames."""
while True:
frame = (yield)
print('Got frame:', ''.join('%02x' % x for x in frame))
bytestream = bytes(
bytearray((0x70, 0x24,
0x61, 0x99, 0xAF, 0xD1, 0x62,
0x56, 0x62,
0x61, 0xAB, 0xAB, 0x14, 0x62,
0x7)))
frame_consumer = frame_receiver()
next(frame_consumer) # Get to the yield
unwrapper = unwrap_protocol(target=frame_consumer)
next(unwrapper) # Get to the yield
for byte in bytestream:
unwrapper.send(byte)
これは正しく実行されます...
$ ./decoder.py
Got frame: 99afd1
Got frame: ab14
...またタイプチェック:
$ mypy --disallow-untyped-defs decoder.py
$
しかし、型の仕様でGenerator
基本クラスを使用するよりも、(Callable
の場合と同じように)うまくやることができると確信しています。 3つの型パラメーター(Generator[A,B,C]
)が必要であることはわかっていますが、それらがここでどのように指定されるのか正確にはわかりません。
どんな助けでも大歓迎です。
私は自分で答えを見つけました。
私は検索しましたが、Generator
の3つの型パラメーターに関するドキュメントは見つかりませんでした Python 3.5.2)の公式タイピングドキュメント -本当に不可解な言及を超えて...
class typing.Generator(Iterator[T_co], Generic[T_co, T_contra, V_co])
幸い、 元のPEP484 (これがすべて始まった)の方がはるかに役に立ちました。
"ジェネレーター関数の戻り値の型は、typeing.pyモジュールによって提供されるジェネリック型Generator [yield_type、send_type、return_type]によって注釈を付けることができます:
def echo_round() -> Generator[int, float, str]:
res = yield
while res:
res = yield round(res)
return 'OK'
これに基づいて、ジェネレーターに注釈を付けることができ、mypy
が割り当てを確認するのを見ました:
from typing import Callable, Generator
# A protocol decoder:
#
# - yields Nothing
# - expects ints to be `send` in his yield waits
# - and doesn't return anything.
ProtocolDecodingCoroutine = Generator[None, int, None]
# A frame consumer (passed as an argument to a protocol decoder):
#
# - yields Nothing
# - expects List[int] to be `send` in his waiting yields
# - and doesn't return anything.
FrameConsumerCoroutine = Generator[None, List[int], None]
def unwrap_protocol(header: int=0x61,
footer: int=0x62,
dle :int=0xAB,
after_dle_func: Callable[[int], int]=lambda x: x,
target: FrameConsumerCoroutine=None) -> ProtocolDecodingCoroutine:
...
def frame_receiver() -> FrameConsumerCoroutine:
...
私は自分の課題を、たとえば型の順序を入れ替える-そして期待通りに、mypy
は文句を言い、適切なものを要求しました(上記参照)。
完全なコード ここからアクセス可能 。
特にPython 3.5(async def
、)の新しいコルーチンスタイルの使用に関して)誰かが参加したい場合に備えて、質問を数日間開いたままにします。 -ここでそれらがどのように使用されるかについてのヒントを教えていただければ幸いです。
yield
を使用する単純な関数がある場合は、Iterator
ではなくGenerator
タイプを使用して、その結果に注釈を付けることができます。
from typing import Iterator
def count_up() -> Iterator[int]:
for x in range(10):
yield x