how to dispatch objects depending on their class

C

Curzio Basso

Hi all.

I have a couple of question regarding the following situation:

class A(object):
def __init__(self):
pass

class B(object):
def __init__(self):
A.__init__(self)

def func(object):
if isinstance(object, A):
do_something_with_A(object)
elif isinstance(object, B):
do_something_with_B(object)

Note that in my real problem I cannot move the logic of func to the
class A and B because I will have a hierarchy also for func. Then I need
a way to dispatch the object to the right function. I thought about
using a dictionary, like:

FUNC = {"<class '__main__.A'>": do_something_with_A,
"<class '__main__.B'>": do_something_with_B}

def func(object):
FUNC[type(object)](object)

But: (1) I am not sure of what the type(object) function return. In this
case A and B are in the __main__ namespace (assuming this is how is
called), but if they are in a module; (2) I am not sure if it is
efficient; and finally (3): probably there is a better way to do it
(this is always a safe assumption).

I hope my problem is clear, and excuse me if I am asking about something
that is common knowledge, but I don't even know what to google for...

thanks, curzio
 
P

Peter Otten

Curzio said:
FUNC = {"<class '__main__.A'>": do_something_with_A,
"<class '__main__.B'>": do_something_with_B}

def func(object):
FUNC[type(object)](object)

You'll have to modify that slightly for it to work. It will be very
efficient, too.

func_dict = {A: do_something_with_A,
B: do_something_with_B}
def func(obj):
func_dict[obj.__class__](obj)

Using classes as keys avoids the ambiguity you fear. The overhead will be
one dictionary lookup and a function call. The main drawback of this simple
approach is that it does not search the inheritance tree but insists on an
exact class match.
I hope my problem is clear, and excuse me if I am asking about something
that is common knowledge, but I don't even know what to google for...

Maybe

python generic dispatch

Peter
 
C

Casey Duncan

It should not be a problem to use the actual class object as keys in the
dict and the functions as values. No need to convert the class to
strings.

So why can't they be static methods of the classes exactly? That would
be simpler, however the dict will work efficiently.

-Casey
 
B

Bruno Desthuilliers

Curzio said:
Hi all.

I have a couple of question regarding the following situation:

class A(object):
def __init__(self):
pass

class B(object):
def __init__(self):
A.__init__(self)

def func(object):
if isinstance(object, A):
do_something_with_A(object)
elif isinstance(object, B):
do_something_with_B(object)

Note that in my real problem I cannot move the logic of func to the
class A and B because I will have a hierarchy also for func. Then I need
a way to dispatch the object to the right function.

I saw something like a multi-methods implementation in Python somewhere,
this may interest you.
http://www-106.ibm.com/developerworks/linux/library/l-pydisp.html
 
P

Peter Abel

Curzio Basso said:
Hi all.

I have a couple of question regarding the following situation:

class A(object):
def __init__(self):
pass

class B(object):
def __init__(self):
A.__init__(self)

def func(object):
if isinstance(object, A):
do_something_with_A(object)
elif isinstance(object, B):
do_something_with_B(object)

Note that in my real problem I cannot move the logic of func to the
class A and B because I will have a hierarchy also for func. Then I need
a way to dispatch the object to the right function. I thought about
using a dictionary, like:

FUNC = {"<class '__main__.A'>": do_something_with_A,
"<class '__main__.B'>": do_something_with_B}

def func(object):
FUNC[type(object)](object)

But: (1) I am not sure of what the type(object) function return. In this
case A and B are in the __main__ namespace (assuming this is how is
called), but if they are in a module; (2) I am not sure if it is
efficient; and finally (3): probably there is a better way to do it
(this is always a safe assumption).

I hope my problem is clear, and excuse me if I am asking about something
that is common knowledge, but I don't even know what to google for...

thanks, curzio


At my opinion you try to do something outside a class which should
be a basic task of OOP. Why not let the instances do their class-specific
things:

class A(object):
def __init__(self):
pass
def do_something(self):
""" Do A-specific things """
pass

class B(object):
def __init__(self):
A.__init__(self)
def do_something(self):
""" Do B-specific things """
pass

a=A()
b=B()
a.do_something()
b.do_something()

