[Python] Vérification simple du type d'argument avec la classe de données

introduction

--Lorsque je pratiquais l'écriture de code avec DDD, j'ai essayé et erroné la méthode de vérification de type de ValueObject, donc un mémo à ce moment-là

Conclusion

Qu'est-ce qu'une classe de données?

--__ init__ () est généré automatiquement.

Écriture normale


class Users:
    def __init__(self, user_name: str, user_id: int):
        self.user_name = user_name
        self.user_id = user_id

Comment écrire en classe de données


class User:
    user_name: str
    user_id: int

Il vaut mieux écrire dans la classe de données!

Pouvez-vous taper check avec dataclass?

C'est le sujet principal.

Je tape avec ʻuser_name: str et ʻuser_id: int, et il semble que je vérifie le type, mais c'est en fait une annotation normale.

Même s'il est spécifié comme type str, il est entré comme type int.

code


import dataclasses

@dataclasses.dataclass(frozen=True)
class User:
    user_name: str
    user_id: int

c = User(user_name='Hinoya Koma', user_id=1)
print(f'{c.user_id}:{c.user_name}')

c_fail = User(user_name=2, user_id='Flamme de Serre')
print(f'{c_fail.user_id}:{c_fail.user_name}')

Résultat d'exécution


> python .\test de classe de données.py
1:Hinoya Koma
Flamme de Serre:2

Tout peut être mis comme ci-dessus.

Effectuer une vérification de type à l'initialisation avec __post_init__ ()

En définissant la fonction __post_init__ () dans la classe de données comme indiqué ci-dessous, vous pouvez écrire le traitement au moment de l'initialisation. Tapons check ici en utilisant ʻis instance`


@dataclasses.dataclass(frozen=True)
class User:
    user_name: str
    user_id: int

    def __post_init__(self):
        if not isinstance(self.user_name, str):
            raise Exception
        if not isinstance(self.user_id, int):
            raise Exception

Résultat d'exécution


> python .\test de classe de données.py
1:Hinoya Koma
Traceback (most recent call last):
  File ".\test de classe de données.py", line 17, in <module>
    c_fail = User(user_name=2, user_id='Flamme de Serre')
  File "<string>", line 4, in __init__
  File ".\test de classe de données.py", line 10, in __post_init__
    raise Exception
Exception

Comme prévu, j'ai pu faire une exception!

La vérification de type est difficile lorsqu'il y a de nombreuses variables

Dans les exemples jusqu'à présent, il y en avait deux, ʻuser_name et ʻuser_id, mais c'est difficile s'il y en a beaucoup. Par conséquent, je veux limiter le nombre de vérifications de type écrites dans __post_init__ au nombre de variables.

C'est pourquoi j'ai écrit ce qui suit.


@dataclasses.dataclass(frozen=True)
class User:
    user_name: str
    user_id: int

    def __post_init__(self):
        # 1.Convertir l'instance utilisateur en type dict avec asdict
        user_dict = dataclasses.asdict(self)
        # 2. self.__annotations__Obtenez le type de valeur attendu à partir de
        #    self.__annotations__Contient le nom de l'argument et le type spécifié par celui-ci en tant que dict.
        #A partir de maintenant, récupérez le type de la valeur attendue et vérifiez le type avec isinstance.
        for user_arg_name, user_arg_expected_type in self.__annotations__.items():
            # 3.est l'exécution de l'instance
            #À partir du type Utilisateur converti en dict, spécifiez la variable cible avec la clé de l'annotation et exécutez-la.
            if not isinstance(user_dict[user_arg_name], user_arg_expected_type):
                print(f'{user_arg_name} is not ok')
                raise Exception
            else:
                print(f'{user_arg_name} is ok')

J'ai mis les détails dans les commentaires. Obtenez tous les arguments (variables) que l'instance a avec ʻasdict, obtenez le type de valeur attendue avec auto .__ annotations__, et multipliez par ʻis instance. En excluant les commentaires et `` imprimer '', vous pouvez écrire en 4 ou 5 lignes environ

Résultat d'exécution

> python .\test de classe de données.py
user_name is ok
user_id is ok
1:Hinoya Koma
user_name is not ok
Traceback (most recent call last):
  File ".\test de classe de données.py", line 21, in <module>
    c_fail = User(user_name=2, user_id='Flamme de Serre')
  File "<string>", line 4, in __init__
  File ".\test de classe de données.py", line 13, in __post_init__
    raise Exception
Exception

Faiblesses de ce code

Si vous spécifiez le type avec la saisie, etc., cette méthode ne fonctionnera pas. Ce qui suit est celui dans lequel le type d'argument est spécifié par List [int] sans commentaires.


import dataclasses
from typing import List 

