tags:

views:

91

answers:

4
class Class1(object):
    ...

class Class2(object):
    ...

class Class3(object):
    ...

class A(object):
    def _methA(parm1, parm2)
        ...

    def _methB(parm1, parm2)
        ...

    def _methC(parm1, parm2)
        ...

    def manager(parm1, method, params)
        ...
        if parm1.__class__.__name__==Class1.__name__:
            response = _methA(parm1, params)
        elif parm1.__class__.__name__==Class2.__name__:
            response = _methB(parm1, params)
        elif io_source.__class__.__name__==Class3.__name__:
            response = _methC(parm1, params)
        else:
            raise Exception, "Unsupported parm1"
        ...

I didn't like the way that if/elif block in manager() looked and refactored it to this:

def manager(parm1, method, params)
    ...
    try:
        response = {
                Class1.__name__: lambda parm1, parms: _methA(parm1, parms),
                Class2.__name__: lambda parm1, parms: _methB(parm1, parms),
                Class3.__name__: lambda parm1, parms: _methC(parm1, parms)
                }[parm1.__class__.__name__](parm1, parms)
    except KeyError:
        raise Exception, "Unsupported parm1"

But the fact that the code is still looking at class names bothers me - I really don't know how to explain why... Should it bother me?

Is there a better way to write code to call a method in class A that, depending on the class of one of its parameters, triggers the calling of different methods in A?

PS. Sorry for the contrived example, but posting the actual code would make the question even more convoluted. I tried to distill the issue to its essence...

A: 

I would prefer

if isinstance(parm1, Class1):
    _methA(parm1, params)
elif isinstance(parm1, Class2):
    _methB(parm1, params)
elif isinstance(parm1, Class3):
    _methC(parm1, params)

but this still smells of a design flaw. :)

Maybe your three classes ClassX should rather all have a single method meth(params), then your manager could just call parm1.meth(params).

jellybean
+1  A: 

You're trying to emulate what language geeks call "multiple dispatch", or "multimethods". The linked-to Wikipedia article has a good discussion, including Python examples.

Jonathan Feinberg
+5  A: 

That's one of many wrong ways to implement polymorphism. You should never look at class names. Looking at class names should bother you because it means that you haven't delegated the responsibility correctly.

Move each method into the appropriate class.

class Class1(object):
    def method( self, theA, params ):
        theA.methA( params )

class Class2(object):
    def method( self, theA, params ):
        theA.methB( params )

class Class3(object):
    def method( self, theA, params ):
        theA.methC( params )

class A(object):
    def methA(parm1, parm2)
        ...

    def methB(parm1, parm2)
        ...

    def methC(parm1, parm2)
        ...

    def manager(parm1, method, params)
        ...
        param1.method( self, params )
S.Lott
+1 for verbalizing why I feel queasy on the code looking at class names. :-) Yeah, that is one of the things we are discussing - why are these methods not encapsulated in Class1, Class2 and Class3. In the actual code there are reasons for it (they may not be good reasons though).
celopes
@celopes: "they may not be good reasons" -- almost correct. They are not good reasons. They "seem" to make `Class1`, `Class2` and `Class3` aware of features of `A`; but that judgement is false. The "hidden" methods of `A` really *are* the public interface of `A`.
S.Lott
A: 

I usually do this when processing messages, so i don't have tons of ifs ... but it still uses the class names.

Kind of a poor man's polymorphism - but, as S.Lott said, Python supports real polymorphism so why not use it :p

class Handler(object):
    # .. stuff

    def dispatch(self, msg):
        handlername = "on_%s" % type(msg)
        return getattr(self, handlername, 'on_missing_handler')(msg)

    def on_SomeClass(self, msg):
        # msg is of SomeClass here ..

    def on_SomeOtherClass(self, msg):
        # msg is of SomeOtherClass here ..

    def on_missing_handler(self, msg):
        # there is no other handler for msg
THC4k