Hi all !
I have written a little module called 'polymorph' that allows to call
different methods of a class that have the same name but that have not
the same number of arguments. *args are supported. You could find the
module attached to this message.
As in python, you could not define two method with same name (the second
one override the first one), i have choosen to design it through private
prefix '__' (defined method names and called method name are different).
You just have to put the number of args of your method (without the
'self' one) and use the prefix keyword 'p' (polymorph) or 'pe' (elliptic
method where last argument is '*args').
now an example:
#================================================= ======================
import polymorph
""" You could add polymorphism for your class.
You just have to inherit polymorph.Polymorph class
and prefix your methods that have the same name
but differ from the number of args with the
following prefix :
__<x>p__ where x is the number of args of your method
__<x>pe__ where x is the number of args of your method if you
use elliptic argument at the end (*args)
see below for a little demo class.
Priority : check if __<x>p__... exists before __<x>pe__... for the
same <x>.
"""
class Demo(polymorph.Polymorph):
def __init__(self):
PolymorphicClass.__init__(self)
def __1p__foo(self, first):
return (first,)
def __2p__foo(self,first,second):
return (first,second)
def __0p__bar(self):
return ()
def __1pe__bar(self,*args):
return [args]
def __2p__bar(self,first,second):
return (second,first)
aninstance = Demo()
#print "foo with no parameter : ",aninstance.foo()
print "foo with 1 parameter : ",aninstance.foo(3)
print "foo with 2 parameters : ",aninstance.foo(4,6)
#print "foo with 3 parameters : ",aninstance.foo(4,6,8)
print "bar with 0 parameter : ",aninstance.bar()
print "bar with 1 parameter : ",aninstance.bar(4)
print "bar with 2 parameters : ",aninstance.bar(9,1)
print "bar with 10 parameters : ",aninstance.bar(1,2,3,4,5,6,7,8,9,0)
#================================================= ======================
returns :
foo with 1 parameter : (3,)
foo with 2 parameters : (4, 6)
bar with 0 parameter : ()
bar with 1 parameter : [(4,)]
bar with 2 parameters : (1, 9)
bar with 10 parameters : [(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)]
I think that this could help to design classes with lesser 'switch'
behavior in all-in-one method with *args argument.
Do you find it useful ?
regards,
rashkatsa
import types,string
""" You could add polymorphism for your class.
You just have to inherit polymorph.Polymorph class
and prefix your methods that have the same name
but differ from the number of args with the
following prefix :
__<x>p__ where x is the number of args of your method
__<x>pe__ where x is the number of args of your method if you use elliptic argument at the end (*args)
for example :
def __1p__spam(self, first)
def __3pe__egg(self,first,second,*args)
Priority : check if __<x>p__... exists before __<x>pe__... for the same <x>.
author: rashkatsa
send any comments to ra*******@wanadoo.fr
"""
# metaclass
class MetaPolymorph:
def __init__(self, name, bases, namespace):
"""Create a new class."""
self.__name__ = name
self.__bases__ = bases
self.__namespace__ = namespace
def __call__(self):
"""Create a new instance."""
return PolymorphInstance(self)
# instance
class PolymorphInstance:
def __init__(self, metaclass):
self.__metaclass__ = metaclass
# simple polymorphic method
self.polymorphicMethods={}
for pmn in [x for x in self.__metaclass__.__namespace__.keys() if string.find(x,'p__') != -1 and string.rfind(x[:string.find(x,'p__')],'__')!=-1 and type(self.__metaclass__.__namespace__[x]) is types.FunctionType]:
pm = pmn[string.find(pmn,'p__')+3:]
nbarg=str(pmn[string.rfind(pmn[:string.find(pmn,'p__')],'__')+2:string.find(pmn,'p__')])
if pm in self.polymorphicMethods:
self.polymorphicMethods[pm][nbarg]=self.__metaclass__.__namespace__[pmn]
else:
self.polymorphicMethods[pm]={nbarg:self.__metaclass__.__namespace__[pmn]}
# elliptic polymorphic method
self.ePolymorphicMethods={}
for pmn in [x for x in self.__metaclass__.__namespace__.keys() if string.find(x,'pe__') != -1 and string.rfind(x[:string.find(x,'pe__')],'__')!=-1 and type(self.__metaclass__.__namespace__[x]) is types.FunctionType]:
pm = pmn[string.find(pmn,'pe__')+4:]
nbarg=str(pmn[string.rfind(pmn[:string.find(pmn,'pe__')],'__')+2:string.find(pmn,'pe__')])
if pm in self.ePolymorphicMethods:
self.ePolymorphicMethods[pm][nbarg]=self.__metaclass__.__namespace__[pmn]
else:
self.ePolymorphicMethods[pm]={nbarg:self.__metaclass__.__namespace__[pmn]}
def __getattr__(self, name):
polymorphMethodName=None
try:
value = self.__metaclass__.__namespace__[name]
except KeyError:
if name in self.polymorphicMethods.keys()+self.ePolymorphicMe thods.keys():
polymorphMethodName=name
else:
raise AttributeError, name
if not polymorphMethodName:
if type(value) is not types.FunctionType:
return value
return BoundMethod(self,value)
else:
if name in self.polymorphicMethods.keys():
pMethods=self.polymorphicMethods[name]
else:
pMethods=None
if name in self.ePolymorphicMethods.keys():
ellipticPMethods=self.ePolymorphicMethods[name]
else:
ellipticPMethods=None
return BoundPolymorphMethods(self,polymorphMethodName,pMe thods,ellipticPMethods)
# bound function with instance == BoundMethod
class BoundMethod:
def __init__(self, instance, function):
self.instance = instance
self.function = function
def __call__(self, *args):
return apply(self.function, (self.instance,) + args)
# bound polymorph functions with instance == BoundPolymorphMethods
class BoundPolymorphMethods:
def __init__(self, instance, functionName, functions, ellipticFunctions):
self.instance = instance
self.functionName=functionName
self.functions = functions
self.ellipticFunctions=ellipticFunctions
def __call__(self, *args):
self.func=None
argl=len(args)
try:
self.func = self.functions[str(argl)]
except KeyError:
# try to find an elliptic function
if self.ellipticFunctions:
# sort the list to find the min-max elliptic function that could be called
sortedKeys=[int(x) for x in self.ellipticFunctions.keys()]
sortedKeys.sort()
sortedKeys.reverse()
keyFound=None
# (x-1) because *args could be empty : ()
for x in sortedKeys:
if argl >= (x-1):
keyFound=str(x)
break
if keyFound:
self.func=self.ellipticFunctions[keyFound]
if not self.func:
if argl < 1:
errorMsg='Error: Unknown polymorphic method '+self.functionName+' with no argument'
elif argl == 1:
errorMsg='Error: Unknown polymorphic method '+self.functionName+' with 1 argument'
else:
errorMsg='Error: Unknown polymorphic method '+self.functionName+' with '+str(len(args))+' arguments'
raise errorMsg
return apply(self.func, (self.instance,) + args)
# create Polymorph class with the metaclass defined above
Polymorph = MetaPolymorph('Polymorph', (), {})