Python-Code wie Typescript schreiben

王林
Freigeben: 2024-08-01 20:18:11
Original
666 Leute haben es durchsucht

Ich gehe davon aus, dass Sie, die diesen Artikel lesen möchten, wissen, was Typoskript ist. Der Javascript-Entwickler hat Typescript erstellt, um Javascript typsicherer zu machen. Typesafe macht den Code lesbarer und weist weniger Fehler auf, ohne dass ein Test geschrieben werden muss. Kann Typsicherheit in Python erreicht werden?.

Warum brauchen wir Typsicherheit?

Stellen Sie sich diese unschuldig aussehende Funktion vor

def send_email(sender, receiver, message):
    ...
Nach dem Login kopieren

Ich verstecke absichtlich die Code-Implementierung. Können Sie anhand des Funktionsnamens und des Parameters erraten, wozu eine Funktion dient und welchen Parameter wir benötigen, um diese Funktion zu verwenden? Anhand des Funktionsnamens wissen wir, dass es sich um eine Funktion zum Versenden einer E-Mail handelt. Was ist mit dem Parameter? Was sollten wir eingeben, um diese Funktion zu verwenden?

Erste Vermutung: Absender ist die Zeichenfolge der E-Mail, Empfänger ist die Zeichenfolge der E-Mail, Nachricht ist die Zeichenfolge des E-Mail-Texts.

send_email(sender="john@mail.com", receiver="doe@mail.com", message="Hello doe! How are you?")
Nach dem Login kopieren

Die einfachste Vermutung. aber es ist nicht die einzige Vermutung.

Zweite Vermutung: Absender ist Ganzzahl der Benutzer-ID in der Datenbank, Empfänger ist Ganzzahl der Benutzer-ID in der Datenbank, Nachricht ist die Zeichenfolge des E-Mail-Texts.

john_user_id = 1
doe_user_id = 2
send_email(sender=1, receiver=2, message="Hello doe! How are you?")
Nach dem Login kopieren

Stellen Sie sich vor, Sie arbeiten an einer Anwendung. Die meisten Anwendungen verwenden eine Datenbank. Der Benutzer wird normalerweise durch seine ID repräsentiert.

Dritte Vermutung: Absender ist Wörterbuch, Empfänger ist Wörterbuch, Nachricht ist Wörterbuch.

john = {
    "id": 1,
    "username": "john",
    "email": "john@mail.com"
}
doe = {
    "id": 2,
    "username": "doe",
    "email": "doe@mail.com"
}
message = {
    "title": "Greeting my friend doe",
    "body": "Hello doe! How are you?"
}
send_email(sender=john, receiver=doe, message=message)
Nach dem Login kopieren

Vielleicht benötigt send_email mehr als E-Mail und Benutzer-ID. Um mehr Daten zu jedem Parameter hinzuzufügen, wird eine Wörterbuchstruktur verwendet. Sie bemerken, dass es sich bei der Nachricht nicht nur um eine Zeichenfolge handelt, sondern möglicherweise um einen Titel und einen Text.

Weitere Vermutung: Absender ist Klassenbenutzer, Empfänger ist Klassenbenutzer, Nachricht ist Wörterbuch.

class User():

    def __init__(self, id, username, email):
        self.id = id
        self.username = username
        self.email = email

john = User(id=1, username="john", email="john@mail.com")
doe = User(id=2, username="doe", email="doe@mail.com")
message = {
    "title": "Greeting my friend doe",
    "body": "Hello doe! How are you?"
}
send_email(sender=john, receiver=doe, message=message)
Nach dem Login kopieren

Vielleicht lässt sich send_email in eine Datenbank-Orm wie Django ORM oder Sqlalchemy integrieren. Um es dem Endbenutzer einfacher zu machen, wird direkt die ORM-Klasse verwendet.

Welche Antwort ist also die richtige? Eine davon kann eine richtige Antwort sein. Vielleicht kann die richtige Antwort eine Kombination aus zwei Vermutungen sein. Wie Sender und Empfänger ist die Klasse Benutzer (vierte Vermutung), aber die Nachricht ist str (erste Vermutung). Wir können nicht sicher sein, es sei denn, wir lesen die Code-Implementierung. Das ist Zeitverschwendung, wenn Sie ein Endbenutzer sind. Als Endbenutzer, der diese Funktion verwendet, benötigen wir lediglich, was die Funktion tut, welche Parameter sie benötigt und welche Funktionsausgabe erfolgt.

Die Lösung

Dokumentzeichenfolge

Python hat eine Funktionsdokumentation mithilfe von Docstring integriert. Hier ein Beispiel für eine Dokumentzeichenfolge.

