J'ai aimé écrire TypeScript pendant environ un an, mais j'ai commencé à utiliser Python3 pour les entreprises il y a environ un mois. Python est simple et amusant, mais je l'ai hérité des autres, il est difficile à développer et je n'ai aucun type.
La partie la plus difficile est la lecture de code, que renvoie cette fonction? Je ne sais pas en un coup d'œil ce que contient cette variable. …… Il n'y a pas de type comme document. S'il n'existe pas, j'ai décidé de l'introduire, j'ai donc décidé d'introduire l'annotation de type et mypy.
Même si vous introduisez soudainement le moule à la robustesse, ce sera douloureux au contraire, j'ai donc pris la méthode d'introduction du moule sans en faire trop de l'état sans moule, et j'ai pu l'introduire assez bien, donc cette fois la procédure à ce moment-là et les connaissances obtenues , Je voudrais vous présenter quelques conseils.
Cette fois, je teste dans un environnement pipenv, mais je pense que mypy ne change pas même avec pip etc.
python3.7.5 pipenv, version 2018.11.26
Veuillez vous référer ici pour l'introduction de pipenv https://pipenv-ja.readthedocs.io/ja/translate-ja/index.html
Installez mypy dans le répertoire du projet.
cd ./myproject
pipenv install mypy -d
Si mypy est installé dans le pip global et que vous voulez taper statiquement le code sous le répertoire src, vous pouvez taper avec mypy. / Src
, mais si mypy est uniquement dans pipenv, vous obtiendrez une erreur. Devenir.
$ mypy ./src
Command 'mypy' not found, but can be installed with:
sudo apt install mypy
Si vous entrez dans l'environnement virtuel de pipenv, vous pouvez l'exécuter sans aucun problème.
$ pipenv shell
(myproject) $ mypy ./src
Success: no issues found in 2 source files
Il est difficile d'entrer dans l'environnement virtuel à chaque fois, alors enregistrons le script dans Pipfile.
[scripts]
type-check = "mypy ./src"
Référence https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#custom-script-shortcuts
Puisque la commande exécutée par le script est exécutée dans l'environnement de pipenv, mypy peut être exécuté sans exécuter pipenv shell
.
$ pipenv run type-check
mypy.ini: No [mypy] section in config file
Success: no issues found in 1 source files
Utilisez cette commande lors de la vérification de type, par exemple dans CI.
Je pense que la plupart des gens le savent, mais passons en revue les types de base. Parmi les documents officiels, je pense que les 8 types suivants sont tout au plus utilisés dans l'inspection de type normale. https://docs.python.org/ja/3.7/library/stdtypes.html
Si vous vous perdez, vous pouvez le mettre dans le type de fonction intégré () et voir le résultat, de sorte que vous n'ayez même pas à vous en souvenir.
Type de type | Nom du modèle | Exemple |
---|---|---|
Type booléen | bool | True |
Type entier | int | 10 |
Type à virgule flottante | float | 1.2 |
Type de séquence de texte (type chaîne de caractères) | str | 'hoge' |
Type de liste | list | [1, 2, 3] |
Type de taple | tuple | ('a', 'b') |
Type de dictionnaire (type de mappage) | dict | { 'a': 'hoge', 'b': 'fuga'} |
Type collectif | set | { 'j', 'k', 'l'} |
Commençons par créer une fonction non typée.
Créez my_module.py
sous ./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'))
J'ai essayé d'en faire une fonction qui reçoit l'heure de 0 à 24 et renvoie un message d'accueil. Maintenant, quand j'exécute une vérification de type ...
$ pipenv run type-check
Success: no issues found in 1 source file
Pas d'erreur! La raison en est que puisque l'annotation de type n'est pas effectuée, la valeur de retour et l'argument de la fonction seront le type de base Any (type de tout). (S'il y a une base de code existante, je pense que c'est bien car cela ne provoque pas d'erreur lors de l'introduction du type et cela ne me brise pas le cœur.) Ensuite, ajoutons une annotation de type.
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'))
Ajout d'une annotation de type sur la première ligne pour indiquer "recevoir un type entier et renvoyer un type chaîne". Essayez de réexécuter la vérification de type dans cet état.
$ 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)
Cette fois, j'ai une erreur correctement. Quand j'ai lu le message d'erreur, j'ai passé la chaîne de caractères lors de l'appel de la fonction get_greeting sur la 14e ligne. Si vous l'exécutez tel quel, une erreur d'exécution se produira. Si vous modifiez le code pour passer un type entier et effectuez à nouveau la vérification de type, l'erreur disparaîtra.
print(get_greeting(10))
En ajoutant des annotations de type, nous avons pu rendre le code plus facile à comprendre et éviter les erreurs d'exécution.
Même dans ce cas, vous souhaitez parfois forcer les annotations de type. Dans ce cas, créez un fichier de paramètres.
mypy.ini
[mypy]
python_version = 3.7
disallow_untyped_calls = True
disallow_untyped_defs = True
Modifiez le script pour spécifier également le fichier de configuration.
[scripts]
type-check = "mypy ./src --config-file ./mypy.ini"
En faisant cela, si vous oubliez d'ajouter l'annotation de type, une erreur sera renvoyée.
$ 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)
Référence https://mypy.readthedocs.io/en/latest/config_file.html
Bien qu'il puisse être installé dans une base de code existante avec une tolérance Any, il est inévitable qu'un grand nombre d'erreurs se produise lors de l'installation si la base de code d'origine est volumineuse. Veuillez attendre un moment avant que votre cœur soit sur le point de se briser. 90% des erreurs devraient disparaître simplement en exécutant les deux suivantes.
Référence https://mypy.readthedocs.io/en/latest/existing_code.html#start-small
Par exemple, si vous avez le code suivant:
import request
Lorsque j'effectue une vérification de type, j'obtiens une erreur de 3 lignes.
$ 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)
C'est parce qu'il n'y a pas de fichier de définition de type (stub) pour le module importé.
Comme il est difficile de préparer tous les fichiers de définition de type au moment de l'installation, ** ignorez ** dans les paramètres de mypy.ini.
mypy.ini
[mypy-request.*]
ignore_missing_imports = True
Cela ignorera le manque de stub dans l'importation depuis request
.
$ pipenv run type-check
Success: no issues found in 1 source file
La paix est maintenant de retour.
Je ne peux pas le recommander beaucoup, mais il est souvent utilisé lorsque vous souhaitez attribuer le mauvais type de test, etc. Ajoutez un commentaire «# type: ignore» à la fin de la ligne de code que vous souhaitez ignorer.
print(get_greeting('hoge')) #type: ignore
Vous pouvez supprimer l'erreur de type qui aurait dû se produire sur cette ligne.
Vous pouvez dire: "Non, je veux utiliser le stub." Cependant, il n'y a aucune garantie que les développeurs de modules tiers disposent de stubs. Il est difficile de le fabriquer soi-même. Dans un tel cas, générons-le automatiquement.
Vous pouvez générer automatiquement un stub en spécifiant un fichier ou un répertoire avec la commande stubgen, qui peut être utilisé en insérant mypy.
$ stubgen foo.py bar.py
S'il s'agit d'un module importé, vous pouvez vérifier le chemin du module avec * .__ path __
, vous pouvez donc également créer un stub en spécifiant directement ce chemin.
>>> import request
>>> request.__path__
['/home/username/.local/share/virtualenvs/myproject-xxxxxxxx/lib/python3.7/site-packages/request']
>>>
Une fois que vous connaissez le chemin, exécutez stubgen.
(myproject) $ stubgen /home/username/.local/share/virtualenvs/myproject-xxxxxxxx/lib/python3.7/site-packages/request
Processed 1 modules
Generated out/request/__init__.pyi
L'exécution de stubgen crée un répertoire out dans la racine du projet, spécifiez donc ce chemin dans mypy.ini afin que mypy puisse le voir.
mypy.ini
[mypy]
python_version = 3.7
mypy_path = ./out
L'inspection de type est maintenant réussie.
$ pipenv run type-check
Success: no issues found in 1 source file
Les types générés par stubgen ne sont pas parfaits. Ce sera presque n'importe quel type, donc si vous voulez l'utiliser sérieusement, vous devez modifier le fichier stub vous-même.
Référence https://github.com/python/mypy/blob/master/docs/source/stubgen.rst
En plus du type intégré, il existe d'autres types qui sont souvent utilisés, je vais donc les présenter.
Dans mypy, à l'exception des types intégrés, le module typing
et le module typing_extensions
appellent et utilisent des classes de ces types.
C'est un peu différent du tapuscript, mais comme la plupart des types, y compris les types génériques, sont couverts dans ces modules, cela semble être satisfaisant pour ceux qui veulent faire de la programmation de type.
Référence https://mypy.readthedocs.io/en/latest/
Optional
Les fonctions telles que le renvoi normal d'un entier et le renvoi None si une valeur incorrecte est reçue sont courantes. La valeur de retour dans ce cas est int ou None, mais facultatif peut l'exprimer.
from typing import Optional
def sample(time: int) -> Optional[int]:
if 24 < time:
return None
else:
return time
List, Dict Une liste d'entiers, une liste de chaînes, etc. peut être représentée par List.
from typing import List
#Liste d'entiers
intList: List[int] = [1, 2, 3, 4]
#Liste des chaînes
strList: List[str] = ['a', 'b', 'c']
De même, si vous utilisez Dict, vous pouvez exprimer quelque chose comme "la clé est un caractère et la valeur est un entier" même dans un type de dictionnaire.
from typing import Dict
#Type de dictionnaire où la clé est un caractère et la valeur est un entier
strIntDict: Dict[str, int] = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Union
Vous pouvez créer un type d'union qui combine plusieurs personnes.
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
Bien sûr, tout type est également possible si vous ne souhaitez pas spécifier le type
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
Vous pouvez exprimer le type d'une fonction avec Callable.
from typing import Callable
#Définition de type de fonction qui reçoit un argument de type entier et renvoie un type chaîne
func: Callable[[int], str]
def sample(num: int) -> str:
return str(num)
func = sample
TypedDict
Vous souhaiterez peut-être spécifier la valeur d'une clé dans la carte et spécifier le type de valeur de la clé. S'il s'agit d'un script typographique, il est exprimé par interface.
Par exemple, supposons que vous ayez une valeur de type dictionnaire appelée movie.
movie = {'name': 'Blade Runner', 'year': 1982}
move a des clés appelées nom et année, mais si vous mettez accidentellement un entier dans le nom ou si vous mettez une chaîne de caractères dans l'année lors de l'écrasement, ce serait un problème. TypedDict le rend facile à exprimer en tant que type.
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")
Il peut également être exprimé sous la forme d'une classe. Personnellement, je préfère cela car cela ressemble à une interface TS.
from typing_extensions import TypedDict
class Movie(TypedDict):
name: str
year: int
Veuillez consulter le document officiel pour plus de détails. https://mypy.readthedocs.io/en/latest/more_types.html#typeddict
Qu'as-tu pensé? J'espère que vous pensez que démarrer un type en Python est un obstacle étonnamment faible.
Si vous avez du mal sans type en Python, introduisons-le maintenant! C'est plus que ce à quoi je m'attendais, donc je peux être heureux.
Le mécontentement est que le soutien de l'éditeur n'est pas très solide. J'utilise Pyright comme extension de VS-Code, mais j'aimerais changer s'il y a quelque chose de mieux.
Bonne fin d'année!
Recommended Posts