2009-03-02 8 views
11

Python déconseille de vérifier les types. Mais dans de nombreux cas cela peut être utile:Dois-je vérifier les types d'arguments constructeurs (et à d'autres endroits aussi)?

  1. Vérification des arguments du constructeur. par exemple. vérification de foe Boolean, string, dict etc. Si je ne le fais pas et que je place les membres de l'objet dans les arguments cela causera des problèmes plus tard.

  2. Vérification des arguments de fonctions.

  3. Dans les propriétés. Si quelqu'un définit une valeur erronée ou un type différent, je devrais répondre rapidement.

+0

Le type Python échoue - n'échelle pas; Je préférerais utiliser Mono. –

Répondre

9

La réponse est simple Non, utilisez Polymorphisme, exceptions, etc.

  1. Dans le cas des arguments du constructeur étant du mauvais type, une exception sera levée lorsque l'exécution du code qui dépendent s sur le paramètre étant d'un type particulier. Si c'est une chose bizarre, spécifique au domaine, élévez votre propre Exception. Entourez les blocs de code susceptibles d'échouer avec les erreurs try-except et handle. Il est donc préférable d'utiliser la gestion des exceptions. (va de même pour les arguments de la fonction)

  2. Dans les propriétés, le même argument est applicable. Si vous validez la valeur reçue, utilisez une assertion pour vérifier sa portée, etc. Si la valeur est du mauvais type, elle échouera quand même. Ensuite, gérer AssertionError.

