Python, MyPy und Annotations

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
PythonEngineer
User
Beiträge: 3
Registriert: Samstag 31. Dezember 2022, 15:13

Hallo zusammen,

ich habe mal angefangen, mich mit Type Hints und Annotations zu beschäftigen. Aber so ganz verstehe ich das noch nicht. Ich habe hier mal ein Beispiel:

Code: Alles auswählen

from __future__ import annotations
import hashlib

a: hashlib._Hash = hashlib.sha256(b"Hallo Welt")
print(a.hexdigest())
Dass sha256() ein Objekt vom Typ "hashlib._Hash" zurückgibt, weiß ich, weil ich zuvor

Code: Alles auswählen

print(type(hashlib.sha256))
ausprobiert habe. Aaaaaber, das klappt alles nur mit dem import von annotations aus __future__. Und da verstehe ich grade nicht, was es damit aufsich hat.

Könnt ihr mir den Sinn von __future__.annotations erklären?


Vielen Dank.
Benutzeravatar
__blackjack__
User
Beiträge: 13998
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PythonEngineer: Damit kann man die Typprüfung quasi verschieben weil statisch gibt's das in dem Modul ja nicht, die ganzen Sachen dort werden dynamisch zusammengebaut.

Wobei das IMHO falsch ist Implementierungsdetails von anderen Modulen als Typen anzugeben. Der `_` am Anfang von `_Hash` sagt, das ist ein Implementierungsdetail, das muss nicht so sein. Andere Python-Version, anderer Interpreter, anderes Betriebssystem oder *irgendwas* und der Typ kann ein anderer sein.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
PythonEngineer
User
Beiträge: 3
Registriert: Samstag 31. Dezember 2022, 15:13

Aber hashlib.py definiert nirgends _Hash. Also es steht nirgends in der py-Datei. Ich versteh's net :D
Benutzeravatar
pillmuncher
User
Beiträge: 1529
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@PythonEngineer: Schaust du hier:

Code: Alles auswählen

>>> type("".join(reversed("hsaH_")), (), {})
<class '__main__._Hash'>
In Python kann man (fast) alles zur Laufzeit zusammenzimmern, was von object erbt und nicht bei der Erzeugung eine Exception schmeißt. In meinem Beispiel eine Klasse, denn Klassen sind ja auch nur Objekte, und zwar solche, die von type erben:

Code: Alles auswählen

>>> C = type("".join(reversed("hsaH_")), (), {})
>>> isinstance(C, object)
True
>>> isinstance(C, type)
True
>>> isinstance(type, object)
True
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

PythonEngineer hat geschrieben: Freitag 7. März 2025, 20:38 Dass sha256() ein Objekt vom Typ "hashlib._Hash" zurückgibt, weiß ich, weil ich zuvor

Code: Alles auswählen

print(type(hashlib.sha256))
ausprobiert habe
Ach so?

Code: Alles auswählen

>>> import hashlib
>>> print(type(hashlib.sha256))
<class 'builtin_function_or_method'>

Code: Alles auswählen

>>> import hashlib
>>> print(type(hashlib.sha256()))
<class '_hashlib.HASH'>
Deshalb ist es wichtig, dass du den Beitrag von __blackjack__ verstehst.
Ich bin eh kein Freund von Annotations. Aber bei solchen Implementierungsdetails ist es falsch bis nicht möglich.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1219
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ich mag Typenannotationen und will sie nicht missen, aber die Verwendung von Implementierungsdetails führt zu Problemen, falls man unterschiedliche Interpreter verwendet. Nicht alle Module sind mit TypeHints nutzbar, da vieles in C implementiert worden ist und dafür keine pyi Dateien erstellt worden sind.

Bei MicroPython ist es z.B. anders:

Code: Alles auswählen

MicroPython v1.25.0-preview.245.g990f50fbb.dirty on 2025-02-03; linux [GCC 14.2.1] version
Use Ctrl-D to exit, Ctrl-E for paste mode
>>> import hashlib
>>> hashlib.sha
sha1            sha256
>>> hashlib.sha256()
<sha256>
>>> _.__class__
<class 'sha256'>
>>>
Hier meckert mypy nicht:

Code: Alles auswählen

import hashlib
from _hashlib import HASH

def foo() -> HASH:
    return hashlib.sha256(b"")


foo().hexdigest()
Wenn ich das aber mit Micropython ausführen möchte:
[deadeye@nexus ~]$ git/micropython/ports/unix/build-standard/micropython hh.py
Traceback (most recent call last):
File "hh.py", line 2, in <module>
ImportError: no module named '_hashlib'
Mit PyPy gehts aber z.B.