def add(x, y):
    """Add two number

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x + y

def send_email(sender, receiver, message):
    """Send email from sender to receiver

    Parameter:\n
    sender -- email sender, class User\n
    receiver -- email receiver, class User\n
    message -- body of the email, dictionary (ex: {"title": "some title", "body": "email body"}\n

    Return: None
    """
    ...
Nach dem Login kopieren

Das Tolle an docstring ist, dass es mit dem Editor kompatibel ist. In vscode wird die Dokumentzeichenfolge angezeigt, wenn Sie mit der Maus über eine Funktion fahren. Die meisten Bibliotheken in Python verwenden Docstring, um ihre Funktion zu dokumentieren.

Writing Python code like Typescript

Das Problem mit docstring ist die Dokumentsynchronisierung. So stellen Sie sicher, dass die Dokumentzeichenfolge immer mit der Codeimplementierung synchronisiert wird. Man kann es nicht richtig auf die Probe stellen. Ich höre von einer zufälligen Person im Internet: „Veraltete Dokumentation zu haben ist schlimmer, als keine Dokumentation zu haben.“

Doktortest

Übrigens können Sie docstring irgendwie mit doctest testen. Doctest testet Ihren Dokumentstring, indem er ein Beispiel für Ihren Dokumentstring ausführt. Doctest ist in Python bereits vorinstalliert, sodass Sie keine externen Abhängigkeiten benötigen. Sehen wir uns dieses Beispiel an. Erstellen Sie eine neue Datei mit dem Namen my_math.py und geben Sie dann diesen Code ein.

# my_math.py
def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    >>> add(1, 2)
    3
    """
    return x + y


if __name__ == "__main__":
    import doctest

    doctest.testmod()

Nach dem Login kopieren

Es ist derselbe Code wie docstring example, aber ich füge in der letzten Zeile des Codes example und doctest hinzu. Um den Dokumentstring zu testen, führen Sie einfach die Datei python my_math.py aus. Wenn es keine Ausgabe gibt, bedeutet das, dass Ihr Beispiel den Test besteht. Wenn Sie die Ausgabe sehen möchten, führen Sie sie im ausführlichen Modus python my_math.py -v aus. Sie sehen diese Ausgabe.

Trying:
    add(1, 2)
Expecting:
    3
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.add
1 tests in 2 items.
1 passed and 0 failed.
Test passed
Nach dem Login kopieren

Wenn Sie im Codebeispiel einen Fehler machen, wird ein Fehler zurückgegeben.

# my_math.py
def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    >>> add(2, 2) # <-- I change it here
    3
    """
    return x + y


if __name__ == "__main__":
    import doctest

    doctest.testmod()

Nach dem Login kopieren

die Ausgabe:

**********************************************************************
File "~/typescript-in-python/my_math.py", line 12, in __main__.add
Failed example:
    add(2, 2) # <-- I change it here
Expected:
    3
Got:
    4
**********************************************************************
1 items had failures:
   1 of   1 in __main__.add
***Test Failed*** 1 failures.
Nach dem Login kopieren

Großartig! Jetzt kann ich meinen Dokumentstring testen. Aber die Vorbehalte sind:

  1. doctest prüft nur das Beispieles überprüft nicht die Parameter der Kommentarfunktion und gibt nicht zurück
  2. Doctest muss den Code ausführen wie andere Testtools, um zu überprüfen, ob er korrekt ist oder nicht. Doctest muss das Codebeispiel ausführen. Wenn Ihr Code externe Tools wie eine Datenbank oder einen SMTP-Server benötigt (z. B. das Senden einer E-Mail), ist es schwierig, ihn mit doctest zu testen.

Python-Eingabe

Manchmal müssen Sie den Code nicht ausführen, um zu überprüfen, ob der Code korrekt ist oder nicht. Sie benötigen lediglich den Eingabetyp und den Ausgabetyp. Wie? Betrachten Sie dieses Beispiel.

def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x + y

def sub(x, y):
    """Substract two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x - y

a = add(2, 1)
b = add(1, 1)
c = sub(a, b)
Nach dem Login kopieren

Funktion add gibt einen int zurück und function sub benötigt zwei int als Eingabeparameter. Wenn ich zwei Return from Add-Funktionen verwende und sie dann wie im obigen Beispiel auf einen Unterparameter füge, wird es einen Fehler geben? Natürlich nicht, weil die Unterfunktion ein int benötigt und Sie auch ein int eingeben.

Seit Python 3.5 verfügt Python über einen integrierten Typ namens Typing. Mit der Eingabe können Sie Ihrer Funktion wie im folgenden Beispiel einen Typ hinzufügen.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

a = add(1, 2)
Nach dem Login kopieren

Instead put it on your docstring you put it on the function. Typing is supported on many editor. If you use vscode you can hover on variable and it will shown it's type.
Writing Python code like Typescript

Nice now our code will have a type safety. eeehhhh not realy. If I intentionally use function incorrectlly like this.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add(1, [])
print(res)
Nach dem Login kopieren

It will show error

Traceback (most recent call last):
  File "~/typescript-in-python/main.py", line 5, in <module>
    res = add(1, [])
          ^^^^^^^^^^
  File "~/typescript-in-python/main.py", line 3, in add
    return x + y
           ~~^~~
TypeError: unsupported operand type(s) for +: 'int' and 'list'
Nach dem Login kopieren

But it doesn't show that you put incorrect type. Even worse if you use it like this.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add("hello", "world")
print(res)
Nach dem Login kopieren
Nach dem Login kopieren

It will succeed. It must be error because you put incorrect type.

helloworld
Nach dem Login kopieren

Why python typing doesn't have type checker by default??. Based on pep-3107 it said

Before launching into a discussion of the precise ins and outs of Python 3.0’s function annotations, let’s first talk broadly about what annotations are and are not:

  1. Function annotations, both for parameters and return values, are completely optional.
  2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time. By itself, Python does not attach any particular meaning or significance to annotations. Left to its own, Python simply makes these expressions available as described in Accessing Function Annotations below.

The only way that annotations take on meaning is when they are interpreted by third-party libraries. ...

So in python typing is like a decorator in typescript or java it doesn't mean anything. You need third party libraries todo type checking. Let's see some library for typechecking.

Python typing + type checker

Here are libraries for typechecking in python. For example we will typecheck this wrong.py file

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add("hello", "world")
print(res)
Nach dem Login kopieren
Nach dem Login kopieren

1.mypy

The "OG" of python type checker. To install it just using pip pip install mypy. Now let's use mypy to typecheck this file. Run mypy wrong.py. It will shown type error which is nice.

wrong.py:5: error: Argument 1 to "add" has incompatible type "str"; expected "int"  [arg-type]
wrong.py:5: error: Argument 2 to "add" has incompatible type "str"; expected "int"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)
Nach dem Login kopieren

