Nécessité de router les appels d'instance dans une classe python
Question
Le problème est la nécessité de prendre en compte les arguments avant de choisir le répondant. Voici ma tentative jusqu'à présent.
from responders import A, B, C
class RandomResponder(object)
def init(self, *args, *kwargs):
self.args = args
self.kwargs = kwargs
def __getattr__(self, name):
# pick a responder based on the args the function was called with
# I don't know how to do this part
# for sake of argument lets the args a function was called with lead me to pick responder A
r = A
responder = r(*self.args, **self.kwargs)
return responder.__getattr__(name)
L'effet souhaité serait:
r = RandomResponder()
r.doSomething(1)
#returns A.doSomething()
r.doSomething(2)
#returns B.doSomething()
r.doSomething(3)
#return C.doSomething()
r.doSomethingElse(1)
#returns A.doSomethingElse()
r.doSomethingElse(2)
#returns B.doSomethingElse()
r.doSomethingElse(3)
#returns C.doSomethingElse()
Je ne saurai pas à l'avance toutes les fonctions contenues dans les répondeurs A, B et C.
La solution
Essayez ceci:
class RandomResponder(object):
choices = [A, B, C]
@classmethod
def which(cls):
return random.choice(cls.choices)
def __getattr__(self, attr):
return getattr(self.which(), attr)
which () sélectionne de manière aléatoire une option parmi les choix et getattr à utiliser pour obtenir l'attribut.
EDIT: on dirait que vous voulez quelque chose de plus semblable à ça.
class RandomResponder(object):
choices = [A, B, C]
def __getattr__(self, attr):
# we define a function that actually gets called
# which takes up the first positional argument,
# the rest are left to args and kwargs
def doCall(which, *args, **kwargs):
# get the attribute of the appropriate one, call with passed args
return getattr(self.choices[which], attr)(*args, **kwargs)
return doCall
Cela pourrait être écrit en lambda, mais je vais le laisser comme ça pour que ce soit plus clair.
Autres conseils
Quand vous faites cela
r.doSomething(1)
ce qui se passe est, dans l'ordre:
-
r.__getattr__
est appelé et renvoie un objet - cet objet est appelé avec un argument & "1 &";
Au moment où __getattr__
est appelé, vous n'avez aucun moyen de savoir quels arguments l'objet que vous renvoyez est appelé , ou même s'il le sera. ..
Ainsi, pour obtenir le comportement souhaité, <=> doit renvoyer un objet appelable qui prend la décision lui-même en fonction des arguments avec lesquels est appelé. Par exemple
from responders import A, B, C
class RandomResponder(object):
def __getattr__(self, name):
def func(*args, **kwds):
resp = { 1:A, 2:B, 3:C }[args[0]] # Decide which responder to use (example)
return getattr(resp, name)() # Call the function on the responder
return func
Qu'en est-il de:
RandomResponder = [A, B, C]
RandomResponder[0].doSomething() # returns A.doSomething()
RandomResponder[1].doSomething() # returns B.doSomething()
RandomResponder[2].doSomething() # returns C.doSomething()
# etc
Si vous spécifiez args
(sans l'astérisque), il ne s'agit que d'une liste de valeurs (chaînes). De même, kwargs
est un dicton de correspondance de clés (chaînes) avec des valeurs (chaînes).
Ce est l'un des premiers résultats que j'ai trouvés après la recherche sur Google args kwargs .
Edit: En fait, je ne sais pas trop ce que vous cherchez, ce n’est donc qu’une hypothèse.
Essayez-vous de faire cela?
from responders import A, B, C
class RandomResponder(object)
def pickAResponder( self, someName ):
"""Use a simple mapping."""
return { 'nameA': A, 'nameB': B, 'nameC': C }[someName]
def __init__(self, *args, *kwargs):
self.args = args
self.kwargs = kwargs
def __getattr__(self, name):
"""pick a responder based on the args[0]"""
r = self.pickAResponder(self.args[0])
responder = r(*self.args, **self.kwargs)
return responder.__getattr__(name)
Vos classes de répondeurs (A, B, C) ne sont que des objets. Vous pouvez manipuler une classe en utilisant des mappages, des listes, des instructions if, le code Python de votre choix dans la méthode pickAResponder
.