Ich habe ungefähr ein Jahr lang gerne TypeScript geschrieben, aber vor ungefähr einem Monat habe ich angefangen, Python3 für geschäftliche Zwecke zu verwenden. Python ist einfach und macht Spaß, aber ich habe es von anderen geerbt, und es ist schwer zu entwickeln und hat keinen Typ.
Der schwierigste Teil ist das Lesen von Code. Was gibt diese Funktion zurück? Ich weiß nicht auf einen Blick, was in dieser Variablen enthalten ist. …… Es gibt keinen Typ als Dokument. Wenn es nicht existiert, habe ich beschlossen, es einzuführen, also habe ich beschlossen, Typanmerkungen und mypy einzuführen.
Selbst wenn Sie die Form plötzlich in die Robustheit einführen, wird es im Gegenteil schmerzhaft sein. Deshalb habe ich die Methode zum Einführen der Form gewählt, ohne sie aus dem Zustand ohne die Form zu übertreiben, und ich konnte sie recht gut einführen, also diesmal das Verfahren zu diesem Zeitpunkt und das gewonnene Wissen Ich möchte einige Tipps vorstellen.
Dieses Mal teste ich in einer Pipenv-Umgebung, aber ich glaube nicht, dass es einen besonderen Unterschied zwischen Mypy und Pip usw. gibt.
python3.7.5 pipenv, version 2018.11.26
Die Einführung von pipenv finden Sie hier https://pipenv-ja.readthedocs.io/ja/translate-ja/index.html
Installieren Sie mypy im Projektverzeichnis.
cd ./myproject
pipenv install mypy -d
Wenn mypy im globalen Pip installiert ist und Sie den Code im Verzeichnis src statisch überprüfen möchten, können Sie mit mypy. / Src
tippen. Wenn sich mypy jedoch nur in pipenv befindet, wird eine Fehlermeldung angezeigt. Werden.
$ mypy ./src
Command 'mypy' not found, but can be installed with:
sudo apt install mypy
Wenn Sie die virtuelle Umgebung von pipenv aufrufen, können Sie diese problemlos ausführen.
$ pipenv shell
(myproject) $ mypy ./src
Success: no issues found in 2 source files
Es ist schwierig, jedes Mal in die virtuelle Umgebung zu gelangen. Registrieren Sie das Skript daher in Pipfile.
[scripts]
type-check = "mypy ./src"
Referenz https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#custom-script-shortcuts
Da der vom Skript ausgeführte Befehl in der Umgebung von pipenv ausgeführt wird, kann mypy ausgeführt werden, ohne "pipenv shell" auszuführen.
$ pipenv run type-check
mypy.ini: No [mypy] section in config file
Success: no issues found in 1 source files
Verwenden Sie diesen Befehl, wenn Sie eine Typprüfung durchführen, z. B. in CI.
Ich denke, die meisten Leute wissen es, aber lassen Sie uns die Grundtypen überprüfen. Von den offiziellen Dokumenten denke ich, dass die folgenden 8 Typen höchstens bei der normalen Typprüfung verwendet werden. https://docs.python.org/ja/3.7/library/stdtypes.html
Wenn Sie sich verlaufen, können Sie es in den integrierten Funktionstyp () einfügen und das Ergebnis anzeigen, sodass Sie sich nicht einmal daran erinnern müssen.
Art des Typs | Modellname | Beispiel |
---|---|---|
Boolescher Typ | bool | True |
Ganzzahliger Typ | int | 10 |
Gleitkomma-Typ | float | 1.2 |
Textsequenztyp (Zeichenkettentyp) | str | 'hoge' |
Listentyp | list | [1, 2, 3] |
Taple-Typ | tuple | ('a', 'b') |
Wörterbuchtyp (Zuordnungstyp) | dict | { 'a': 'hoge', 'b': 'fuga'} |
Kollektiver Typ | set | { 'j', 'k', 'l'} |
Lassen Sie uns zunächst eine untypisierte Funktion erstellen. Erstellen Sie "my_module.py" unter ./src.
my_module.py
def get_greeting(time):
if 4 <= time < 10:
return 'Good morning!'
elif 10 <= time < 14:
return 'Hello!'
elif 14 <= time < 24:
return 'Goog afternoon.'
elif 0 <= time < 4:
return 'zzz..'
else:
return ''
if __name__ == "__main__":
print(get_greeting('morning'))
Ich habe versucht, daraus eine Funktion zu machen, die die Zeit von 0 bis 24 empfängt und eine Begrüßung zurückgibt. Wenn ich jetzt eine Typprüfung durchführe ...
$ pipenv run type-check
Success: no issues found in 1 source file
Kein Fehler! Der Grund dafür ist, dass der Rückgabewert und das Argument der Funktion der grundlegende Any-Typ (Typ von irgendetwas) sind, da keine Typanmerkung ausgeführt wird. (Wenn es eine vorhandene Codebasis gibt, denke ich, dass dies in Ordnung ist, da es beim Einführen des Typs keinen Fehler verursacht und mein Herz nicht bricht.) Als nächstes fügen wir eine Typanmerkung hinzu.
def get_greeting(time: int) -> str:
if 4 <= time < 10:
return 'Good morning!'
elif 10 <= time < 14:
return 'Hello!'
elif 14 <= time < 20:
return 'Goog afternoon.'
elif 0 <= time < 4:
return 'zzz..'
else:
return None
if __name__ == "__main__":
print(get_greeting('morning'))
In der ersten Zeile wurde eine Typanmerkung hinzugefügt, die angibt, dass "ein ganzzahliger Typ empfangen und ein Zeichenfolgentyp zurückgegeben werden soll". Versuchen Sie in diesem Zustand erneut, die Typprüfung durchzuführen.
$ pipenv run type-check
src/my_module.py:14: error: Argument 1 to "get_greeting" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)
Diesmal habe ich einen Fehler richtig bekommen. Beim Lesen der Fehlermeldung habe ich beim Aufrufen der Funktion get_greeting in der 14. Zeile die Zeichenfolge übergeben. Wenn Sie es so ausführen, wie es ist, tritt ein Laufzeitfehler auf. Wenn Sie den Code so ändern, dass ein ganzzahliger Typ übergeben und die Typprüfung erneut durchgeführt wird, verschwindet der Fehler.
print(get_greeting(10))
Durch Hinzufügen von Typanmerkungen konnten wir den Code verständlicher machen und Laufzeitfehler vermeiden.
Trotzdem gibt es Zeiten, in denen Sie Typanmerkungen erzwingen möchten. Erstellen Sie in diesem Fall eine Einstellungsdatei.
mypy.ini
[mypy]
python_version = 3.7
disallow_untyped_calls = True
disallow_untyped_defs = True
Ändern Sie das Skript, um auch die Konfigurationsdatei anzugeben.
[scripts]
type-check = "mypy ./src --config-file ./mypy.ini"
Wenn Sie auf diese Weise vergessen, die Typanmerkung hinzuzufügen, wird ein Fehler zurückgegeben.
$ pipenv run type-check
src/my_module.py:1: error: Function is missing a type annotation
src/my_module.py:14: error: Call to untyped function "get_greeting" in typed context
Found 2 errors in 1 file (checked 1 source file)
Referenz https://mypy.readthedocs.io/en/latest/config_file.html
Obwohl es mit einer beliebigen Toleranz in einer vorhandenen Codebasis installiert werden kann, ist es unvermeidlich, dass während der Installation eine große Anzahl von Fehlern auftritt, wenn die ursprüngliche Codebasis groß ist. Bitte warten Sie einen Moment, bevor Ihr Herz bricht. 90% der Fehler sollten durch Ausführen der folgenden beiden Fehler verschwinden.
Referenz https://mypy.readthedocs.io/en/latest/existing_code.html#start-small
Zum Beispiel, wenn Sie den folgenden Code haben:
import request
Wenn ich eine Typprüfung durchführe, erhalte ich einen Fehler von 3 Zeilen.
$ pipenv run type-check
src/my_module.py:1: error: Cannot find implementation or library stub for module named 'request'
src/my_module.py:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
Dies liegt daran, dass für das importierte Modul keine Typdefinitionsdatei (Stub) vorhanden ist.
Da es zum Zeitpunkt der Installation schwierig ist, alle Typdefinitionsdateien vorzubereiten, ** ignorieren ** Sie in den Einstellungen von mypy.ini.
mypy.ini
[mypy-request.*]
ignore_missing_imports = True
Dies ignoriert das Fehlen eines Stubs beim Import von "Anfrage".
$ pipenv run type-check
Success: no issues found in 1 source file
Der Frieden ist jetzt zurück.
Ich kann es nicht sehr empfehlen, aber es wird oft verwendet, wenn Sie im Test usw. den falschen Typ zuweisen möchten. Fügen Sie am Ende der Codezeile, die Sie ignorieren möchten, einen Kommentar "# type: ignore" hinzu.
print(get_greeting('hoge')) #type: ignore
Sie können den Typfehler unterdrücken, der in dieser Zeile hätte auftreten sollen.
Sie können sagen: "Nein, ich möchte Stub verwenden." Es gibt jedoch keine Garantie dafür, dass den Entwicklern von Modulen von Drittanbietern Stubs zur Verfügung stehen. Es ist mühsam, es selbst zu machen. In einem solchen Fall generieren wir es automatisch.
Sie können automatisch einen Stub generieren, indem Sie mit dem Befehl stubgen eine Datei oder ein Verzeichnis angeben, die bzw. das durch Einfügen von mypy verwendet werden kann.
$ stubgen foo.py bar.py
Wenn es sich um ein importiertes Modul handelt, können Sie den Pfad des Moduls mit * .__ path __
überprüfen, sodass Sie auch einen Stub erstellen können, indem Sie diesen Pfad direkt angeben.
>>> import request
>>> request.__path__
['/home/username/.local/share/virtualenvs/myproject-xxxxxxxx/lib/python3.7/site-packages/request']
>>>
Sobald Sie den Pfad kennen, führen Sie stubgen aus.
(myproject) $ stubgen /home/username/.local/share/virtualenvs/myproject-xxxxxxxx/lib/python3.7/site-packages/request
Processed 1 modules
Generated out/request/__init__.pyi
Wenn Sie stubgen ausführen, wird ein Out-Verzeichnis im Projektstamm erstellt. Geben Sie diesen Pfad in mypy.ini an, damit mypy ihn sehen kann.
mypy.ini
[mypy]
python_version = 3.7
mypy_path = ./out
Die Typprüfung ist jetzt bestanden.
$ pipenv run type-check
Success: no issues found in 1 source file
Die von stubgen erzeugten Typen sind nicht perfekt. Es wird fast jeder Typ sein. Wenn Sie es also ernsthaft verwenden möchten, müssen Sie die Stub-Datei selbst ändern.
Referenz https://github.com/python/mypy/blob/master/docs/source/stubgen.rst
Zusätzlich zum eingebauten Typ gibt es andere Typen, die häufig verwendet werden, daher werde ich sie vorstellen. In mypy rufen mit Ausnahme der eingebauten Typen das Modul "typing" und das Modul "typing_extensions" Klassen dieser Typen auf und verwenden sie. Es unterscheidet sich ein wenig von Typoskript, aber da die meisten Typen, einschließlich generischer Typen, in diesen Modulen behandelt werden, scheint es für diejenigen, die Typprogrammierung durchführen möchten, zufriedenstellend zu sein.
Referenz https://mypy.readthedocs.io/en/latest/
Optional
Funktionen wie das normale Zurückgeben einer Ganzzahl und das Zurückgeben von None, wenn ein falscher Wert empfangen wird, sind häufig. Der Rückgabewert ist in diesem Fall int oder None, aber Optional kann dies ausdrücken.
from typing import Optional
def sample(time: int) -> Optional[int]:
if 24 < time:
return None
else:
return time
List, Dict Eine Liste von Ganzzahlen, eine Liste von Zeichenfolgen usw. kann durch Liste dargestellt werden.
from typing import List
#Liste der ganzen Zahlen
intList: List[int] = [1, 2, 3, 4]
#Liste der Zeichenfolgen
strList: List[str] = ['a', 'b', 'c']
Wenn Sie Dict verwenden, können Sie auch in einem Wörterbuchtyp etwas wie "Schlüssel ist ein Zeichen und Wert ist eine Ganzzahl" ausdrücken.
from typing import Dict
#Wörterbuchtyp, bei dem der Schlüssel ein Zeichen und der Wert eine Ganzzahl ist
strIntDict: Dict[str, int] = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Union
Sie können einen Gewerkschaftstyp erstellen, der mehrere Personen kombiniert.
from typing import Union
strOrInt: Union[str, int] = 1 # OK
strOrInt = 'hoge' # OK
strOrInt = None # error: Incompatible types in assignment (expression has type "None", variable has type "Union[str, int]")
Any
Natürlich ist auch jeder Typ möglich, wenn Sie den Typ nicht angeben möchten
from typing import Any
string: str = 'hoge'
any: Any = string
any = 10 # OK
notAny = string
notAny = 10 # error: Incompatible types in assignment (expression has type "int", variable has type "str")
Callable
Sie können den Typ einer Funktion mit Callable ausdrücken.
from typing import Callable
#Funktionstypdefinition, die ein Ganzzahlargument empfängt und einen Zeichenfolgentyp zurückgibt
func: Callable[[int], str]
def sample(num: int) -> str:
return str(num)
func = sample
TypedDict
Möglicherweise möchten Sie den Wert eines Schlüssels in der Zuordnung angeben und angeben, welchen Werttyp der Schlüssel hat. Wenn es sich um Typoskript handelt, wird es durch die Schnittstelle ausgedrückt.
Angenommen, Sie haben einen Wörterbuchwert namens movie.
movie = {'name': 'Blade Runner', 'year': 1982}
move hat Schlüssel mit den Namen name und year. Wenn Sie jedoch beim Überschreiben versehentlich eine Ganzzahl in den Namen oder eine Zeichenfolge in das Jahr eingeben, ist dies ein Problem. TypedDict erleichtert das Ausdrücken als Typ.
from typing_extensions import TypedDict
Movie = TypedDict('Movie', {'name': str, 'year': int})
movie1: Movie = {'name': 'Blade Runner', 'year': 1982} # OK
movie2: Movie = {'name': 'Blade Runner', 'year': '1982'} # error: Incompatible types (expression has type "str", TypedDict item "year" has type "int")
Es kann auch in Form einer Klasse ausgedrückt werden. Persönlich bevorzuge ich dies, weil es wie eine TS-Schnittstelle aussieht.
from typing_extensions import TypedDict
class Movie(TypedDict):
name: str
year: int
Bitte überprüfen Sie das offizielle Dokument für Details. https://mypy.readthedocs.io/en/latest/more_types.html#typeddict
Was haben Sie gedacht? Ich hoffe, Sie glauben, dass das Starten eines Typs in Python eine überraschend niedrige Hürde ist.
Wenn Sie Schwierigkeiten haben, Python nicht einzugeben, stellen wir es jetzt vor! Es ist mehr als ich erwartet hatte, also kann ich glücklich sein.
Die Unzufriedenheit ist, dass die Unterstützung des Herausgebers nicht sehr solide ist. Ich verwende Pyright als Erweiterung von VS-Code, möchte aber wechseln, wenn es etwas Besseres gibt.
Habt ein schönes Jahresende!
Recommended Posts