Seite 1 von 1
Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 18:26
von imonbln
Hallo,
Ich spiele gerade etwas mit Python Protocol und mypy dabei ist mir aufgefallen das meine mypy Version, diesen minimalen Code ohne Fehler akzeptiert.
Code: Alles auswählen
import collections
import typing
class Command(typing.Protocol):
def execute(self):
...
class CommandBatch(collections.UserList[Command]):
def execute(self):
for command in self:
command.execute()
class Duck:
def execute(self):
print("duck quark")
class Swan:
def execute(self):
print("Swan QUARK")
def main():
batch = CommandBatch[Duck]()
batch.append(Duck())
batch.append(Swan())
batch.execute()
if __name__ == '__main__':
main()
Meine Erwartung wäre aber gewesen, dass mypy bei
batch.append(Swan()) eine Warnung oder einen Fehler generiert, da Swan zwar das Command Protocol erfüllt, aber die CommandBatch an Duck gebunden wurde. Was ist mein Denkfehler?
mypy Version: 0.991
python Version: 3.10.9
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 18:33
von __deets__
Ich kenne mich mit mypy wenig aus, aber aus meiner Sicht ist CommandBatch doch schon vollstaendig typisiert fuer deinen Anwendungsfall hier. CommandBatch braeuchte fuer mein Verstaendnis eine Typvariable, aber die sehe ich nicht. Das [Duck] wuerde doch wenn gar nichts bedeuten, denn Duck ist ja nix (in Bezug auf einen Typ). Es implementiert implizit Command, aber das reicht doch nicht fuer ein Typkalkuel.
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 18:45
von imonbln
__deets__ hat geschrieben: Mittwoch 25. Januar 2023, 18:33
Ich kenne mich mit mypy wenig aus, aber aus meiner Sicht ist CommandBatch doch schon vollstaendig typisiert fuer deinen Anwendungsfall hier. CommandBatch braeuchte fuer mein Verstaendnis eine Typvariable.
Kling erstmal logisch, hast du eine Idee wie ich denn Code ändern kann, damit es für Typkalkuel, reicht und mypy hier eine Warnung/Fehlerausgibt.
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 18:47
von __blackjack__
@imonbln: mypy prüft nix in `main()` solange Du da keine Typsignatur anbringst. Also den Rückgabetyp von `main()` mit `` -> None`` annotieren und schon meldet mypy auch einen Fehler bei ``CommandBatch[Duck]``:
Code: Alles auswählen
forum21.py:27: error: Value of type "Type[CommandBatch]" is not indexable
Found 1 error in 1 file (checked 1 source file)
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 19:05
von imonbln
__blackjack__ hat geschrieben: Mittwoch 25. Januar 2023, 18:47
@imonbln: mypy prüft nix in `main()` solange Du da keine Typsignatur anbringst. Also den Rückgabetyp von `main()` mit `` -> None`` annotieren und schon meldet mypy auch einen Fehler bei ``CommandBatch[Duck]``:
Guter Hinweis ist aber nur die halbe Wahrheit, selbst mit der annotieren Signatur bekomme ich noch nicht, was ich mir eigentlich wünsche.
Code: Alles auswählen
import collections
import typing
T= typing.TypeVar("T", covariant=True)
class Command(typing.Protocol[T]):
def execute(self):
...
class CommandBatch(collections.UserList[Command[T]]):
def execute(self):
for command in self:
command.execute()
class Duck:
def execute(self):
print("duck quark")
class Swan:
def execute(self):
print("Swan QUARK")
def main() -> None:
batch = CommandBatch[Duck]()
batch.append(Duck())
batch.append(Swan())
batch.execute()
if __name__ == '__main__':
main()
Wenn ich das richtig verstehen müsste hier eigentlich
covariant=False stehen, aber dann gibt es einen anderen Fehler, als erwartet. Irgendwie fehlt mir noch ein Stück Erkenntnis.
Code: Alles auswählen
bla.py:8: error: Invariant type variable "T" used in protocol where covariant one is expected [misc]
Found 1 error in 1 file (checked 1 source file)
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 20:53
von __deets__
Na es scheint, als ob collections.UserList eben nur covariante Typen akzeptiert. Wieso das so ist kann ich leider auf die Schnelle nicht eruieren, wie auch immer mypy das ableitet.
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 21:13
von __deets__
Wollte mal spielen, aber bei mir kommt der Fehler nicht. Wie genau provozierst du den?
Code: Alles auswählen
21:12 $ mypy /tmp/test.py
Success: no issues found in 1 source file
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 21:52
von imonbln
ich muss hier nur das True in False ändern um den Fehler zu bekommen.
Ich vermute das Protokoll nur kovariante Typen akzeptiert, aber den Grund kenne ich noch nicht.
Re: Python Protocol und mypy
Verfasst: Mittwoch 25. Januar 2023, 22:10
von imonbln
Ich habe es gefunden im
PEP 544 wurde das verhalten, was ich gerne hätte abgelehnt. Es geht also per Sprachdefinition nicht.
Re: Python Protocol und mypy
Verfasst: Donnerstag 26. Januar 2023, 01:41
von imonbln
Mach mal hilft es das Problem ein wenig ruhen zu lassen, was ich wollte, geht doch. Man muss nur die
TypeVar T an das Protokoll binden, dann kann mypy erkennen das Swan keine Duck ist. So erkennt mypy denn Fehler.
Code: Alles auswählen
import collections
import typing
T = typing.TypeVar("T", bound=Command)
class Command(typing.Protocol):
def execute(self):
...
class CommandBatch(collections.UserList[T]):
def execute(self):
for command in self:
command.execute()
class Duck:
def execute(self):
print("duck quark")
class Swan:
def execute(self):
print("Swan QUARK")
def main() -> None:
batch = CommandBatch[Duck]()
batch.append(Duck())
batch.append(Swan())
batch.execute()
if __name__ == '__main__':
main()
Re: Python Protocol und mypy
Verfasst: Freitag 27. Januar 2023, 09:24
von __deets__
Ich habe da nochmal drüber nachgedacht, und was du hier machst bedeutet doch, einfach nur zu sagen „es muss Duck sein“. Warum also der Umweg über ein Protokoll, das ja genau eine solche spezifische Typisierung vermeidet? Dann braucht’s doch das Protokoll garnicht.
Re: Python Protocol und mypy
Verfasst: Freitag 27. Januar 2023, 12:13
von imonbln
Stell dir einfach vor, es gibt noch eine zweite CommandBatch[Swan] und beide platzieren Bestellungen in Handelssysteme. Also ich finde es dann schon sinnvoll, wenn die CommandBatch generisch ist, aber der Statische Codeanalyse erkennt kann, dass ich gerade eine Duck in den Swan Handelsplatz senden will.
Re: Python Protocol und mypy
Verfasst: Freitag 27. Januar 2023, 12:40
von __deets__
Den Teil verstehe ich. Nicht den, warum das über eine Protokoll läuft. Du sagst „hier ist eine Collection of Command“, aber dann soll batch eine Collection of Ducks sein. Mach es doch gleich eine Collection of Ducks?
Re: Python Protocol und mypy
Verfasst: Freitag 27. Januar 2023, 13:22
von imonbln
Aber dann bräuchte ich auch noch eine Collection of Swan, welche exakt den gleichen Code hat wie die Collection of Duck. Daher denke ich, dass es, als eine Art Template hier sinnvoll ist, das Protokoll zu verwenden.
Re: Python Protocol und mypy
Verfasst: Freitag 27. Januar 2023, 13:28
von __deets__
Aber die muss doch keinem Protocol gehorchen, oder vertue ich mich da? Zumindest in C++ würde das gehen, da prüft der Compiler dann,w as der konkrete Typ kann. Aber es kann sein, das genau da mein Missverständnis liegt.