@dataclasses.dataclass(frozen=True)
class User:
    user_name: str
    user_id: int
    status_list: List[int]

    def __post_init__(self):
        user_dict = dataclasses.asdict(self)
        for user_arg_name, user_arg_expected_type in self.__annotations__.items():
            if not isinstance(user_dict[user_arg_name], user_arg_expected_type):
                print(f'{user_arg_name} is not ok')
                raise Exception
            else:
                print(f'{user_arg_name} is ok')

status_list=[50,51]

c = User(user_name='Hinoya Koma', user_id=1, status_list=status_list)
print(f'{c.user_id}:{c.user_name}')

c_fail = User(user_name=2, user_id='Flamme de Serre', status_list=status_list)
print(f'{c_fail.user_id}:{c_fail.user_name}')

J'obtiens une erreur dans la liste comme indiqué ci-dessous.

> python .\.py
user_name is ok
user_id is ok
Traceback (most recent call last):
  File ".\.py", line 27, in <module>
    c = User(user_name='', user_id=1, status_list=status_list)
  File "<string>", line 5, in __init__
  File ".\.py", line 19, in __post_init__
    if not isinstance(user_dict[user_arg_name], user_arg_expected_type):
  File "C:\Users\proje\AppData\Local\Programs\Python\Python37\lib\typing.py", line 708, in __instancecheck__
    return self.__subclasscheck__(type(obj))
  File "C:\Users\proje\AppData\Local\Programs\Python\Python37\lib\typing.py", line 716, in __subclasscheck__
    raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks

>>> from typing import List 
>>> print(type(List[dir]))  
<class 'typing._GenericAlias'>

test de classe de données Test de classe de données Hinoyakoma Vous pouvez voir pourquoi ce n'est pas bon en exécutant ce qui suit, mais List [int] est un type pour spécifier un type, donc ce n'est pas un type de liste réel. Il semble qu'il soit nécessaire de convertir au moment de la vérification de type.

en conclusion

La vérification de type en utilisant «post_init» semble correcte. Cependant, si vous utilisez typing lors de la spécification du type, il semble que vous ayez besoin de trouver un autre moyen de vérifier en le tournant avec l'instruction for. Il peut être possible de vérifier de l'extérieur au lieu de le mettre dans le code avec mypy etc. (Si l'environnement est solide, comme la confirmation automatique au moment du push, ceci est également disponible)

référence

--Python Documentation contents dataclasses --- Dataclasses

Recommended Posts

[Python] Vérification simple du type d'argument avec la classe de données
Vérification de domaine avec Python
Python # Vérifier l'identité du type
Vérifier la version avec python
[Analyse de co-occurrence] Analyse de co-occurrence facile avec Python! [Python]
Vérifiez la couverture de python avec pytest-cov
Python - Vérifiez le type de valeurs
Synchronisation facile des dossiers avec Python
Compilation facile de Python avec NUITKA-Utilities
Serveur HTTP facile avec Python
[Python] Traitement parallèle facile avec Joblib
Définition du type d'argument de fonction en python
Programmation facile Python + OpenCV avec Canopy
Transmission de courrier facile avec Hâte Python3
Optimisation bayésienne très simple avec Python
Maîtriser le type avec Python [compatible Python 3.9]
Visualisez facilement vos données avec Python seaborn.
Exécution parallèle facile avec le sous-processus python
Extraction de mots-clés facile avec TermExtract pour Python
[Python] Test super facile avec instruction assert
Vérifier l'existence du fichier avec python
Introduction facile de la reconnaissance vocale avec Python
Vérifiez les cours des actions avec Slackbot en utilisant Python
Recevoir le type de date (datetime) avec ArgumentParser [python]
[Easy Python] Lecture de fichiers Excel avec openpyxl
Application Web facile avec Python + Flask + Heroku
Traitez facilement des images en Python avec Pillow
[Easy Python] Lecture de fichiers Excel avec des pandas
Scraping Web facile avec Python et Ruby
[Python] Essayez facilement l'apprentissage amélioré (DQN) avec Keras-RL
[Python] Introduction facile à l'apprentissage automatique avec python (SVM)
FizzBuzz en Python3
Sortie CSV de la recherche Google avec [Python]! 【Facile】
Grattage avec Python
Python est facile
Type numérique Python
Statistiques avec python
Lire les données avec python / netCDF> nc.variables [] / Vérifier la taille des données
Vérifier automatiquement les scripts Python avec GitHub + Travis-CI + pycodestyle
vérification de la grammaire python
Grattage avec Python
Python avec Go
Python> Argument d'exécution> Vérifier si -d est attaché
Twilio avec Python
Intégrer avec Python
Vérifiez la date du devoir de drapeau avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Bingo avec python
Zundokokiyoshi avec python
Type de chaîne Python2
Python # type de chaîne
[Python] Déterminez le type d'iris avec SVM
Analyse de régression LASSO facile avec Python (pas de théorie)
Excel avec Python