7 votes

Python - vérification de type OK lorsque l'erreur ne serait pas levée autrement ?

J'ai quelques classes Python qui, si on les simplifie, ressemblent à ceci :

class base:
    def __init__(self, v):
        self.value = v

    def doThings(self):
        print "Doing things."

    def doMoreThings(self):
        print "Doing more things."

    def combine(self, b):
        self.value += b.value

class foo(base):
    def showValue(self):
        print "foo value is %d." % self.value

class bar(base):
    def showValue(self):
        print "bar value is %d." % self.value

En base contient des méthodes (représentées ci-dessus par doThings y doMoreThings ) qui mettent en œuvre des fonctionnalités communes à la fois à l foo y bar sous-classes. Le site foo y bar Les sous-classes diffèrent essentiellement dans la manière dont elles interprètent l'élément value champ. (Au-dessus, ils ne diffèrent que par ce qu'ils montrent lors de l'impression, mais dans mon application réelle, ils font plusieurs autres choses qui sont plus compliquées). base peuvent être considérés comme "abstraits" : les utilisateurs ne travaillent jamais qu'avec les foo et bar s. base n'existe que pour abriter le code commun à ses sous-classes.

La méthode sur laquelle je veux poser une question est combine qui vous permet de prendre deux de ces objets et d'en créer un troisième. Parce que foo y bar interpréter value différemment, cela n'a pas de sens de combine deux sous-classes de types différents : vous pouvez combine deux foo pour obtenir un foo ou deux bar pour obtenir un bar mais pas un foo et un bar . Malgré cela, la procédure de combine est le même pour toutes les sous-classes, il est donc logique qu'elle soit prise en compte et définie à un seul endroit.

J'aimerais probablement signaler une erreur si un utilisateur tente de combine deux objets incompatibles, mais je ne vois pas comment le faire sans introduire de vilains contrôles de type. Est-ce une bonne pratique de faire cela ? Ou dois-je faire comme d'habitude et ne pas vérifier, documenter le problème, et supposer que l'utilisateur n'essaiera pas d'utiliser combine d'une manière qui n'était pas prévue, même si cette utilisation semble " réussir " et renvoie un objet poubelle au lieu de soulever une erreur ?

Merci pour votre aide.

3voto

glglgl Points 35668

Je vois plusieurs approches ici :

  1. Ne vérifiez rien et faites confiance à l'utilisateur pour faire ce qu'il faut. Cela peut être approprié ou dangereux, selon la situation.

  2. Vérifiez le type. Vous avez raison de dire que c'est laid, mais c'est la chose la plus facile.

  3. Ne pas avoir de value mais nommez-les comme ils sont censés l'être ( pressure , temperature ) et laissez-les se combiner entre eux.

  4. Idem que 3, mais en plus les sous-classes ont un property qui met en correspondance les accès à .value à leur variable de valeur "réelle" respective. De cette façon, vous pouvez garder le . __init__() mais le .combine() devra être fait par chaque sous-classe.

  5. Idem que 4, mais n'utilisez pas property mais une auto-création descripteur . En cette réponse Je montre comment on peut le faire.

2voto

Vorsprung Points 5623

Modifier la méthode de la moissonneuse-batteuse

def combine(self, b):
    if (not(self.__class__.__name__ == b.__class__.__name__)):
        raise TypeError("%s cannot combine with %s" % ( self.__class__.__name__, b.__class__.__name__))
    else:        
        self.value += b.value

Alternativement, modifiez la définition de la classe pour base en

class base(object):

et ensuite une méthode de combinaison plus simple, plus agréable et plus heureuse est possible (merci glglgl)

def combine(self, b):
    if (not(type(self) == type(b))):
        raise TypeError("%s cannot combine with %s" %
                        ( type(self), type(b) ))
    else:        
        self.value += b.value

0voto

Jakub M. Points 6126

Quand je travaillais sur du code de production qui ne manquera pas J'ai utilisé un décorateur pour éviter qu'une fonction risquée n'explose, ne se transforme en boule de feu et ne fasse sauter tout mon système.

def bulltproof(func):
    def _bulletproof(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
        except Exception as exc:
            raise BulletproofError(exc)
        return result
    return _bulletproof

Utilisez-le comme ça :

@bulletproof
def some_risky_function():
    #....

Et n'attraper qu'une seule erreur :

try:
    some_risky_function()
except Bulletproof:
...

Au lieu d'afficher l'erreur, vous pouvez retourner None , enregistrer l'erreur ou l'éteindre pour les tests. Bien sûr, cette approche est utile lorsque vous voulez vous assurer que votre programme se remettra d'une erreur, mais parfois il vaut mieux mourir que de continuer avec une erreur

0voto

user2310967 Points 89

Depuis .combine() fait conceptuellement des choses complètement différentes selon que les objets sont foo ou bar Je pense qu'il est logique de ne PAS avoir la fonction dans le parent abstrait, mais de la garder dans les classes dérivées. Même si c'est le même code, combiner deux instances de foo serait une opération différente de la combinaison de deux instances de bar . Si le code pour .combine() est en fait très longue, vous pourriez créer une méthode privée dans la classe de base pour effectuer le travail, mais ne l'appeler qu'à partir de fonctions définies dans la classe dérivée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X