web-dev-qa-db-fra.com

Comment vérifier si une valeur correspond à un type de python?

Disons que j'ai un python fonction dont l'argument unique est un type non trivial:

from typing import List, Dict
ArgType = List[Dict[str, int]]  # this could be any non-trivial type
def myfun(a: ArgType) -> None:
    ...

... Et puis j'ai une structure de données que j'ai déballée à partir d'une source JSON:

import json
data = json.loads(...)

Ma question est la suivante: Comment puis-je vérifier au moment de l'exécution que data a le type correct à utiliser comme argument à myfun() Avant l'utiliser comme argument pour myfun()?

if not isCorrectType(data, ArgType):
    raise TypeError("data is not correct type")
else:
    myfun(data)
12
too much php

Le moyen courant de gérer cela consiste à utiliser le fait que si quel que soit l'objet que vous passez à myfun _ n'a pas la fonctionnalité requise une exception correspondante sera soulevée (généralement TypeError ou AttributeError). Donc, vous feriez ce qui suit:

try:
    myfun(data)
except (TypeError, AttributeError) as err:
    # Fallback for invalid types here.

Vous indiquez dans votre question que vous allumeriez un TypeError si l'objet transcuté n'a pas la structure appropriée, mais Python le fait déjà pour vous. La question critique est la façon dont vous voulez gérer ce cas. Vous pouvez également déplacer le try / except bloquer dans myfun, le cas échéant. Quand il s'agit de taper Python vous comptez habituellement sur dactylographie de canard : Si l'objet a la fonctionnalité requise, vous ne vous souciez pas beaucoup de ce type de type, Tant que cela sert le but.

Considérez l'exemple suivant. Nous venons de transmettre les données dans la fonction, puis d'obtenir le AttributeError gratuitement (que nous pouvons alors sauf); Pas besoin de vérification de type manuel:

>>> def myfun(data):
...     for x in data:
...             print(x.items())
... 
>>> data = json.loads('[[["a", 1], ["b", 2]], [["c", 3], ["d", 4]]]')
>>> myfun(data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myfun
AttributeError: 'list' object has no attribute 'items'

Si vous êtes préoccupé par l'utilité de l'erreur résultante, vous pouvez toujours sauf re-élever une exception personnalisée (ou même changer le message de l'exception):

try:
    myfun(data)
except (TypeError, AttributeError) as err:
    raise TypeError('Data has incorrect structure') from err

try:
    myfun(data)
except (TypeError, AttributeError) as err:
    err.args = ('Data has incorrect structure',)
    raise

Lorsque vous utilisez le code tiers, il faut toujours vérifier la documentation des exceptions qui seront soulevées. Par exemple numpy.inner rapporte qu'il augmentera un ValueError dans certaines circonstances. Lorsque vous utilisez cette fonction, nous n'avons pas besoin d'effectuer nous-mêmes des chèques, mais s'appuie sur le fait qu'il augmentera l'erreur si nécessaire. Lorsque vous utilisez un code tiers pour lequel il n'est pas clair comment il se comportera dans certains cas d'angle, c'est-à-dire Il est plus facile et plus clair pour simplement coder un vérificateur de type correspondant (voir ci-dessous) au lieu d'utiliser une solution générique qui fonctionne pour n'importe quel type. Ces cas devraient être rares de toute façon et laisser un commentaire correspondant rend vos collègues développeurs au courant de la situation.

La bibliothèque typing est destinée à l'allusion de type et à ce titre, il ne vérifiera pas les types d'exécution. Bien sûr, vous pourriez le faire manuellement, mais c'est plutôt encombrant:

def type_checker(data):
    return (
        isinstance(data, list)
        and all(isinstance(x, dict) for x in list)
        and all(isinstance(k, str) and isinstance(v, int) for x in list for k, v in x.items())
    )

Ceci avec un commentaire approprié est toujours une solution acceptable et il est réutilisable lorsqu'une structure de données similaire est attendue. L'intention est claire et le code est facilement vérifiable.

1
a_guest

C'est gênant qu'il n'y a pas de fonction intégrée pour cela, mais typeguard est livré avec une fonction pratique check_type():

>>> from typeguard import check_type
>>> from typing import List
>>> check_type("foo", [1,2,"3"], List[int])
Traceback (most recent call last):
...
TypeError: type of foo[2] must be int; got str instead

type of foo[2] must be int; got str instead

Pour plus, voir: https://typeguard.readthedocs.io/fr/latest/api.html#typeguard.check_type

0
Granitosaurus

Vous devriez vérifier manuellement votre structure de type imbriquée manuellement - le type d'indice n'est pas appliqué.

Vérification de ce type de choses IST MEILLEUR FAIT en utilisant ABC (CLASSES ABSJECTES META) - Les utilisateurs peuvent donc fournir leurs classes dérivées qui prennent en charge le même accès/listes par défaut:

import collections.abc 

def isCorrectType(data):
    if isinstance(data, collections.abc.Collection): 
        for d in data:
            if isinstance(d,collections.abc.MutableMapping): 
                for key in d:
                    if isinstance(key,str) and isinstance(d[key],int):
                        pass
                    else:
                        return False
            else: 
                return False
    else:
        return False
    return True

Sortir:

print ( isCorrectType( [ {"a":2} ] ))       # True
print ( isCorrectType( [ {2:2} ] ))         # False   
print ( isCorrectType( [ {"a":"a"} ] ))     # False   
print ( isCorrectType( [ {"a":2},1 ] ))     # False   

Doku:

En rapport:


L'autre sens serait de suivre la "Demander le pardon non autorisée" - Expliquez Paradigm et Simyply Utilisez Vos données dans le formulaire que vous voulez et try:/except: autour si cela ne se conforme pas à ce que vous vouliez. Cela correspond mieux avec Qu'est-ce que le dactylographie de canard? - et permet (semblable à ABC-Vérification), le consommateur de vous fournir des cours dérivés de la liste/dict pendant qu'il fonctionnera toujours ...

0
Patrick Artner