Necesita enrutar llamadas de instancia dentro de una clase de Python
Pregunta
El problema es la necesidad de tener en cuenta los argumentos antes de elegir el respondedor. Aquí está mi intento hasta ahora.
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)
El efecto deseado sería:
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()
No sabré de antemano todas las funciones contenidas en los respondedores A, B y C.
Solución
Prueba esto:
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 () selecciona aleatoriamente una opción de las opciones, y que getattr usa para obtener el atributo.
EDITAR: en realidad parece que quieres algo más como esto.
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
Esto podría escribirse usando lambda, pero lo dejaré así para que quede más claro.
Otros consejos
Cuando haces esto
r.doSomething(1)
lo que sucede es, en orden:
-
r.__getattr__
se llama y devuelve un objeto - este objeto se llama con un argumento " 1 "
En el momento en que se llama a __getattr__
, no tiene forma de saber con qué argumentos se va a llamar el objeto que devuelve, va , o incluso si se va a llamar. ..
Por lo tanto, para obtener el comportamiento que desea, <=> tiene que devolver un objeto invocable que tome la decisión en sí mismo en función de los argumentos con los que se llama. Por ejemplo
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é pasa con:
RandomResponder = [A, B, C]
RandomResponder[0].doSomething() # returns A.doSomething()
RandomResponder[1].doSomething() # returns B.doSomething()
RandomResponder[2].doSomething() # returns C.doSomething()
# etc
Si especifica args
(sin el asterisco), es solo una Lista de valores (cadenas). Del mismo modo, kwargs
es un Dict de coincidencia de claves (cadenas) con valores (cadenas).
Esto es uno de los primeros resultados que encontré después de buscar en Google args kwargs .
Editar: en realidad no sé exactamente lo que estás buscando, así que esto es solo una suposición.
¿Estás tratando de hacer esto?
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)
Tus clases de respuesta (A, B, C) son solo objetos. Puede manipular una clase utilizando asignaciones, listas, sentencias if, cualquier codificación de Python que desee en el método pickAResponder
.