web-dev-qa-db-fra.com

Meilleure pratique pour Python assert

  1. L'utilisation de assert dans le code standard pose-t-elle un problème de performances ou de maintenance du code au lieu de l'utiliser uniquement à des fins de débogage?

    Est

    assert x >= 0, 'x is less than zero'
    

    mieux ou pire que

    if x < 0:
        raise Exception, 'x is less than zero'
    
  2. De même, existe-t-il un moyen de définir une règle commerciale telle que if x < 0 raise error toujours vérifiée sans le try/except/finally de sorte que, si à tout moment dans le code x est inférieur à 0, une erreur est générée, comme si vous définissez assert x < 0 au début d'une fonction, n'importe où dans la fonction où x devient inférieur à 0, une exception est déclenchée?

445
meade

Pouvoir automatiquement renvoyer une erreur lorsque x devient inférieur à zéro dans toute la fonction. Vous pouvez utiliser descripteurs de classe . Voici un exemple:

class LessThanZeroException(Exception):
    pass

class variable(object):
    def __init__(self, value=0):
        self.__x = value

    def __set__(self, obj, value):
        if value < 0:
            raise LessThanZeroException('x is less than zero')

        self.__x  = value

    def __get__(self, obj, objType):
        return self.__x

class MyClass(object):
    x = variable()

>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "my.py", line 7, in __set__
    raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
139
Nadia Alramli

Les assertions doivent être utilisées pour tester des conditions qui ne devraient jamais se produire . Le but est d’arrêter tôt dans le cas d’un programme corrompu.

Les exceptions devraient être utilisées pour les erreurs qui pourraient éventuellement se produire, et vous devriez presque toujours créer vos propres classes d'exception.


Par exemple, si vous écrivez une fonction pour lire un fichier de configuration dans un dict, un formatage incorrect dans le fichier doit générer un ConfigurationSyntaxError, alors que vous pouvez assert pas sur le point de retourner None.


Dans votre exemple, si x est une valeur définie via une interface utilisateur ou une source externe, une exception est préférable.

Si x n'est défini que par votre propre code dans le même programme, procédez avec une assertion.

703
Deestan

Les instructions "assert" sont supprimées lorsque la compilation est optimisée . Donc, oui, il existe des différences de performances et de fonctionnalités.

Le générateur de code actuel n'émet aucun code pour une instruction assert lorsque l'optimisation est demandée au moment de la compilation. - Python 2.6.4 Docs

Si vous utilisez assert pour implémenter les fonctionnalités de l'application, puis optimisez le déploiement en production, vous serez affecté par des défauts "but-it-works-in-dev".

Voir PYTHONOPTIMIZE et - O -OO

342
John Mee

Les quatre objectifs de assert

Supposons que vous travaillez sur 200 000 lignes de code avec quatre collègues, Alice, Bernd, Carl et Daphne. Ils appellent votre code, vous appelez leur code.

Alors assert a quatre rôles:

  1. Informez Alice, Bernd, Carl et Daphne de ce que votre code attend.
    Supposons que vous avez une méthode qui traite une liste de n-uplets et que la logique du programme peut échouer si ces n-uplets ne sont pas immuables:

    def mymethod(listOfTuples):
        assert(all(type(tp)==Tuple for tp in listOfTuples))
    

    C'est plus fiable que des informations équivalentes dans la documentation et beaucoup plus facile à gérer.

  2. Informez l'ordinateur de ce que votre code attend.
    assert impose le comportement correct des appelants de votre code. Si votre code appelle le code d'Alices et que le code de Bernd appelle le vôtre, alors sans le assert, si le programme se bloque dans le code Alices, Bernd peut supposer que c'est la faute d'Alice, Alice enquête et peut supposer que c'est de votre faute, vous enquêtez et dites Bernd c'était en fait le sien. Beaucoup de travail perdu.
    Avec des affirmations, quiconque se trompe d’appel, ils seront rapidement en mesure de voir que c’est de leur faute, pas de la vôtre. Alice, Bernd et vous en profitez tous. Économise d'immenses quantités de temps.

  3. Informez les lecteurs de votre code (y compris vous-même) sur les résultats obtenus par votre code.
    Supposons que vous avez une liste d'entrées et que chacune d'entre elles peut être propre (ce qui est bien) ou bien smorsh, trale, gullup ou scintillante (qui ne sont pas toutes acceptables). Si c'est smorsh il doit être non-morshed; si c'est vrai, il doit être baludé; si c'est gullup, il faut le trotter (et éventuellement aussi le rythme); si elle scintille, elle doit être scintillée à nouveau, sauf le jeudi. Vous avez l’idée: c’est compliqué. Mais le résultat final est (ou devrait être) que toutes les entrées sont propres. The Right Thing (TM) consiste à résumer l’effet de votre boucle de nettoyage comme suit:

    assert(all(entry.isClean() for entry in mylist))
    

    Cette déclaration épargne un mal de tête à tout le monde qui essaie de comprendre ce que (=== -) exactement accomplit. Et le plus fréquent de ces personnes sera probablement vous-même.

  4. Informez l'ordinateur de ce que votre code a atteint à un moment donné.
    Si vous oubliez un jour de modifier une entrée après en avoir besoin, la assert vous sauvera la journée et vous évitera que votre code ne casse votre chère Daphne beaucoup plus tard.

