Parfois, la vérification des arguments dans Python est nécessaire. Par exemple, j'ai une fonction qui accepte l'adresse d'un autre nœud du réseau comme adresse de chaîne brute ou la classe Node = qui encapsule les informations de l'autre nœud.
J'utilise la fonction type () comme dans:
if type(n) == type(Node):
do this
Elif type(n) == type(str)
do this
Est-ce une bonne façon de procéder?
Mise à jour 1: Python 3 a une annotation pour les paramètres de fonction. Ceux-ci peuvent être utilisés pour les vérifications de type à l'aide de l'outil: - http://mypy-lang.org/
Utilisez isinstance()
. Échantillon:
if isinstance(n, unicode):
# do this
Elif isinstance(n, Node):
# do that
...
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
Non, la vérification des arguments dans Python n'est pas nécessaire. C'est jamais nécessaire.
Si votre code accepte des adresses en tant que chaîne brute ou en tant qu'objet Node
, votre conception est rompue.
Cela vient du fait que si vous ne connaissez pas déjà le type d'un objet dans votre propre programme, vous faites déjà quelque chose de mal.
La vérification typographique nuit à la réutilisation du code et réduit les performances. Avoir une fonction qui exécute différentes choses selon le type d'objet transmis est sujet aux bogues et a un comportement plus difficile à comprendre et à maintenir.
Vous disposez des options d'assainissement suivantes:
Créez un constructeur d'objet Node
qui accepte les chaînes brutes, ou une fonction qui convertit les chaînes en objets Node
. Faites en sorte que votre fonction suppose que l'argument passé est un objet Node
. De cette façon, si vous devez passer une chaîne à la fonction, vous n'avez qu'à:
myfunction(Node(some_string))
C'est votre meilleure option, elle est propre, facile à comprendre et à entretenir. Quiconque lit le code comprend immédiatement ce qui se passe, et vous n'avez pas à vérifier la frappe.
Créez deux fonctions, une qui accepte les objets Node
et une qui accepte les chaînes brutes. Vous pouvez faire un appel à l'autre en interne, de la manière la plus pratique (myfunction_str
Peut créer un objet Node
et appeler myfunction_node
, Ou l'inverse).
Assurez-vous que les objets Node
ont une méthode __str__
Et à l'intérieur de votre fonction, appelez str()
sur l'argument reçu. De cette façon, vous obtenez toujours une chaîne par coercition.
Dans tous les cas, ne pas cocher la case. C'est complètement inutile et n'a que des inconvénients. Refactorisez votre code à la place d'une manière que vous n'avez pas besoin de vérifier. Vous obtenez uniquement des avantages à le faire, à court et à long terme.
On dirait que vous recherchez une "fonction générique" - qui se comporte différemment en fonction des arguments donnés. C'est un peu comme comment vous obtiendrez une fonction différente lorsque vous appelez une méthode sur un objet différent, mais plutôt que d'utiliser simplement le premier argument (l'objet/self) pour rechercher la fonction, vous utilisez plutôt tous les arguments.
Turbogears utilise quelque chose comme ça pour décider comment convertir des objets en JSON - si je me souviens bien.
Il y a n article d'IBM sur l'utilisation du package dispatcher pour ce genre de chose:
De cet article:
import dispatch
@dispatch.generic()
def doIt(foo, other):
"Base generic function of 'doIt()'"
@doIt.when("isinstance(foo,int) and isinstance(other,str)")
def doIt(foo, other):
print "foo is an unrestricted int |", foo, other
@doIt.when("isinstance(foo,str) and isinstance(other,int)")
def doIt(foo, other):
print "foo is str, other an int |", foo, other
@doIt.when("isinstance(foo,int) and 3<=foo<=17 and isinstance(other,str)")
def doIt(foo, other):
print "foo is between 3 and 17 |", foo, other
@doIt.when("isinstance(foo,int) and 0<=foo<=1000 and isinstance(other,str)")
def doIt(foo, other):
print "foo is between 0 and 1000 |", foo, other
Vous pouvez également utiliser une tentative de capture pour taper check si nécessaire:
def my_function(this_node):
try:
# call a method/attribute for the Node object
if this_node.address:
# more code here
pass
except AttributeError, e:
# either this is not a Node or maybe it's a string,
# so behavior accordingly
pass
Vous pouvez voir un exemple de cela dans Beginning Python dans la seconde sur les générateurs (page 197 dans mon édition) et je crois dans le Python Cookbook. Souvent, attraper un AttributeError
ou TypeError
est plus simple et apparemment plus rapide. En outre, cela peut fonctionner mieux de cette manière car vous n'êtes alors pas lié à un arbre d'héritage particulier (par exemple, votre objet peut être un Node
ou il peut s'agir d'un autre objet qui a le même comportement qu'un Node
).