2009-10-29 4 views
3

je me retrouve à écrire le même argument code de vérification tout le temps pour le nombre crissement:arguments dans le code Vérification Python numérique

def myfun(a, b): 
    if a < 0: 
     raise ValueError('a cannot be < 0 (was a=%s)' % a) 
    # more if.. raise exception stuff here ... 
    return a + b 

Y at-il une meilleure façon? On m'a dit de ne pas utiliser 'affirmer' pour ces choses (bien que je ne vois pas le problème, à part de ne pas connaître la valeur de la variable qui a causé l'erreur).

edit: Pour clarifier, les arguments sont généralement juste des nombres et les conditions de vérification d'erreurs peuvent être complexes, non triviales et ne mèneront pas nécessairement à une exception plus tard, mais simplement à un mauvais résultat. (algorithmes instables, solutions dénuées de sens, etc.)

+0

Qui a dit de ne pas utiliser affirmer? Quelle raison ont-ils donné? Pouvez-vous obtenir un devis ou une référence? –

+0

@ S.Lott: parce que assert s'en va avec '__debug__' ou optimisations, et le" affirmer pour le programmeur, les exceptions pour l'utilisateur "mantra. Référence? err .. appelons cela "communication privée" – MarkkS

Répondre

0

Vous ne voulez pas utiliser affirm parce que votre code peut être exécuté (et est par défaut sur certains systèmes) de telle manière que les lignes assertives ne sont pas vérifiées et ne génèrent pas d'erreurs (-O drapeau de ligne de commande).

Si vous utilisez beaucoup de variables qui sont tous censés avoir les mêmes propriétés, pourquoi ne pas sous-classe quel que soit le type que vous utilisez et ajoutez ce chèque à la classe elle-même? Ensuite, lorsque vous utilisez votre nouvelle classe, vous savez que vous n'avez jamais une valeur invalide, et vous n'avez pas besoin de la vérifier partout.

4

assert obtient optimisé loin si vous exécutez avec python -O (optimisations modestes, mais parfois agréable à avoir). Une alternative préférable si vous avez des motifs qui se répètent souvent peut-être d'utiliser des décorateurs - excellent moyen d'éviter la répétition. Par exemple, disons que vous avez des fonctions zillion qui doivent être appelées avec des arguments by-position (pas par mot-clé) et doivent avoir leurs premiers arguments positifs; puis ...:

def firstargpos(f): 
    def wrapper(first, *args): 
    if first < 0: 
     raise ValueError(whateveryouwish) 
    return f(first, *args) 
    return wrapper 

vous dire quelque chose comme:

@firstargpos def myfun (a, b): ...

et les contrôles sont effectués dans les décorateurs (ou plutôt la fermeture de l'emballage, elle revient) une fois pour toutes. Ainsi, la seule partie délicate est de déterminer exactement quels contrôles vos fonctions ont besoin et la meilleure façon d'appeler le décorateur (s) d'exprimer les (difficile à dire, sans voir l'ensemble des fonctions que vous définissez et l'ensemble des contrôles chacun des besoins ! -). Rappelez-vous, DRY ("Ne vous répétez pas") est proche de la première place parmi les principes directeurs du développement logiciel, et Python a un support raisonnable pour vous permettre de mettre en œuvre DRY et éviter le code répétitif! -)

+0

Je suppose que son cas d'utilisation fait une différence, mais les wrappers sont-ils préférés à ma suggestion de sous-classer son type de données? –

+0

Forcer chaque appelant à utiliser le type de données "sous-classé" est très envahissant pour chaque appelant et réduit la réutilisation des fonctions dans une mesure délicate. Les décorateurs sont juste une autre façon d'exprimer l'ensemble des vérifications dont chaque fonction a besoin (en évitant les messages standard et les répétitions) et sont totalement transparentes pour les appelants, ce qui est une très bonne chose. –

+0

Assez juste. Je supposais qu'il nettoyait les entrées, puis les transmettait à toutes ses fonctions, et donc un type de données sous-classé l'empêchait d'avoir à relancer cette vérification à chaque fois. –

-1

I ne suis pas sûr si cela va répondre à votre question, mais il me semble que la vérification beaucoup d'arguments au début d'une fonction n'est pas très pythonique.

Ce que je veux dire par là qu'il est l'hypothèse de la plupart Pythoneux que nous sommes tous adultes consentants, et nous nous faisons confiance à ne pas faire quelque chose de stupide. Voici comment j'écrirais votre exemple:

def myfun(a, b): 
    '''a cannot be < 0''' 
    return a + b 

Ceci a trois avantages distincts. Tout d'abord, c'est concis, il n'y a vraiment pas de code supplémentaire qui fasse quoi que ce soit sans rapport avec ce que vous essayez de faire. En second lieu, il met les informations exactement où il appartient, dans help(myfun), où Pythoneux devraient chercher des notes d'utilisation. Enfin, une valeur non-positive pour a est-elle vraiment une erreur? Bien que vous pourriez penser, à moins que quelque chose de vraiment cassera si un est égal à zéro (ici, il ne sera probablement pas), puis laisser peut-être glisser à travers et provoquer une erreur le flux d'appels est plus sage.après tout, si a + b est en erreur, il déclenche une exception qui est transmise à la pile d'appels et le comportement reste à peu près le même.

+2

Je ne suis pas d'accord. Si votre fonction est plus complexe que a + b, une erreur de nature négative peut très bien évaluer correctement mais retourner des résultats incorrects. Pensez aux dépôts sur votre compte bancaire. Je suis d'accord que vous devriez seulement vérifier vos entrées pour la validité aux endroits appropriés, mais supposer que Python jettera des erreurs pour de mauvaises données est dangereux. "Explicite est mieux que implicite"! –

+0

@ Paul McMillan: Dans le contexte des transactions bancaires, l'hypothèse des «adultes consentants» est probablement également invalide. Au contraire, je préférerais que l'institution avec laquelle je fais affaire prenne en charge toutes les personnes avec lesquelles elle interagit pour être des enfants ou des criminels, ou les deux. – SingleNegationElimination

+2

Je ne suis pas d'accord. Si vous savez à l'avance qu'un argument négatif est faux, vérifiez l'appel de la fonction et déclenchez immédiatement une exception. Il pourrait être faux et ne pas conduire à une exception; il pourrait être faux et conduire à une exception étrange et déroutante; il pourrait être faux et casser quelque chose qui, plusieurs milliers de lignes de code plus tard, soulève une exception et est donc très perplexe en effet. (À l'origine, je crois que la question des «adultes consentants» faisait référence à la plainte selon laquelle il est impossible d'empêcher les gens d'introspecter des variables privées et, dans ce contexte, je suis d'accord avec cela.) – steveha

Questions connexes