Dans mon esprit, les deux objectifs de la documentation de assert (1 et 3) et de la sauvegarde (2 et 4) ont la même valeur.
Informer les gens peut même avoir plus de valeur qu'informer l'ordinateur, car il permet d'éviter les erreurs mêmes que la assert vise à attraper (dans le cas 1) et beaucoup d'erreurs ultérieures dans tous les cas.

119
Lutz Prechelt

En plus des autres réponses, les assertions elles-mêmes jettent des exceptions, mais uniquement AssertionErrors. D'un point de vue utilitaire, les assertions ne conviennent pas lorsque vous avez besoin d'un contrôle précis du grain en fonction des exceptions que vous capturez.

21
outis

La seule chose qui ne va vraiment pas avec cette approche, c'est qu'il est difficile de faire une exception très descriptive en utilisant des déclarations d'assertion. Si vous cherchez la syntaxe la plus simple, souvenez-vous que pouvez faites aussi quelque chose comme ceci:

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

Un autre problème est que l'utilisation d'assert pour le contrôle de condition normal rend difficile la désactivation des assertions de débogage à l'aide de l'indicateur -O.

18
Jason Baker

Comme cela a été dit précédemment, les assertions doivent être utilisées lorsque votre code NE DOIT JAMAIS atteindre un point, ce qui signifie qu'il y a un bogue. La raison la plus utile pour utiliser une assertion est probablement un invariant/pré/postcondition. Celles-ci doivent être vérifiées au début ou à la fin de chaque itération d'une boucle ou d'une fonction.

Par exemple, une fonction récursive (2 fonctions séparées, donc 1 gère la mauvaise entrée et l’autre gère le mauvais code, car il est difficile de faire la distinction avec la récursion). Cela rendrait évident si j'oubliais d'écrire la déclaration if, ce qui s'était mal passé.

def SumToN(n):
    if n <= 0:
        raise ValueError, "N must be greater than or equal to 0"
    else:
        return RecursiveSum(n)

def RecursiveSum(n):
    #precondition: n >= 0
    assert(n >= 0)
    if n == 0:
        return 0
    return RecursiveSum(n - 1) + n
    #postcondition: returned sum of 1 to n

Ces invariants de boucle peuvent souvent être représentés par une assertion.

7
matts1

Le mot anglais assert est utilisé ici dans le sens de jure , affirmer , avouez . Cela ne signifie pas "cocher" ou "devrait être" . Cela signifie que vous en tant que codeur faites une déclaration ici:

# I solemnly swear that here I will tell the truth, the whole truth, 
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42

Si le code est correct, sauf événements uniques , défaillances matérielles et autres , aucune assertion n'échouera jamais . C'est pourquoi le comportement du programme vis-à-vis de l'utilisateur final ne doit pas être affecté. En particulier, une assertion ne peut pas échouer même dans des conditions de programmation exceptionnelles . Cela n'arrive jamais jamais. Si cela se produit, le programmeur devrait être zappé pour cela.

6
Antti Haapala

Existe-t-il un problème de performances ?

  • S'il vous plaît rappelez-vous de "le faire fonctionner avant de le faire fonctionner rapidement".
    Très peu de programmes sont pertinents pour sa rapidité. Vous pouvez toujours supprimer ou simplifier un assert s'il s'avère que cela pose un problème de performances - et la plupart d'entre eux ne le feront jamais.

  • Soyez pragmatique:
    Supposons que vous avez une méthode qui traite une liste non-vide de n-uplets et que la logique du programme sera rompue si ces n-uplets ne sont pas immuables. Vous devriez écrire:

    def mymethod(listOfTuples):
        assert(all(type(tp)==Tuple for tp in listOfTuples))
    

    Cela convient probablement si vos listes ont tendance à contenir dix entrées, mais cela peut devenir un problème si elles contiennent un million d'entrées. Mais au lieu de jeter entièrement ce précieux chèque, vous pouvez simplement le rétrograder à

    def mymethod(listOfTuples):
        assert(type(listOfTuples[0])==Tuple)  # in fact _all_ must be tuples!
    

    ce qui n’est pas cher, mais risque quand même d’attraper la plupart des erreurs de programme réelles .

3
Lutz Prechelt

Un assert est à vérifier -
1. La condition valide,
2. la déclaration valide,
3. vraie logique;
du code source. Au lieu de faire échouer tout le projet, cela donne l’alarme que quelque chose n’est pas approprié dans votre fichier source.

Dans l'exemple 1, puisque la variable 'str' n'est pas nulle. Donc, aucune affirmation ou exception ne sera soulevée.

Exemple 1:

#!/usr/bin/python

str = 'hello Pyhton!'
strNull = 'string is Null'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
hello Pyhton!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

Dans l'exemple 2, var 'str' est nul. Donc, nous évitons à l’utilisateur d’aller de l’avant avec un programme défectueux par une instruction assert.

Exemple 2:

#!/usr/bin/python

str = ''
strNull = 'NULL String'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
AssertionError: NULL String

Au moment où nous ne voulons pas de débogage et avons réalisé le problème d'assertion dans le code source. Désactiver l'indicateur d'optimisation

python -O assertStatement.py
rien ne sera imprimé

1
akD

Il existe un framework appelé JBoss Drools pour Java qui effectue une surveillance à l'exécution pour affirmer des règles de gestion, ce qui répond à la deuxième partie de votre question. Cependant, je ne suis pas sûr qu'il existe un tel cadre pour python.

1

Dans les environnements de développement tels que PTVS, PyCharm, Wing assert isinstance(), vous pouvez utiliser la complétion de code pour certains objets non clairs.

0
denfromufa