Votre intérêt pour le nouveau livre " Python Pro Secrets " nous a convaincus que l'histoire des bizarreries de Python mérite d'être poursuivie. Aujourd'hui, nous vous proposons de lire un petit tutoriel sur la création de classes d'exceptions personnalisées (dans le texte - les vôtres). L'auteur a trouvé cela intéressant, il est difficile de ne pas être d'accord avec lui que l'avantage le plus important d'une exception est l'exhaustivité et la clarté du message d'erreur. Une partie du code de l'original est sous forme d'images.
Bienvenue au chat.
Créer vos propres classes d'erreur
Python offre la possibilité de créer vos propres classes d'exceptions. En créant de telles classes, vous pouvez diversifier la conception des classes dans votre application. Une classe d'erreurs personnalisée pourrait consigner les erreurs, inspecter l'objet. C'est nous qui définissons ce que fait la classe d'exception, même si généralement une classe personnalisée peut difficilement faire plus que d'afficher un message.
Naturellement, le type d'erreur lui-même est important, et nous créons souvent nos propres types d'erreur pour indiquer une situation spécifique qui n'est généralement pas couverte au niveau Python. De cette façon, les utilisateurs de la classe sauront exactement ce qui se passe lorsqu'ils rencontrent cette erreur.
Cet article est divisé en deux parties. Tout d'abord, nous définirons une classe d'exception par elle-même. Nous montrons ensuite comment nous pouvons intégrer nos propres classes d'exceptions dans nos programmes Python et montrer comment nous pouvons ainsi améliorer la convivialité des classes que nous concevons.
Classe d'exception personnalisée MyCustomError
Lancer une exception nécessite les méthodes
__init__()
et
__str__()
.
Lors de la levée d'une exception, nous créons déjà une instance de l'exception et en même temps l'affiche à l'écran. Examinons de plus près notre classe d'exception personnalisée ci-dessous.
La classe MyCustomError ci-dessus a deux méthodes magiques,
__init__
et
__str__
, qui sont automatiquement appelées lors de la gestion des exceptions. La méthode
Init
est appelée lorsque l'instance est créée et la méthode
str
est appelée lorsque l'instance est affichée. Par conséquent, lorsqu'une exception est levée, ces deux méthodes sont généralement appelées immédiatement l'une après l'autre. L'instruction throw d'exception Python met un programme dans un état d'erreur.
Dans la liste des arguments de la méthode
__init__
est
*args
. Un composant
*args
est un mode de correspondance de modèle spécial utilisé dans les fonctions et les méthodes. Il vous permet de passer plusieurs arguments et stocke les arguments passés sous forme de tuple, mais vous permet en même temps de ne pas passer d'arguments du tout.
Dans notre cas, nous pouvons dire que si
MyCustomError
des arguments ont été passés au constructeur , nous prenons le premier argument passé et l'affectons à un attribut
message
de l'objet. Si aucun argument n'a été passé, l'attribut se
message
verra attribuer une valeur
None
.
Dans le premier exemple, l'exception
MyCustomError
est levée sans aucun argument, donc l'attribut
message
cet objet reçoit une valeur
None
. Une méthode sera appelée
str
et affichera le message «Le message MyCustomError a été déclenché».
L'exception
MyCustomError
est levée sans aucun argument (les parenthèses sont vides). En d'autres termes, une telle conception d'objet semble non standard. Mais ce n'est qu'un support syntaxique fourni en Python lors de la levée d'une exception.
Dans le deuxième exemple, il
MyCustomError
est passé avec l'argument de chaîne «Nous avons un problème». Il est défini comme un attribut
message
sur l'objet et affiché sous forme de message d'erreur lorsqu'une exception est levée.
Le code de la classe d'exception MyCustomError est ici...
class MyCustomError(Exception):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
print('calling str')
if self.message:
return 'MyCustomError, {0} '.format(self.message)
else:
return 'MyCustomError has been raised'
# MyCustomError
raise MyCustomError('We have a problem')
Classe CustomIntFloatDic
Nous créons notre propre dictionnaire, dont les valeurs ne peuvent être que des entiers et des nombres à virgule flottante.
Allons-y et montrons comment injecter facilement et utilement des classes d'erreur dans nos propres programmes. Pour commencer, je vais offrir un exemple légèrement artificiel. Dans cet exemple fictif, je vais créer mon propre dictionnaire qui ne peut accepter que des entiers ou des nombres à virgule flottante.
Si l'utilisateur tente de spécifier un autre type de données comme valeur dans ce dictionnaire, une exception sera levée. Cette exception fournira des informations utiles à l'utilisateur sur la façon d'utiliser ce dictionnaire. Dans notre cas, ce message informe directement l'utilisateur que seuls des entiers ou des nombres à virgule flottante peuvent être spécifiés comme valeurs dans ce dictionnaire.
Lorsque vous créez votre propre dictionnaire, gardez à l'esprit qu'il existe deux endroits où des valeurs peuvent être ajoutées au dictionnaire. Premièrement, cela peut se produire dans la méthode init lors de la création d'un objet (à ce stade, l'objet peut déjà se voir attribuer des clés et des valeurs), et deuxièmement, lors de la définition des clés et des valeurs directement dans le dictionnaire. Dans ces deux endroits, vous devez écrire du code pour vous assurer que la valeur ne peut être que de type
int
ou
float
.
Tout d'abord, je définirai la classe CustomIntFloatDict qui hérite de la classe intégrée
dict
.
dict
est passé dans une liste d'arguments, qui sont entre parenthèses et suivent le nom de la classe
CustomIntFloatDict
.
Si une classe est instanciée
CustomIntFloatDict
, de plus, aucun argument n'est passé aux paramètres de clé et de valeur, ils seront mis à
None
. L'expression est
if
interprétée comme suit: si soit la clé est égale
None
, soit la valeur est égale
None
, alors une méthode sera appelée avec l'objet
get_dict()
, qui retournera l'attribut
empty_dict
; un tel attribut sur un objet pointe vers une liste vide. N'oubliez pas que les attributs de classe sont disponibles sur toutes les instances de la classe.
Le but de cette classe est de permettre à l'utilisateur de passer une liste ou un tuple avec les clés et les valeurs à l'intérieur. Si l'utilisateur entre une liste ou un tuple à la recherche de clés et de valeurs, ces deux ensembles itérables seront concaténés à l'aide de la fonction
zip
le langage Python. Une variable crochetée pointant vers un objet
zip
est itérable et les tuples sont décompressables. En itérant sur les tuples, je vérifie si val est une instance de la classe
int
ou
float
. S'il
val
n'appartient à aucune de ces classes, je lance
IntFloatValueError
ma propre exception et je lui passe val comme argument.
Classe d'exception IntFloatValueError
Lorsqu'une exception est levée,
IntFloatValueError
nous créons une instance de la classe
IntFloatValueError
et l'affiche simultanément à l'écran. Cela signifie que les méthodes magiques
init
et seront appelées
str
.
La valeur qui a provoqué l'exception levée est définie comme un attribut
value
accompagnant la classe
IntFloatValueError
. Lors de l'appel de la méthode magic str, l'utilisateur reçoit un message d'erreur l'informant que la valeur
init
dans
CustomIntFloatDict
est invalide. L'utilisateur sait quoi faire pour corriger cette erreur.
Classes d'exception
IntFloatValueError
et
KeyValueConstructError
si aucune exception n'est levée, c'est-à-dire
val
de l'objet chaîné sont de types
int
ou
float
, alors ils seront définis à l'aide de
__setitem__()
, et la méthode de la classe parent fera tout pour nous
dict
, comme indiqué ci-dessous.
KeyValueConstructError, classe
Que se passe-t-il si l'utilisateur entre un type qui n'est pas une liste ou un tuple de clés et de valeurs?
Encore une fois, cet exemple est un peu artificiel, mais il est pratique pour vous montrer comment vous pouvez utiliser vos propres classes d'exceptions.
Si l'utilisateur ne spécifie pas les clés et les valeurs sous forme de liste ou de tuple, une exception sera levée
KeyValueConstructError
. Le but de cette exception est d'informer l'utilisateur que pour écrire des clés et des valeurs dans un objet
CustomIntFloatDict
, une liste ou un tuple doit être spécifié dans le constructeur de
init
classe
CustomIntFloatDict
.
Dans l'exemple ci-dessus, en tant que deuxième argument du constructeur
init
beaucoup de choses ont été adoptées et une exception a été levée à cause de cela
KeyValueConstructError
. L'avantage du message d'erreur affiché est que le message d'erreur affiché informe l'utilisateur que les clés et les valeurs à insérer doivent être signalées sous forme de liste ou de tuple.
Là encore, lorsqu'une exception est levée, une instance de KeyValueConstructError est créée et la clé et les valeurs sont transmises en tant qu'arguments au constructeur KeyValueConstructError. Ils sont définis comme les valeurs des attributs clé et valeur de KeyValueConstructError et sont utilisés dans la méthode __str__ pour générer un message d'erreur significatif lorsque le message est affiché à l'écran.
De plus, j'inclus même les types de données inhérents aux objets ajoutés au constructeur
init
- Je fais ça pour plus de clarté.
Définition de la clé et de la valeur dans CustomIntFloatDict
CustomIntFloatDict
hérite de
dict
. Cela signifie qu'il fonctionnera exactement comme un dictionnaire, sauf aux endroits où nous choisissons de modifier sélectivement son comportement.
__setitem__
Est une méthode magique appelée lors de la définition d'une clé et d'une valeur dans un dictionnaire. Dans notre implémentation,
setitem
nous vérifions que la valeur est de type
int
ou
float
, et ce n'est qu'après une vérification réussie qu'elle peut être définie dans le dictionnaire. Si la vérification échoue, vous pouvez à nouveau utiliser la classe d'exception
IntFloatValueError
. Ici, vous pouvez vous assurer que nous obtiendrons une exception en essayant de définir une chaîne
‘bad_value’
comme valeur dans le dictionnaire
test_4
.
Tout le code de ce didacticiel est présenté ci-dessous et publié sur Github .
# , int float
class IntFloatValueError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return '{} is invalid input, CustomIntFloatDict can only accept ' \
'integers and floats as its values'.format(self.value)
class KeyValueContructError(Exception):
def __init__(self, key, value):
self.key = key
self.value = value
def __str__(self):
return 'keys and values need to be passed as either list or tuple' + '\n' + \
' {} is of type: '.format(self.key) + str(type(self.key)) + '\n' + \
' {} is of type: '.format(self.value) + str(type(self.value))
class CustomIntFloatDict(dict):
empty_dict = {}
def __init__(self, key=None, value=None):
if key is None or value is None:
self.get_dict()
elif not isinstance(key, (tuple, list,)) or not isinstance(value, (tuple, list)):
raise KeyValueContructError(key, value)
else:
zipped = zip(key, value)
for k, val in zipped:
if not isinstance(val, (int, float)):
raise IntFloatValueError(val)
dict.__setitem__(self, k, val)
def get_dict(self):
return self.empty_dict
def __setitem__(self, key, value):
if not isinstance(value, (int, float)):
raise IntFloatValueError(value)
return dict.__setitem__(self, key, value)
#
# test_1 = CustomIntFloatDict()
# print(test_1)
# test_2 = CustomIntFloatDict({'a', 'b'}, [1, 2])
# print(test_2)
# test_3 = CustomIntFloatDict(('x', 'y', 'z'), (10, 'twenty', 30))
# print(test_3)
# test_4 = CustomIntFloatDict(('x', 'y', 'z'), (10, 20, 30))
# print(test_4)
# test_4['r'] = 1.3
# print(test_4)
# test_4['key'] = 'bad_value'
Conclusion
Si vous créez vos propres exceptions, travailler avec la classe devient beaucoup plus pratique. La classe d'exception doit avoir des méthodes magiques
init
et
str
qui sont automatiquement appelées lors de la gestion des exceptions. C'est entièrement à vous de décider de ce que fera votre propre classe d'exception. Parmi les méthodes présentées, il y a celles qui sont chargées d'inspecter un objet et d'afficher un message d'erreur informatif à l'écran.
Quoi qu'il en soit, les classes d'exception facilitent grandement la gestion des erreurs qui surviennent!