À 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?
la source
Réponses:
Permettez-moi de vous donner quelques principes directeurs.
Principe # 1. Comme indiqué dans http://docs.python.org/2/reference/simple_stmts.html, la surcharge de performances des assertions peut être supprimée avec une option de ligne de commande, tout en étant là pour le débogage. Si les performances sont un problème, faites-le. Laissez les assertions. (Mais ne faites rien d'important dans les assertions!)
Principe # 2. Si vous affirmez quelque chose et que vous avez une erreur fatale, utilisez une assertion. Il n'y a absolument aucune valeur à faire autre chose. Si quelqu'un veut changer cela plus tard, il peut changer votre code ou éviter cet appel de méthode.
Principe # 3. Ne refusez pas quelque chose simplement parce que vous pensez que c'est une chose stupide à faire. Et si votre méthode autorise les chaînes? Si ça marche, ça marche.
Principe # 4. Interdisez les choses qui sont des signes d'erreurs probables. Par exemple, envisagez de passer un dictionnaire d'options. Si ce dictionnaire contient des éléments qui ne sont pas des options valides, cela signifie que quelqu'un n'a pas compris votre API ou qu'il avait une faute de frappe. Faire sauter cela est plus susceptible d'attraper une faute de frappe que d'empêcher quelqu'un de faire quelque chose de raisonnable.
Sur la base des 2 premiers principes, votre deuxième version peut être jetée. Lequel des deux autres vous préférez est une question de goût. Lequel pensez-vous le plus probable? Que quelqu'un passera un non-client à
add_customer
et les choses vont se casser (dans ce cas, la version 3 est préférée), ou que quelqu'un voudra à un moment donné remplacer votre client par un objet proxy d'une sorte qui répond à toutes les bonnes méthodes (auquel cas la version 1 est préférée).Personnellement, j'ai vu les deux modes de défaillance. J'aurais tendance à opter pour la version 1 en partant du principe général que je suis paresseux et qu'il faut moins taper. (De plus, ce type d'échec a tendance à apparaître tôt ou tard d'une manière assez évidente. Et quand je veux utiliser un objet proxy, je suis vraiment ennuyé par les gens qui m'ont attaché les mains.) Mais il y a des programmeurs que je respecte qui irait dans l'autre sens.
la source