2010-09-20 23 views
22

J'ai défini une classe Vector qui a trois variables de propriétés: x, y et z. Les coordonnées doivent être des nombres réels, mais il n'y a rien pour arrêter un de faire ce qui suit:Type de sécurité en Python

>>> v = Vector(8, 7.3, -1) 
>>> v.x = "foo" 
>>> v.x 
"foo" 

je pourrais mettre en œuvre la « sécurité de type » comme celui-ci:

import numbers 

class Vector: 
    def __init__(self, x, y, z): 
     self.setposition(x, y, z) 

    def setposition(self, x, y, z): 
     for i in (x, y, z): 
      if not isinstance(i, numbers.Real): 
       raise TypeError("Real coordinates only") 

     self.__x = x 
     self.__y = y 
     self.__z = z 

    @property 
    def x(self): 
     return self.__x 

    @property 
    def y(self): 
     return self.__y 

    @property 
    def z(self): 
     return self.__z 

... mais cela semble ONU- Pythonique.

Suggestions?

+0

Mais pourquoi? Les entiers fonctionnent totalement. –

+0

Je comprends ce genre de préoccupations viennent généralement avec de grands projets/équipe. Si la sécurité des types est quelque chose que vous ressentez fortement alors je suggère que vous jetez un oeil à la place. – BlueSky

Répondre

17

Vous devez vous demander pourquoi vous souhaitez tester le type lors de la définition de ces valeurs. Il suffit d'augmenter un TypeError dans tout calcul qui arrive à trébucher sur le mauvais type . Bonus: les opérations standard le font déjà.

>>> 3.0/'abc' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in ? 
TypeError: unsupported operand type(s) for /: 'float' and 'str' 
+2

Ensuite, écrivez un ensemble complet de tests unitaires qui exercent toutes les branches de votre code, pour attraper vos TypeErrors avant vos utilisateurs. Droite? De toute façon, en ce qui concerne le moment où l'erreur devrait être lancée: si vous savez qu'une certaine combinaison est invalide et posera des problèmes, ne serait-il pas préférable/plus clair d'échouer le plus tôt possible? Ne pas échouer tôt rend la recherche du problème racine beaucoup plus facile? (Je suppose que le "pourquoi c'est invalide" est moins clair, cependant.) –

+1

Je pense que la question était sur LBYL vs. EAFP, donc d'une manière "pythonique" il est logique non seulement de "permettre" les types que vous vouliez initialement . Pour un «mauvais résultat connu», il est parfaitement logique de couper son chemin d'exécution (relèvement). – knitti

4

Mais il n'y a rien pour arrêter un de faire ce qui suit:

Je crois en essayant d'empêcher quelqu'un de faire quelque chose comme ça est non Pythonic. Si vous devez, alors vous devriez vérifier la sécurité de type pendant toutes les opérations que vous pourriez faire en utilisant Vector, à mon avis.

Pour citer G.V.R:

nous sommes tous adultes.

après tout. Voir ce question et ses réponses pour plus d'informations. Je suis sûr que les pythonistes plus expérimentés ici peuvent vous donner de meilleures réponses.

12

Duck Typing est la manière habituelle en Python. Il devrait fonctionner avec tout ce que se comporte comme un nombre, mais pas nécessairement est un nombre réel.

Dans la plupart des cas, en Python, il ne faut pas vérifier explicitement les types. Vous gagnez en flexibilité car votre code peut être utilisé avec des types de données personnalisés, à condition qu'ils se comportent correctement.

4

Les autres réponses ont déjà fait remarquer que cela n'a pas beaucoup de sens de vérifier le type ici. De plus, votre classe ne sera pas très rapide si elle est écrite en pur Python.

Si vous voulez une solution plus pythonique - vous pouvez utiliser setters de propriété comme:

@x.setter 
def x(self, value): 
    assert isinstance(value, numbers.Real) 
    self.__x = value 

L'instruction assert sera supprimé lorsque vous désactivez le débogage ou d'activer le mode optimisation.

Vous pouvez également forcer value à virgule flottante dans le setter.Cela déclenchera une exception si le type/la valeur n'est pas convertible:

@x.setter 
def x(self, value): 
    self.__x = float(value) 
2

Vous n'êtes pas censé fournir la sécurité de type de cette façon. Oui, quelqu'un peut délibérément casser votre code en fournissant des valeurs pour lesquelles votre conteneur ne fonctionnera pas - mais c'est la même chose avec d'autres langues. Et même si quelqu'un met la bonne valeur pour un paramètre dans une méthode ou une fonction membre ne signifie pas nécessairement que ce n'est pas cassé: Si un programme attend une adresse IP, mais vous passez un nom d'hôte, cela ne fonctionnera toujours pas, bien que les deux cordes.

Ce que je dis est: L'état d'esprit de Python est intrinsèquement différent. Duck typage dit essentiellement: Hey, je ne suis pas limité à certains types, mais à l'interface, ou le comportement des objets. Si un objet agit comme si c'était le genre d'objet auquel je m'attendais, je m'en fous - allez-y.

Si vous essayez d'introduire la vérification de type, vous limitez fondamentalement l'une des fonctionnalités les plus utiles de la langue. Ceci étant dit, vous avez vraiment besoin d'un développement piloté par les tests, ou de tests unitaires au moins. Il n'y a vraiment aucune excuse pour ne pas le faire avec des langages dynamiques - c'est juste en déplaçant la façon dont les erreurs (type) sont détectées à une autre étape du processus de construction, loin du temps de compilation pour exécuter une suite de tests plusieurs fois par jour. Bien que cela semble être un effort supplémentaire, cela réduira le temps consacré au débogage et à la correction du code, car c'est un moyen intrinsèquement plus puissant de détecter les erreurs dans votre code.

Mais assez de cela, je suis déjà décousu.

+0

Bien que "interface" soit un peu impropre ici, car aucune interface n'est explicitement définie (sauf si tout va bien dans les commentaires). La saisie de canard signifie que vous avez réellement regardé ou exécuté le code pour voir ce que "l'interface" est. Dans mon expérience (limitée) en Python, c'est le plus grand inconvénient du typage du canard. Écrire mon propre code est plus rapide, mais utiliser l'API non documentée de quelqu'un d'autre (par exemple écrire un plugin) est beaucoup plus difficile, donc les commentaires deviennent plus cruciaux. –

+1

Les tests unitaires sont excellents, mais le passage à travers les tests dans un débogueur n'est pas le moyen le plus optimal d'apprendre quel type de canards chaque méthode s'attend à être transmis - c'est-à-dire, comment utiliser correctement l'API. Et il est difficile de garantir que vos tests unitaires couvrent toujours tous les cas. –