À propos de la frappe de canard :
Le typage de canard est aidé en ne testant généralement pas le type d'arguments dans les corps de méthode et de fonction, en s'appuyant sur la documentation, un code clair et des tests pour garantir une utilisation correcte.
À propos de la validation des arguments (EAFP: plus facile de demander pardon que permission). Un exemple adapté d' ici :
... il est considéré comme plus pythonique de faire:
def my_method(self, key):
try:
value = self.a_dict[member]
except TypeError:
# do something else
Cela signifie que toute autre personne utilisant votre code n'a pas besoin d'utiliser un véritable dictionnaire ou une sous-classe - ils peuvent utiliser n'importe quel objet qui implémente l'interface de mappage.
Malheureusement, dans la pratique, ce n'est pas si simple. Que faire si le membre dans l'exemple ci-dessus peut être un entier? Les entiers sont immuables - il est donc parfaitement raisonnable de les utiliser comme clés de dictionnaire. Cependant, ils sont également utilisés pour indexer des objets de type séquence. Si le membre se trouve être un entier, l'exemple deux pourrait laisser passer des listes et des chaînes ainsi que des dictionnaires.
À propos de la programmation affirmée :
Les assertions sont un moyen systématique de vérifier que l'état interne d'un programme est celui attendu par le programmeur, dans le but de détecter les bogues. En particulier, ils sont bons pour détecter les fausses hypothèses qui ont été faites lors de l'écriture du code, ou pour abuser d'une interface par un autre programmeur. De plus, ils peuvent agir en tant que documentation en ligne dans une certaine mesure, en rendant évidentes les hypothèses du programmeur. ("Explicite vaut mieux qu'implicite.")
Les concepts mentionnés sont parfois en conflit, donc je compte sur les facteurs suivants pour choisir si je ne fais aucune validation des données, si je fais une validation forte ou si j'utilise des assertions:
Validation forte. Par validation forte, j'entends lever une exception personnalisée (
ApiError
par exemple). Si ma fonction / méthode fait partie d'une API publique, il est préférable de valider l'argument pour afficher un bon message d'erreur sur un type inattendu. En vérifiant le type, je ne veux pas dire utiliser uniquementisinstance
, mais aussi si l'objet passé prend en charge l'interface nécessaire (typage canard). Alors que je documente l'API et spécifie le type attendu et que l'utilisateur peut vouloir utiliser ma fonction de manière inattendue, je me sens plus en sécurité lorsque je vérifie les hypothèses. J'utilise habituellementisinstance
et si plus tard je veux supporter d'autres types ou canards, je change la logique de validation.Programmation affirmée. Si mon code est nouveau, j'utilise beaucoup les affirmations. Quels sont vos conseils à ce sujet? Supprimez-vous plus tard les assertions du code?
Si ma fonction / méthode ne fait pas partie d'une API, mais transmet certains de ses arguments à un autre code non écrit, étudié ou testé par moi, je fais beaucoup d'assertions en fonction de l'interface appelée. Ma logique derrière cela - mieux vaut échouer dans mon code, puis quelque part 10 niveaux plus profonds dans stacktrace avec une erreur incompréhensible qui force à déboguer beaucoup et à ajouter plus tard l'assertion à mon code de toute façon.
Commentaires et conseils sur le moment d'utiliser ou de ne pas utiliser la validation de type / valeur, affirme-t-il? Désolé pour la meilleure formulation de la question.
Par exemple, considérons la fonction suivante, où se Customer
trouve un modèle déclaratif SQLAlchemy:
def add_customer(self, customer):
"""Save new customer into the database.
@param customer: Customer instance, whose id is None
@return: merged into global session customer
"""
# no validation here at all
# let's hope SQLAlchemy session will break if `customer` is not a model instance
customer = self.session.add(customer)
self.session.commit()
return customer
Il existe donc plusieurs façons de gérer la validation:
def add_customer(self, customer):
# this is an API method, so let's validate the input
if not isinstance(customer, Customer):
raise ApiError('Invalid type')
if customer.id is not None:
raise ApiError('id should be None')
customer = self.session.add(customer)
self.session.commit()
return customer
ou
def add_customer(self, customer):
# this is an internal method, but i want to be sure
# that it's a customer model instance
assert isinstance(customer, Customer), 'Achtung!'
assert customer.id is None
customer = self.session.add(customer)
self.session.commit()
return customer
Quand et pourquoi utiliseriez-vous chacun de ces éléments dans le contexte de la frappe de canard, de la vérification de type et de la validation des données?