views:

216

answers:

5

The problem is a need to take the arguments into account before choosing the responder. Here is my attempt so far.

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)

The desired effect would be:

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()

I will not know ahead of time all the functions contained with the responders A, B, and C.

A: 

If you specify args (without the asterisk), it's just a List of values (strings). Similarly, kwargs is a Dict of matching keys (strings) to values (strings).

This is one of the first results I found after Googling args kwargs.

Edit: I actually don't know quite what you're looking for, so this is just a guess.

Nikhil Chelliah
A: 

Are you trying to do this?

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)

Your responder classes (A, B, C) are just objects. You can manipulate a class using mappings, lists, if-statements, whatever Python coding you want in the pickAResponder method.

S.Lott
Close, but I don't want the use the args from the __init__, I want to use the args that the attribute is called with.
Jason Christa
Please update your question with the additional facts.
S.Lott
+1  A: 

What about:

RandomResponder = [A, B, C]
RandomResponder[0].doSomething()   # returns A.doSomething()
RandomResponder[1].doSomething()   # returns B.doSomething()
RandomResponder[2].doSomething()   # returns C.doSomething()
# etc
John Fouhy
+3  A: 

When you do this

r.doSomething(1)

what happens is, in order:

  • r.__getattr__ is called, and returns an object
  • this object is called with an argument "1"

At the time when __getattr__ is called, you have no way of knowing what arguments the object you return is going to get called with, or even if it's going to be called at all...

So, to get the behavior that you want, __getattr__ has to return a callable object that makes the decision itself based on the arguments it's called with. For example

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
dF
Should be: resp = { 1:A, 2:B, 3:C }[args[0] - 1]Because it's not zero-indexed.
Nikhil Chelliah
+2  A: 

Try this:

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() randomly selects an option from the choices, and which getattr uses to get the attribute.

EDIT: it actually looks like you want something more like this.

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

This could be written using lambda, but I'll just leave it like this so it's clearer.

Paul Fisher
Very clever by you and dF, I never thought to return a function that contains the logic.
Jason Christa