Code: Alles auswählen

[deadeye@nexus ~]$ pyenv shell pypy3.10-7.3.19
[deadeye@nexus ~]$ pip install mypy
Collecting mypy
  Downloading mypy-1.15.0-py3-none-any.whl (2.2 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.2/2.2 MB 15.8 MB/s eta 0:00:00
Collecting typing_extensions>=4.6.0
  Downloading typing_extensions-4.12.2-py3-none-any.whl (37 kB)
Collecting mypy_extensions>=1.0.0
  Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)
Collecting tomli>=1.1.0
  Downloading tomli-2.2.1-py3-none-any.whl (14 kB)
Installing collected packages: typing_extensions, tomli, mypy_extensions, mypy
Successfully installed mypy-1.15.0 mypy_extensions-1.0.0 tomli-2.2.1 typing_extensions-4.12.2

[notice] A new release of pip is available: 23.0.1 -> 25.0.1
[notice] To update, run: pip install --upgrade pip
[deadeye@nexus ~]$ python hh.py
[deadeye@nexus ~]$ mypy hh.py
Success: no issues found in 1 source file
[deadeye@nexus ~]$
Besser wäre es, wenn es in allen Implementierungen das Objekt Hash geben würde. Ist aber nicht so, müssen wir halt drumherum arbeiten. Oder TypenAnnotationen einfach in diesem Fall nicht anwenden.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
noisefloor
User
Beiträge: 4172
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Postponed Evaluation of Annotations ist auch noch ein experimentelles Feature, welches _nicht_ im Standard enthalten ist und dessen Aufnahme als fixe Implementierung offen ist. Siehe auch https://docs.python.org/3/library/__future__.html, Fussnote 1.

Mit Annotations beschäftigen ist ja ok und das gibt's ja auch schon eine ganze Menge - aber vielleicht nicht direkt mit einem experimentellen Feature.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13998
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Man könnte sich in solchen Fällen selbst ein Protokoll definieren, und dass dann als Typ angeben. Siehe PEP 544.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
DeaD_EyE
User
Beiträge: 1219
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ja, funktioniert gut:

Code: Alles auswählen

import hashlib
from typing import Protocol

class Hash(Protocol):
    def hexdigest(self) -> str: ...

def foo() -> Hash:
    return hashlib.sha256(b"")


foo().hexdigest()
foo().digest()
Ich habe mit voller Absicht nur eine Methode implementiert. Wenn man den Code ausführt, gibt es keinen Fehler. Wenn man das mit mypy überprüft, meckert mypy:
[deadeye@nexus ~]$ mypy hh.py
hh.py:12: error: "Hash" has no attribute "digest"; maybe "hexdigest"? [attr-defined]
Found 1 error in 1 file (checked 1 source file)
Jetzt nochmal mit Absicht den falschen Typen angegeben:

Code: Alles auswählen

import hashlib
from typing import Protocol

class Hash(Protocol):
    def hexdigest(self) -> bytes: ...

def foo() -> Hash:
    return hashlib.sha256(b"")


foo().hexdigest()
hh.py:8: error: Incompatible return value type (got "HASH", expected "Hash") [return-value]
hh.py:8: note: Following member(s) of "HASH" have conflicts:
hh.py:8: note: Expected:
hh.py:8: note: def hexdigest(self) -> bytes
hh.py:8: note: Got:
hh.py:8: note: def hexdigest(self) -> str
Found 1 error in 1 file (checked 1 source file)
Mir gefällt das :-)


Aber:
[deadeye@nexus ~]$ git/micropython/ports/unix/build-standard/micropython hh.py
Traceback (most recent call last):
File "hh.py", line 2, in <module>
ImportError: no module named 'typing'
[deadeye@nexus ~]$
Ok, Lösung gefunden und das fehlende Typing Modul installiert:

Code: Alles auswählen

git/micropython/ports/unix/build-standard/micropython -m mip install github:josverl/micropython-stubs/mip/typing.mpy
Und dann stellt man fest, dass es bei Micropython hexdigest nicht gibt. Diese Methode ist nicht implementiert worden.
[deadeye@nexus ~]$ git/micropython/ports/unix/build-standard/micropython hh.py
Traceback (most recent call last):
File "hh.py", line 11, in <module>
AttributeError: 'sha256' object has no attribute 'hexdigest'
Gibt es dafür auch eine Lösung?

Hier noch der Link zu den Micropython-Stubs: https://micropython-stubs.readthedocs.i ... g_mpy.html
Ist aber schon ziemlich verrückt, den Code auch noch auf den Microcontroller zu packen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten