Puis-je faire assert
lève une exception que je choisis au lieu de AssertionError
?
METTRE À JOUR:
J'expliquerai ma motivation: jusqu'à présent, j'ai eu des tests de type assertion qui ont soulevé mes propres exceptions; Par exemple, lorsque vous créez un objet Node
avec certains arguments, il vérifie si les arguments sont utiles à la création d'un nœud et sinon, il déclenche NodeError
.
Mais je sais que Python a un mode -o
dans lequel les assertions sont ignorées, ce que je souhaiterais que le système soit disponible, car cela rendrait mon programme plus rapide. Mais j'aimerais quand même avoir mes propres exceptions. C'est pourquoi je veux utiliser assert avec mes propres exceptions.
Cela fonctionnera. Mais c'est un peu fou.
try:
assert False, "A Message"
except AssertionError, e:
raise Exception( e.args )
Pourquoi pas ce qui suit? C'est moins fou.
if not someAssertion: raise Exception( "Some Message" )
C'est seulement un peu plus verbeux que la déclaration assert
, mais cela ne contrevient pas à notre attente selon laquelle les échecs d'assertion lèvent AssertionError
.
Considère ceci.
def myAssert( condition, action ):
if not condition: raise action
Ensuite, vous pouvez plus ou moins remplacer vos assertions existantes par quelque chose comme ceci.
myAssert( {{ the original condition }}, MyException( {{ the original message }} ) )
Une fois que vous avez fait cela, vous êtes maintenant libre de vous mêler d'activer ou de désactiver ou quoi que vous essayiez de faire.
Lisez également le module warnings . Cela peut être exactement ce que vous essayez de faire.
Que dis-tu de ça?
>>> def myraise(e): raise e
...
>>> cond=False
>>> assert cond or myraise(RuntimeError)
Traceback (most recent call last):
File "", line 1, in
File "", line 1, in myraise
RuntimeError
Python ignore également les blocs if __debug__:
lorsqu’il est exécuté avec l’option -o
. Le code suivant est plus détaillé, mais fait ce dont vous avez besoin sans piratage:
def my_assert(condition, message=None):
if not condition:
raise MyAssertError(message)
if __debug__: my_assert(condition, message)
Vous pouvez le raccourcir en déplaçant la condition if __debug__:
dans my_assert()
, mais elle sera ensuite appelée (sans aucune action à l'intérieur) lorsque l'optimisation sera activée.
Ne jamais utiliser une affirmation pour la logique! Seulement pour les tests optionnels. N'oubliez pas que si Python est en cours d'exécution avec les optimisations activées, les assertions ne sont même pas compilées dans le bytecode. Si vous faites cela, vous vous souciez évidemment de l'exception levée et si vous vous souciez, vous utilisez des affirmations fausses au départ.
Vous pouvez laisser un context manager faire la conversion pour vous, à l’intérieur d’un bloc with (qui peut contenir plusieurs assertions, ou plusieurs appels de code et de fonction ou ce que vous voulez.
from __future__ import with_statement
import contextlib
@contextlib.contextmanager
def myassert(exctype):
try:
yield
except AssertionError, exc:
raise exctype(*exc.args)
with myassert(ValueError):
assert 0, "Zero is bad for you"
Voir une version précédente de cette réponse pour substituer directement des objets d'exception construits (KeyError("bad key")
) au lieu de réutiliser les arguments des assertions.
Pour voir si j'essaie d'avoir des frais généraux, j'ai essayé cette expérience
voici myassert.py
def myassert(e):
raise edef f1(): #this is the control for the experiment
cond=True
def f2():
cond=True
try:
assert cond, "Message"
except AssertionError, e:
raise Exception(e.args)
def f3():
cond=True
assert cond or myassert(RuntimeError)
def f4():
cond=True
if __debug__:
raise(RuntimeError)
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f1()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f2()'
100 loops, best of 1000: 0.479 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f3()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f4()'
100 loops, best of 1000: 0.42 usec per loop
En Python 2.6.3 au moins, cela fonctionnera aussi:
class MyAssertionError (Exception):
pass
AssertionError = MyAssertionError
assert False, "False"
Traceback (most recent call last):
File "assert.py", line 8, in <module>
assert False, "False"
__main__.MyAssertionError: False
Si vous voulez utiliser des assertions pour cela, cela semble plutôt bien fonctionner:
>>> def raise_(e): raise e
...
>>> x = -2
>>> assert x >= 0, raise_(ValueError('oops'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in raise_
ValueError: oops
Notez que dans l'assertion, les éléments figurant après la virgule ne seront évalués que si la condition est fausse. Par conséquent, la variable ValueError
ne sera créée et levée que lorsque cela sera nécessaire.