En Python, vous traitez les programmeurs comme des êtres intelligents !! Il suffit de bien documenter votre code (rendre les choses évidentes), d'élever les exceptions le cas échéant, d'écrire du code polymorphique, etc. Laissez la gestion des exceptions (où c'est approprié seulement)/erreurs dans la construction au code client.

Avertissement
Laissant la gestion des exceptions aux clients ne signifie pas que vous devez jeter beaucoup d'erreurs d'ordures à l'utilisateur à son insu. Dans la mesure du possible, gérez les exceptions qui peuvent se produire en raison d'une mauvaise construction ou de toute autre raison dans votre code lui-même. Votre code devrait être robuste. Là où il vous est impossible de gérer l'erreur, informez poliment le programmeur du code utilisateur/client!

Remarque
En général, les mauvais arguments à un constructeur est pas quelque chose me préoccupe trop.

4

Cochez tout ce que vous voulez, il vous suffit d'être explicite. L'exemple suivant est un constructeur de a module dans la bibliothèque standard - il vérifie la extrasaction arg:

class DictWriter: 
    def __init__(self, f, fieldnames, restval="", extrasaction="raise", 
       dialect="excel", *args, **kwds): 
     self.fieldnames = fieldnames # list of keys for the dict 
     self.restval = restval   # for writing short dicts 
     if extrasaction.lower() not in ("raise", "ignore"): 
      raise ValueError, \ 
       ("extrasaction (%s) must be 'raise' or 'ignore'" % 
       extrasaction) 
     self.extrasaction = extrasaction 
     self.writer = writer(f, dialect, *args, **kwds) 
11

La réponse est presque toujours « non ». L'idée générale en Python, Ruby, et quelques autres langues s'appelle "Duck Typing". Vous ne devriez pas vous soucier de ce qu'est quelque chose, seulement comment cela fonctionne. En d'autres termes, "si tout ce que vous voulez est quelque chose qui fait des charlatans, vous n'avez pas besoin de vérifier que c'est en fait un canard."

Dans la réalité, le problème avec tous ces contrôles de type est l'impossibilité de remplacer les entrées par des implémentations alternatives. Vous pouvez vérifier dict, mais je peux vouloir passer quelque chose dans lequel n'est pas un dict, mais implémente l'API dict.

La vérification de type vérifie uniquement l'une des nombreuses erreurs possibles dans le code. Par exemple, il n'inclut pas la vérification de distance (du moins pas en Python). Une réponse moderne à l'affirmation selon laquelle il faut effectuer une vérification de type est qu'il est plus efficace de développer des tests unitaires qui garantissent que non seulement les types sont corrects, mais aussi que la fonctionnalité est correcte. Un autre point de vue est que vous devez traiter vos utilisateurs d'API comme des adultes consentants et leur faire confiance pour utiliser correctement l'API. Bien sûr, il y a des moments où la vérification des entrées est utile, mais c'est moins commun que vous ne le pensez. Un exemple est la saisie de sources non fiables, comme sur le web public.

+0

Je ne fais pas confiance à mes utilisateurs d'API comme les adultes consentants. Je vois le code qu'ils écrivent. –

+0

Je ne suis pas d'accord plus. La vérification de type dans une fonction a une grande incidence sur la lisibilité. Utilisez les assertions (voir dangph ci-dessous) en développement, et les éliminer avec 'python -O' pour la production. Avec de bons tests unitaires, c'est tout ce dont vous avez besoin. –

+0

Si les développeurs ne peuvent pas être des "adultes consentants", c'est leur problème. Tant que les documents API existent, ils peuvent vérifier les types d'arguments attendus, tout va bien. SAUF si vous voulez répondre spécifiquement à un certain type. –

0

"Si je ne le fais pas et que je mets les membres de l'objet aux arguments, cela causera des problèmes plus tard."

Veuillez être très clair sur la liste exacte des "problèmes" qui seront causés plus tard.

  • Cela ne fonctionnera pas du tout? C'est ce que les essais/sauf les blocs sont pour.

  • Se comportera-t-il "bizarrement"? Ceci est vraiment rare, et se limite aux types et aux opérateurs «quasi-manqués». L'exemple standard est la division. Si vous attendiez des nombres entiers, mais que vous obteniez un nombre à virgule flottante, la division pourrait ne pas faire ce que vous vouliez. Mais c'est corrigé avec les opérateurs //, vs/division.

  • Est-ce que ce sera tout simplement faux, mais semble toujours se terminer? Ceci est vraiment rare, et nécessiterait un type assez soigneusement conçu qui utilise des noms standard, mais fait des choses non standard. Par exemple

    class MyMaliciousList(list): 
        def append(self, value): 
         super(MyMaliciousList, self).remove(value) 
    

Autre que cela, il est difficile d'avoir des choses « causer des problèmes plus tard ». Veuillez mettre à jour votre question avec des exemples spécifiques de "problèmes".

1

C'est souvent une bonne chose à faire. La vérification des types explicites n'est probablement pas très utile en Python (comme d'autres l'ont dit), mais vérifier les valeurs légales peut être une bonne idée. La raison pour laquelle c'est une bonne idée est que le logiciel échouera plus près de la source du bug (il suit le principe Fail Fast). En outre, les contrôles agissent comme une documentation pour d'autres programmeurs et pour vous-même. Mieux encore, c'est de la "documentation exécutable", ce qui est bien parce que c'est de la documentation qui ne peut pas mentir.

Un moyen rapide et sale mais raisonnable pour vérifier vos arguments est d'utiliser assert:

def my_sqrt(x): 
    assert x >= 0, "must be greater or equal to zero" 
    # ... 

Affirmer vos arguments est une sorte de Design pauvre homme par contrat. (Vous pouvez rechercher Design by Contract, c'est intéressant.)

1

AFAIU, vous voulez vous assurer que certains objets se comportent ("suivez une interface") plus tôt que l'utilisation réelle. Dans votre exemple, vous voulez savoir que les objets sont appropriés au moment de la création de l'instance, et non quand ils seront effectivement utilisés. En gardant à l'esprit que nous parlons ici de Python, je ne suggérerai pas assert (que se passe-t-il si python -O ou une variable d'environnement PYTHONOPTIMIZE est définie sur 1 lorsque votre programme s'exécute?) Ou la vérification des types spécifiques (car qui limite inutilement les types que vous pouvez utiliser), mais je vais suggérer au début des tests fonctionnalité, quelque chose le long des lignes:

def __init__(self, a_number, a_boolean, a_duck, a_sequence): 

    self.a_number= a_number + 0 

    self.a_boolean= not not a_boolean 

    try: 
     a_duck.quack 
    except AttributeError: 
     raise TypeError, "can't use it if it doesn't quack" 
    else: 
     self.a_duck= a_duck 

    try: 
     iter(a_sequence) 
    except TypeError: 
     raise TypeError, "expected an iterable sequence" 
    else: 
     self.a_sequence= a_sequence 

je try… except… else dans cette suggestion parce que je veux Définissez les membres d'instance uniquement si le test a réussi, même si le code est modifié ou augmenté. Vous n'êtes pas obligé de le faire, évidemment. Pour les arguments de fonction et les propriétés de paramétrage, je ne ferais pas ces tests à l'avance, j'utiliserais simplement les objets fournis et agirais sur les exceptions levées, à moins que les objets suspects ne soient utilisés après un long processus.

0

Comme le dit dalke, la réponse est presque toujours "non". En Python, vous ne vous souciez généralement pas qu'un paramètre est un certain type mais plutôt qu'il se comporte comme un certain type. Ceci est connu comme "Duck Typing". Il existe deux manières de tester si un paramètre se comporte comme un type donné: (1) vous pouvez l'utiliser comme s'il se comportait comme prévu et lancer une exception quand/si ce n'est pas le cas ou (2) vous pouvez définir un interface qui décrit comment ce type devrait se comporter et tester la conformité avec cette interface.

zope.interface est mon système d'interface préféré pour Python, mais il en existe plusieurs autres. Avec l'un d'entre eux, vous définissez une interface, puis déclarez qu'un type donné est conforme à cette interface ou définissez un adaptateur qui transforme votre type en quelque chose qui se conforme à cette interface. Vous pouvez ensuite affirmer (ou tester comme vous le souhaitez) que les paramètres fournissent (dans la terminologie zope.interface) cette interface.

Questions connexes