2010-08-12 7 views
3

Python évangélistes dira la raison pour laquelle Python n'a pas une déclaration de commutateur est parce qu'il a des dictionnaires. Alors ... comment puis-je utiliser un dictionnaire pour résoudre ce problème ici? Le problème est que toutes les valeurs sont en cours d'évaluation et de lever des exceptions en fonction de l'entrée.Python paresseux évaluation du dictionnaire

Ceci est juste un exemple stupide d'une classe qui stocke un nombre ou une liste de nombres et fournit une multiplication.

class MyClass(object): 

    def __init__(self, value): 
     self._value = value 

    def __mul__(self, other): 
     return { 
      (False, False): self._value * other._value      , 
      (False, True): [self._value * o for o in other._value]   , 
      (True , False): [v * other._value for v in self._value]   , 
      (True , True): [v * o for v, o in zip(self._value, other._value)], 
     }[(isinstance(self._value, (tuple, list)), isinstance(other._value, (tuple, list)))] 

    def __str__(self): 
     return repr(self._value) 
    __repr__ = __str__ 



>>> x = MyClass(2.0) 
>>> y = MyClass([3.0, 4.0, 5.0]) 
>>> print x 
2.0 
>>> print y 
[3.0, 4.0, 5.0] 
>>> print x * y 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 8, in __mul__ 
TypeError: can't multiply sequence by non-int of type 'float' 

Une façon que je pouvais résoudre ce serait un préfixe à chaque valeur avec « lambda » et à après l'appel recherche de dictionnaire de la fonction lambda .... « } (isinsta ...) »

Y a-t-il un meilleur moyen?

+1

Les méthodes qui prennent * n'importe quel type de valeur * sont à peu près la pire chose que vous pouvez faire du point de vue de la POO et c'est pourquoi le code est si moche. –

Répondre

1

je peux penser à deux approches ici:

  • Quelques if déclarations. Pour seulement quatre combinaisons de True et False, ce n'est pas si mal. Les séquences de if ... elif ... elif ... sont, d'après ce que j'ai vu, pas rares dans le code Python.

  • Création du dict une fois (en tant que champ de classe, plutôt qu'un champ d'instance), et stockage (lambda) des fonctions à l'intérieur. Cela évolue mieux que l'approche précédente et est plus rapide pour beaucoup d'options (bien que je ne connaisse pas la valeur de "beaucoup").

1

Je pense que le point principal ici est la lisibilité.
Une recherche de dictionnaire comme celui que vous avez montré est définitivement difficile à lire, et donc à maintenir. À mon avis, l'objectif principal tout en écrivant un logiciel devrait être la lisibilité; pour cette raison, j'irais chercher un ensemble de if/elif comparant explicitement les deux valeurs (au lieu d'avoir le mapping pour les types); alors si les mesures montrent des problèmes de performances, d'autres solutions (comme une recherche de dictionnaire avec des fonctions) pourraient être explorées.

3

Oui, définissent les petits lambdas pour ces différentes options:

def __mul__(self, other): 
     scalar_times_scalar = lambda x,y: x*y 
     scalar_times_seq = lambda x,y: [x*y_i for y_i in y] 
     seq_times_scalar = lambda x,y: scalar_times_seq(y,x) 
     seq_times_seq  = lambda x,y: [x_i*y_i for x_i,y_i in zip(x,y)] 
     self_is_seq, other_is_seq = (isinstance(ob._value,(tuple, list)) 
                for ob in (self, other)) 
     fn = { 
      (False, False): scalar_times_scalar, 
      (False, True): scalar_times_seq, 
      (True , False): seq_times_scalar, 
      (True , True): seq_times_seq, 
      }[(self_is_seq, other_is_seq)] 
     return fn(self._value, other._value) 

Idéalement, bien sûr, vous définiriez ces lambdas qu'une seule fois à portée de la classe ou d'un module. Je les ai juste montrés dans la méthode __mul__ ici pour la facilité de référence.