2016-07-17 54 views
12

Nachdem Eli Bendersky den Artikel zu lesen on implementing state machines via Python coroutines Ich wollte ...Proper Typenannotation von Python-Funktionen mit Ertrag

  • sehen sein Beispiel laufen unter Python3
  • und auch die entsprechenden Typenannotationen für die Generatoren hinzufügen

Es ist mir gelungen, den ersten Teil () zu machen, aber ohne async def s oder yield from s zu verwenden, habe ich im Grunde nur den Code portiert - also sind alle Verbesserungen dort willkommen).

Aber ich brauche Hilfe bei den Typenannotationen der Koroutinen:

#!/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) 

Dieser richtig läuft ...

$ ./decoder.py 
Got frame: 99afd1 
Got frame: ab14 

... und auch typechecks:

$ mypy --disallow-untyped-defs decoder.py 
$ 

Aber ich bin mir ziemlich sicher, dass ich besser tun kann, als nur die Generator Basisklasse in den Typspezifikationen zu verwenden (nur wie ich für die Callable). Ich weiß, dass es 3 Typparameter benötigt (Generator[A,B,C]), aber ich bin nicht sicher, wie genau sie hier spezifiziert würden.

Jede Hilfe sehr willkommen.

Antwort

11

Ich habe die Antwort selbst herausgefunden.

Ich suchte, fand aber keine Dokumentation für die 3-Typ-Parameter von Generator im official typing documentation for Python 3.5.2 - über eine wirklich kryptisch Erwähnung ...

class typing.Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]) 

Zum Glück the original PEP484 (das alles begann) war weit mehr hilfreich:

„die Rückkehr Generatortyp Funktionen, die vom generischen Typ Generator mit Anmerkungen versehen werden kann [yield_type, send_type, return_type] von typing.py Modul:

def echo_round() -> Generator[int, float, str]: 
    res = yield 
    while res: 
     res = yield round(res) 
    return 'OK' 

Auf dieser Grundlage konnte ich meine Generatoren mit Anmerkungen versehen und sah mypy bestätigen meine Aufgaben:

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: 
    ... 

ich zum Beispiel meine Aufgaben getestet Tausch der Reihenfolge der Typen - und dann wie erwartet, mypy beschwerte und fragte nach den richtigen (wie oben gesehen).

Der vollständige Code is accessible from here.

Ich werde die Frage für ein paar Tage offen lassen, falls jemand in Chime rein will - vor allem in Bezug auf die Verwendung der neuen Coroutine Stile von Python 3.5 (async def, etc) - würde ich einen Hinweis auf genau wie schätzen Sie würden hier benutzt werden.

+3

In Bezug auf 'async def' und Freunde - sie werden derzeit nicht von mypy unterstützt, sind aber in der aktiven Entwicklung/sollten in naher Zukunft bereit sein! Siehe https: // github.com/python/mypy/pull/1808 und https://github.com/python/mypy/issues/1886 für weitere Details. – Michael0x2a

+0

Nur als ein FYI, mypy 0.4.4 (die experimentelle Unterstützung für async/wartet) [wurde gerade veröffentlicht] (http://mypy-lang.blogspot.com/2016/08/mypy-044-released.html) . Weitere Informationen zum Eintippen von Coroutinen und async/aware finden Sie im Allgemeinen in der [mypy docs] (http://mypy.readthedocs.io/en/latest/kinds_of_types.html#typing-async- wait). Momentan denke ich nicht, dass die Dokumente für das Typisierungsmodul selbst Dinge erwähnen, die mit async/await in Verbindung stehen, aber das wird wahrscheinlich irgendwann in den nächsten Tagen behoben werden. – Michael0x2a

+0

Danke, Michael - werde es überprüfen. – ttsiodras