btw you can run mypy on entire project by using mypy ..

2.pyright

Another typechecker is pyright. It created by microsoft. It's same like mypy install through pip pip install pyright. Then run it pyright wrong.py. It will shown this error.

~/typescript-in-python/wrong.py
  ~/typescript-in-python/wrong.py:5:11 - error: Argument of type "Literal['hello']" cannot be assigned to parameter "x" of type "int" in function "add"
    "Literal['hello']" is incompatible with "int" (reportArgumentType)
  ~/typescript-in-python/wrong.py:5:20 - error: Argument of type "Literal['world']" cannot be assigned to parameter "y" of type "int" in function "add"
    "Literal['world']" is incompatible with "int" (reportArgumentType)
2 errors, 0 warnings, 0 informations
Nach dem Login kopieren

It said that it's more faster than mypy but I found that's not much diffrent. Maybe my code base it's to small. Also pyright implement more python standard than mypy you can see on https://microsoft.github.io/pyright/#/mypy-comparison. Personaly I prefer mypy than pyright because the error message were more readable.

3.pylyzer

Speaking of performance and speed another new python typechecker pylyzer. It's written in rust. You can install it through pip pip install pylyzer or through cargo (rust package manager) cargo install pylyzer --locked. Then run it pylyzer wrong.py. It will shown this error.

Start checking: wrong.py
Found 2 errors: wrong.py
Error[#2258]: File wrong.py, line 5, <module>.res

5 | res = add("hello", "world")
  :           -------
  :                 |- expected: Int
  :                 `- but found: {"hello"}

TypeError: the type of add::x (the 1st argument) is mismatched

Error[#2258]: File wrong.py, line 5, <module>.res

5 | res = add("hello", "world")
  :                    -------
  :                          |- expected: Int
  :                          `- but found: {"world"}

TypeError: the type of add::y (the 2nd argument) is mismatched
Nach dem Login kopieren

So far this is the most readable and beautiful error message. It's reminds me of rust compiler error. Speed, performance and most readable error message, I think I will choose to using pylyzer if the package already stable. The problem is at the time I write this blog, pylyzer still in beta. It can only typecheck your code base, it haven't support external depedencies.

Conclusion

Alright we successfully write python code like typescript (kinda). There is more way to using python typing module other than check simple type (str, int, bool etc). Maybe I will cover more advance type it in next blog. Maybe you guys have opinion about this, know better typechecker other then those 3, found other way to do typecheck in python or other. let me know on comment section below. As always Happy Coding.

Das obige ist der detaillierte Inhalt vonPython-Code wie Typescript schreiben. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage