2010-03-21 4 views
0

Le titre n'est pas très clair mais je vais essayer d'expliquer.python, classe d'emballage retournant la moyenne des membres enveloppés

Ayant cette classe:

class Wrapped(object): 
    def method_a(self): 
     # do some operations 
     return n 

    def method_b(self): 
     # also do some operations 
     return n 

Je veux avoir une classe qui fonctionne de la même manière que celui-ci:

class Wrapper(object): 
    def __init__(self): 
     self.ws = [Wrapped(1),Wrapped(2),Wrapped(3)] 

    def method_a(self): 
     results=[Wrapped.method_a(w) for w in self.ws] 
     sum_ = sum(results,0.0) 
     average = sum_/len(self.ws) 
     return average 

    def method_b(self): 
     results=[Wrapped.method_b(w) for w in self.ws] 
     sum_ = sum(results,0.0) 
     average = sum_/len(self.ws) 
     return average 

Il est évident que ce n'est pas le problème réel à portée de main (il est seulement deux méthodes), et ce code est également incomplet (seulement inclus le minimum pour expliquer le problème). Donc, ce que je cherche est un moyen d'obtenir ce comportement. Signification, quelle que soit la méthode appelée dans la classe wrapper, appelez cette méthode pour tous les objets de la classe Wrapped et renvoyez la moyenne de leurs résultats.

Cela peut-il être fait? Comment?

Merci d'avance.

Modifier
Merci pour les réponses ... après les avoir vus la solution tout semble évident :) J'ai choisi la réponse d'Alex Martelli, car il a bien expliqué la solution. Les autres réponses sont aussi utiles, c'est pourquoi j'ai aussi voté pour eux.

Répondre

3

Bien que tout à fait faisable, c'est juste un peu délicat parce que l'obtention d'une méthode (ou d'un autre attribut) et l'appel de celui-ci sont opérations séparées. Voici une solution:

class Wrapper(object): 
    def __init__(self): 
     self.ws = [Wrapped(1),Wrapped(2),Wrapped(3)] 

    def __getattr__(self, n): 
     meth = getattr(Wrapped, n) 
     def caller(): 
      results = [meth(w) for w in self.ws] 
      sum_ = sum(results,0.0) 
      average = sum_/len(self.ws) 
      return average 
     return caller 

Il est un peu simpliste (en supposant aucun changement self.ws entre l'obtention et l'appel - on pourrait bien sûr « instantané » self.ws au moment de l'obtention, si c'est la sémantique souhaitées; n'utilise pas @functools.wraps donc ne conserve pas le docstring & c si ceux-ci ont besoin de préservation), mais devrait être surtout utilisable pour vos besoins.

1

Vous pouvez à ce (appeler une méthode « arbitraire » sur un objet) avec getattr:

Considérez cela comme exemple:

# this fetches you references to the 'method_a' methods for objects in self.ws 
funcs = [getattr(wrapped_obj, "method_a") for wrapped_obj in self.ws] 
# now execute them to get the results 
results = [func() for func in funcs] 

Le reste de votre code resterait le même.

0

Vous pouvez le faire en utilisant la méthode spéciale getattr.

1

Vous pouvez le faire en implémentant une méthode __getattr__ dans votre Wrapper qui renvoie une fonction qui transfère l'appel à tous les objets encapsulés. Voici un exemple d'implémentation de base renvoie simplement les résultats:

import functools 

class Wrapper(object): 
    def __init__(self, *args): 
     self._targets = args 

    def _broadcast(self, name, *args, **kwargs): 
     return [getattr(t, name)(*args, **kwargs) for t in self._targets] 

    def __getattr__(self, name): 
     return functools.partial(self._broadcast, name) 

Notez que __getattr__ sera uniquement appeler quand un attribut est introuvable, de sorte que tous les noms de méthode que vous définissez dans Wrapper ne seront pas transmises (par exemple _broadcast dans l'exemple ci-dessus).

Questions connexes