Regards
Peter
 
C

Curzio Basso

Peter said:
At my opinion you try to do something outside a class which should
be a basic task of OOP. Why not let the instances do their class-specific
things:

I know that this situation would be typically handled by putting the
logic in the class hierarchy, but as I wrote I don't want this. In fact,
as (e-mail address removed) pointed out, at the end what I want to implement
is a visitor pattern (maybe I should have simply mentioned this). In C++
then the right implementations are chosen at compile time because of the
function overloading, but in Python I have to find a different way to
implement this.

cheers, curzio
 
C

Curzio Basso

Visitor Pattern?

Actually, I thought what I was describing was a visitor pattern but I am
probably wrong.

Is it correct that in the visitor pattern the dispatching would be
delegated to the classes, like with:

class A(object):
def __init__(self):
pass
def accept(self, visitor)
visitor.do_something_with_A(self)

class B(A):
def __init__(self):
A.__init__(self)
def accept(self, visitor)
visitor.do_something_with_B(self)

class Visitor(object):
def __init__(self):
pass
def do_something_with_A(self, object):
pass
def do_something_with_B(self, object):
pass

Then I would call

a = A()
b = B()
v = Visitor()
a.accept(v)
b.accept(v)

Is this correct?

thanks, curzio
 
C

Curzio Basso

Casey said:
It should not be a problem to use the actual class object as keys in the
dict and the functions as values. No need to convert the class to
strings.

Ops, did not think about that. But maybe the string is a more efficient key?
So why can't they be static methods of the classes exactly? That would
be simpler, however the dict will work efficiently.

Well, as I wrote in the original post and in some reply, I want to keep
the logic of this particular function separated from the class
hierarchy. And as (e-mail address removed) suggested this could be a Visitor
pattern. I'm looking on that.

cheers, curzio
 
C

Curzio Basso

Peter said:
func_dict = {A: do_something_with_A,
B: do_something_with_B}
def func(obj):
func_dict[obj.__class__](obj)

ok. it tested and it works. thanks.

now I have only to check if the Visitor Pattern is not more appropriate.

cheers, curzio
 
D

Duncan Booth

Is it correct that in the visitor pattern the dispatching would be
delegated to the classes, like with:
Is this correct?
Roughly yes, but with a dynamic language like Python you can easily extract
the implementation out into a mixin class rather than repeating what is
essentially the same code everywhere. e.g.
def accept(self, visitor):
getattr(visitor, 'visit_' + self.__class__.__name__)(self)

def visit_A(self, object):
print "Visiting an A", object
def visit_B(self, object):
print "Visiting a B", object


In practice the accept function could be more complicated, e.g. visiting
child objects or handling errors more intelligently.
 
P

Peter Otten

Curzio said:
Peter said:
func_dict = {A: do_something_with_A,
B: do_something_with_B}
def func(obj):
func_dict[obj.__class__](obj)

ok. it tested and it works. thanks.

now I have only to check if the Visitor Pattern is not more appropriate.

I'd say the choice of the dispatch mechanism - dictionary lookup vs Duncan
Booth's name-mangling - is independent of the question whether the visitor
pattern applies. Duncan's setup would then become something like

class VisitorMixin:
def accept(self, visitor):
visitor.dispatch[self.__class__](self)

class Visitor:
def __init__(self):
self.dispatch = {A: self.visit_A, B: self.visit_B}
def visit_A(self, a):
pass
def visit_B(self, b):
pass


Peter
 
C

Curzio Basso

Peter said:
I'd say the choice of the dispatch mechanism - dictionary lookup vs Duncan
Booth's name-mangling - is independent of the question whether the visitor
pattern applies. Duncan's setup would then become something like

class VisitorMixin:
def accept(self, visitor):
visitor.dispatch[self.__class__](self)

class Visitor:
def __init__(self):
self.dispatch = {A: self.visit_A, B: self.visit_B}
def visit_A(self, a):
pass
def visit_B(self, b):
pass

Yes, the dispatching would be not necessary if no mixin was used. Which
is not desirable, however, because it would add a dependency between the
subclass and the visitor.

thanks again, I got a lot of useful advices.

cheers, curzio
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top