Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Function Overloading and Python

146 views
Skip to first unread message

Allen Peloquin

unread,
Feb 25, 2008, 2:33:59 AM2/25/08
to
I have a personal project that has an elegant solution that requires
both true multiple inheritance of classes (which pretty much limits my
language choices to C++ and Python) and type-based function
overloading.

Now, while this makes it sound like I have to resign myself to C++,
which I am not a fan of writing, I have resisted thus far. Who wants
to manage their own memory? No, I have fallen in love with Python, and
so come asking for help.

I have a very large multiple inheritance tree that will be frequently
extended as my project goes on. Let us call these "type A" classes.

I also have a moderately sized single inheritance tree whose task is
to match sets of "type A" parameters in a function. Let us call these
"type B" classes. "Type Bs" have one function, which is extended with
each new child, and overloaded to match varying combinations of "Type
As." The reason for this is code-reuse and eventual matching with
common "type A" parents.

Now, what are my options in Python to dispatch unique code based on
the permutations of "type A" classes without code repetition?
Such as, where in C++...

class B
{
fun(A x, A y, A z)...
fun(A1 x, A y, A z)...
}

class B1
{
fun(A1 x, A y, A z)...
}

Such that any previous behavior is inherited, but behaves
polymorphically because of the single function name.

B1.fun(A(x), A(y), A(z)) == B.fun(A(x), A(y), A(z))
but
B1.fun(A1(x), A(y), A(z) != B.fun(A1(x), A(y), A(z))

Is there a data-structure solution or third party module that would
mimic this behavior?

PEP 3124 got my hopes up, but I was let down when it was deferred.

Thank you for your time.

Stefan Behnel

unread,
Feb 25, 2008, 2:44:36 AM2/25/08
to Allen Peloquin
Allen Peloquin wrote:
> class B
> {
> fun(A x, A y, A z)...
> fun(A1 x, A y, A z)...
> }
>
> class B1
> {
> fun(A1 x, A y, A z)...
> }
>
> Such that any previous behavior is inherited, but behaves
> polymorphically because of the single function name.

Try something like this:

class B(object):
def fun(x,y,z):
if isinstance(x, A1):
return self._fun(x,y,z)
# ...

def _fun(x,y,z):
# ...

class B1(B):
def _fun(x,y,z):
# ...


Stefan

Stefan Behnel

unread,
Feb 25, 2008, 2:46:13 AM2/25/08
to Allen Peloquin

Oh, well, make that:

class B(object):
def fun(self, x,y,z):


if isinstance(x, A1):
return self._fun(x,y,z)
# ...

def _fun(self, x,y,z):
# ...

class B1(B):
def _fun(self, x,y,z):
# ...


(but you already knew that anyway, didn't you?)

Stefan

Allen Peloquin

unread,
Feb 25, 2008, 2:48:28 AM2/25/08
to

The problem is that I want to reuse the code of the parent classes
through inheritance, otherwise this would work fine.

I am aware that because of the dynamic typing of Python, there
currently is no type-based function overloading, so I'm looking for
alternate solutions to my design problem.

Paul Rudin

unread,
Feb 25, 2008, 2:54:01 AM2/25/08
to
Allen Peloquin <tando...@gmail.com> writes:

> I have a personal project that has an elegant solution that requires
> both true multiple inheritance of classes (which pretty much limits my
> language choices to C++ and Python) and type-based function
> overloading.
>

Common Lisp :/

Stefan Behnel

unread,
Feb 25, 2008, 3:04:28 AM2/25/08
to Allen Peloquin
Allen Peloquin wrote:
> On Feb 24, 11:44 pm, Stefan Behnel <stefan...@behnel.de> wrote:
>> Allen Peloquin wrote:
>>> class B
>>> {
>>> fun(A x, A y, A z)...
>>> fun(A1 x, A y, A z)...
>>> }
>>> class B1
>>> {
>>> fun(A1 x, A y, A z)...
>>> }
>>> Such that any previous behavior is inherited, but behaves
>>> polymorphically because of the single function name.
>> Try something like this:
>>
>> class B(object):
>> def fun(x,y,z):
>> if isinstance(x, A1):
>> return self._fun(x,y,z)
>> # ...
>>
>> def _fun(x,y,z):
>> # ...
>>
>> class B1(B):
>> def _fun(x,y,z):
>> # ...
>>
>> Stefan
>
> The problem is that I want to reuse the code of the parent classes
> through inheritance, otherwise this would work fine.

Ok, I didn't see you were going to add new subtypes, that makes it more tricky
to dispatch in the superclass.

An alternative would be a more generic dispatcher pattern then:

class B(object):
_func_implementations = {}
_dispatch = _func_implementations.get

def func(self, x,y,z):
self._dispatch(type(x), self._func)(self,x,y,z)

def _func(self, x,y,z):
# ...

class B1(B):
def _func(self, x,y,z):
# ...

B._func_implementations[B1] = B1._func

Or, you could dispatch based on type /names/:

class B(object):
def func(self, x,y,z):
func = getattr(self, "_%s_func" % type(x).__name__, self._func)
func(x,y,z)

def _A_func(self, x,y,z):
# ...

class B1(B):
def _A1_func(self, x,y,z):
# ...

Stefan

Bruno Desthuilliers

unread,
Feb 25, 2008, 4:00:49 AM2/25/08
to
Allen Peloquin a écrit :

> I have a personal project that has an elegant solution that requires
> both true multiple inheritance of classes (which pretty much limits my
> language choices to C++ and Python) and type-based function
> overloading.
>
> Now, while this makes it sound like I have to resign myself to C++,
> which I am not a fan of writing, I have resisted thus far. Who wants
> to manage their own memory? No, I have fallen in love with Python, and
> so come asking for help.

(snip)

> Now, what are my options in Python to dispatch unique code based on
> the permutations of "type A" classes without code repetition?

There are at least a couple type- or rule- based dispatch packages or
receipes around. Googling for 'generic' or 'ruledispatch' etc... might
give you a start.

Gerald Klix

unread,
Feb 25, 2008, 3:50:45 AM2/25/08
to pytho...@python.org
Stefan Behnel schrieb:

The BDFL came across that problem, too. You will find his thoughts here:
<http://www.artima.com/weblogs/viewpost.jsp?thread=101605>
The reference implementation for his solution is here:
<http://svn.python.org/view/sandbox/trunk/overload/>

HTH,
Gerald

Carl Banks

unread,
Feb 25, 2008, 7:48:41 AM2/25/08
to

At risk of sounding snarky, I'll first advise:

"Use different names for different expected types."

Presumably you know what types you're passing in when you write the
calling function, so you'd also know which variant of the function
you'd be calling. Might as well just use separate names then.


But you don't want to do that, and you might have good reasons. So,
secondly I will advise:

"Use different names inside the class, and have one function in the
base class dispatch as necessary."

class B(object):
def fun(x,y,z):
if type(x) == "A":
return _fun_A(x,y,z)
if type(x) == "A1":
return _fun_A1(x,y,z)

def _fun_A(x,y,z):
# you know x is of type A

def _fun_A1(x,y,z):
# you know x is of type A1

class B1(B):
def _fun_A(x,y,z):
# overrides B._fun_A


In other words, subclasses only override _fun_type variants. fun is
left alone. Extend idea as needed for your needs.

Carl Banks

casti...@gmail.com

unread,
Feb 25, 2008, 10:27:07 AM2/25/08
to

Does the decorator solution work in this case?

class B:
@multimethod( A, A, A )
def fun( x, y, z )
@multimethod( A1, A, A )
def fun( x, y, z )

class B1:
@multimethod( A1, A, A )
def fun( x, y, z )

b= B1()
b.fun( A(), A(), A() ) -> B.fun#.1
b.fun( A1(), A(), A() ) -> B1.fun#.2

casti...@gmail.com

unread,
Feb 25, 2008, 12:04:31 PM2/25/08
to
> B1.fun(A(x), A(y), A(z)) == B.fun(A(x), A(y), A(z))
> but
> B1.fun(A1(x), A(y), A(z) != B.fun(A1(x), A(y), A(z))
>
> Is there a data-structure solution or third party module that would
> mimic this behavior?

'''
An Overloaded instance, B.xfun, is created in the base class of the
classes the members of which you want to override. Define the
function you want to override, merely to call 'invoke' on it.
xfun.make( typeA, typeB ) is a wrapper which will bind the type-
function pair to it as well. Define any number of these. When the
class is complete, call B.xfun.push with the class just defined, to
let xfun know that the methods just bound were defined in it. Perhaps
future Pythons will allow a class currently being executed to be
referenced. Class decorators will be able to permit this as well:
@xfun.push / class B1( B ). (Anyone know why I got this: "TypeError:
cannot create 'NoneType' instances"?)

Upon calling B().fun( A(), A(), A() ), B.fun, the root of all funs,
calls dispatch on the instance. In theory it could be called directly
with the right binding. Dispatch then checks the types of the
arguments, and tries successively higher subclass types on the class
instance, until it finds a combination of types that was defined. If
none is found, raise a TypeError. If you want a behavior to occur by
default, without a matching type sequence, let me know; or do it
yourself, either with a try: except: combination in B.fun, the
dispatcher for the bound method, or by testing for a match in it, upon
succeeding, call that, and upon failing, do the default.
'''

class A: pass
class A1: pass

class Overloaded:
def __init__( self ):
self._tps= {}
self._pend= {}
def make( self, *tps ):
def _pre( func ):
self._pend[ tps ]= func
return func
return _pre
def dispatch( self, objself, *a ):
tps= tuple( type( i ) for i in a )
for c in objself.__class__.__mro__:
if ( c, )+ tps in self._tps:
match= self._tps[ ( c, )+ tps ]
return match( objself, *a )
raise TypeError( 'No matching type'
'signature for %s %s types %s %s'%
( objself, a, type( objself ), tps ) )
def push( self, tp ):
for k, v in self._pend.items():
self._tps[ ( tp, )+ k ]= v
self._pend.clear()

class B:
xfun= Overloaded()
def fun( self, *a ):
return self.xfun.dispatch( self, *a )
@xfun.make( A, A, A )
def q( self, x, y, z ):
return 'B AAA'
@xfun.make( A1, A, A )
def q( self, x, y, z ):
return 'B A1AA'
B.xfun.push( B )

class B1(B):
@B.xfun.make( A1, A, A )
def q( self, x, y, z ):
return 'B1 A1AA'
B.xfun.push( B1 )

class B2(B1):
@B.xfun.make( A, A, A )
def q( self, x, y, z ):
return 'B2 AAA'
B.xfun.push( B2 )

class B3(B1):
@B.xfun.make( A1, A, A )
def q( self, x, y, z ):
return 'B3 A1AA'
B.xfun.push( B3 )

b= B()
assert b.fun( A(), A(), A() )== 'B AAA'
assert b.fun( A1(), A(), A() )== 'B A1AA'
b1= B1()
assert b1.fun( A1(), A(), A() )== 'B1 A1AA'
assert b1.fun( A(), A(), A() )== 'B AAA'
b2= B2()
assert b2.fun( A1(), A(), A() )== 'B1 A1AA'
assert b2.fun( A(), A(), A() )== 'B2 AAA'
b3= B3()
assert b3.fun( A1(), A(), A() )== 'B3 A1AA'
assert b3.fun( A(), A(), A() )== 'B AAA'
try:
assert b3.fun( A(), A1(), A() )== 'B AAA'
assert False, 'Type matched incorrectly'
except TypeError:
pass
print( 'Pass.' )

'''
This is a somewhat advanced technique.
If you're not using Python 3.0, let me know, and I'll add inheritance
arguments to Overloaded.push, to replace .__mro__ in
Overloaded.dispatch.
'''

casti...@gmail.com

unread,
Mar 2, 2008, 5:43:40 PM3/2/08
to
On Feb 25, 11:04 am, castiro...@gmail.com wrote:
> > B1.fun(A(x), A(y), A(z)) == B.fun(A(x), A(y), A(z))
> > but
> > B1.fun(A1(x), A(y), A(z) != B.fun(A1(x), A(y), A(z))
>
> > Is there a data-structure solution or third party module that would
> > mimic this behavior?
>
> class B:
>    xfun= Overloaded()
>    def fun( self, *a ):
>       return self.xfun.dispatch( self, *a )
>    @xfun.make( A, A, A )
>    def q( self, x, y, z ):
>       return 'B AAA'
>    @xfun.make( A1, A, A )
>    def q( self, x, y, z ):
>       return 'B A1AA'
> B.xfun.push( B )

You could also call xfun.methods( self ) in B.__init__.
Overloaded.methods binds the methods specified with xfun.make to the B
instance. In this case, the effect is,

self.fun= types.MethodType( self.__class__.[x?]fun, self ) -- I forget
by now.

But time is money, and money doesn't grow on trees-- so catch me later
with your own.

(A decorator could also do it too-- and just in the base class!)

0 new messages