polymorphism in python

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • rashkatsa

    polymorphism in python

    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.Polym orph 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):
    PolymorphicClas s.__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.fo o()
    print "foo with 1 parameter : ",aninstance.fo o(3)
    print "foo with 2 parameters : ",aninstance.fo o(4,6)
    #print "foo with 3 parameters : ",aninstance.fo o(4,6,8)
    print "bar with 0 parameter : ",aninstance.ba r()
    print "bar with 1 parameter : ",aninstance.ba r(4)
    print "bar with 2 parameters : ",aninstance.ba r(9,1)
    print "bar with 10 parameters : ",aninstance.ba r(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.Polym orph 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 rashkatsa@wanad oo.fr
    """

    # metaclass
    class MetaPolymorph:
    def __init__(self, name, bases, namespace):
    """Create a new class."""
    self.__name__ = name
    self.__bases__ = bases
    self.__namespac e__ = namespace
    def __call__(self):
    """Create a new instance."""
    return PolymorphInstan ce(self)

    # instance
    class PolymorphInstan ce:
    def __init__(self, metaclass):
    self.__metaclas s__ = metaclass
    # simple polymorphic method
    self.polymorphi cMethods={}
    for pmn in [x for x in self.__metaclas s__.__namespace __.keys() if string.find(x,' p__') != -1 and string.rfind(x[:string.find(x, 'p__')],'__')!=-1 and type(self.__met aclass__.__name space__[x]) is types.FunctionT ype]:
    pm = pmn[string.find(pmn ,'p__')+3:]
    nbarg=str(pmn[string.rfind(pm n[:string.find(pm n,'p__')],'__')+2:string .find(pmn,'p__' )])
    if pm in self.polymorphi cMethods:
    self.polymorphi cMethods[pm][nbarg]=self.__metacla ss__.__namespac e__[pmn]
    else:
    self.polymorphi cMethods[pm]={nbarg:self.__ metaclass__.__n amespace__[pmn]}
    # elliptic polymorphic method
    self.ePolymorph icMethods={}
    for pmn in [x for x in self.__metaclas s__.__namespace __.keys() if string.find(x,' pe__') != -1 and string.rfind(x[:string.find(x, 'pe__')],'__')!=-1 and type(self.__met aclass__.__name space__[x]) is types.FunctionT ype]:
    pm = pmn[string.find(pmn ,'pe__')+4:]
    nbarg=str(pmn[string.rfind(pm n[:string.find(pm n,'pe__')],'__')+2:string .find(pmn,'pe__ ')])
    if pm in self.ePolymorph icMethods:
    self.ePolymorph icMethods[pm][nbarg]=self.__metacla ss__.__namespac e__[pmn]
    else:
    self.ePolymorph icMethods[pm]={nbarg:self.__ metaclass__.__n amespace__[pmn]}
    def __getattr__(sel f, name):
    polymorphMethod Name=None
    try:
    value = self.__metaclas s__.__namespace __[name]
    except KeyError:
    if name in self.polymorphi cMethods.keys() +self.ePolymorp hicMethods.keys ():
    polymorphMethod Name=name
    else:
    raise AttributeError, name
    if not polymorphMethod Name:
    if type(value) is not types.FunctionT ype:
    return value
    return BoundMethod(sel f,value)
    else:
    if name in self.polymorphi cMethods.keys() :
    pMethods=self.p olymorphicMetho ds[name]
    else:
    pMethods=None
    if name in self.ePolymorph icMethods.keys( ):
    ellipticPMethod s=self.ePolymor phicMethods[name]
    else:
    ellipticPMethod s=None
    return BoundPolymorphM ethods(self,pol ymorphMethodNam e,pMethods,elli pticPMethods)

    # 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.func tion, (self.instance, ) + args)

    # bound polymorph functions with instance == BoundPolymorphM ethods
    class BoundPolymorphM ethods:
    def __init__(self, instance, functionName, functions, ellipticFunctio ns):
    self.instance = instance
    self.functionNa me=functionName
    self.functions = functions
    self.ellipticFu nctions=ellipti cFunctions
    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.ellipticFu nctions:
    # sort the list to find the min-max elliptic function that could be called
    sortedKeys=[int(x) for x in self.ellipticFu nctions.keys()]
    sortedKeys.sort ()
    sortedKeys.reve rse()
    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. ellipticFunctio ns[keyFound]
    if not self.func:
    if argl < 1:
    errorMsg='Error : Unknown polymorphic method '+self.function Name+' with no argument'
    elif argl == 1:
    errorMsg='Error : Unknown polymorphic method '+self.function Name+' with 1 argument'
    else:
    errorMsg='Error : Unknown polymorphic method '+self.function Name+' 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', (), {})

  • martin z

    #2
    Re: polymorphism in python

    A very interesting invention... does it work with parameter defaults? Oh,
    and IIRC, the word you're looking for is "Function overloading" not
    "polymorphi sm". Polymorphism refers to the ability to have completely
    different objects that can be used the same way, which Python always has.


    Comment

    • Jay O'Connor

      #3
      Re: polymorphism in python

      martin z wrote:
      [color=blue]
      >A very interesting invention... does it work with parameter defaults? Oh,
      >and IIRC, the word you're looking for is "Function overloading" not
      >"polymorphism" . Polymorphism refers to the ability to have completely
      >different objects that can be used the same way, which Python always has.
      >
      >
      >
      >[/color]

      "Function overloading" is still one form of polymorphism. Polymorphism
      as a concept can be approached in different ways, including function
      overloading, generic programming and inheritance. Languages like
      Python are Smalltalk are very good at implementing polymorphism based
      on sending a message to an object and as long as the object understands
      the message, it doesn't matter what the object is (part of their dynamic
      binding); but they are not as good at overloading based on argument
      types (how do you have two methods that have the same name but one takes
      a single integer argument and one takes a single string argument?
      answer, you have one function with an 'if' decision, or you have two
      functions with two different names, breaking the polymorphism, or for
      more complex objects you get doule-dispatch involved) . Languages like
      C++ and Java restrict polymorphism of objects to those within the
      hierarchy, but do allow for polymophism by function overloading; C++ and
      Ada also have polymopshim through generic programming (generics in Ada,
      templates in C++)

      Comment

      • anton muhin

        #4
        Re: polymorphism in python

        Jay O'Connor wrote:
        [color=blue]
        > martin z wrote:
        > "Function overloading" is still one form of polymorphism.[/color]
        I'm not completly agree. Indeed, dispatching on arguments' types can be
        regarded as polymorphism. However, OP needed to dispatch on *number* of
        parameters that seems like classic overloading IMHO

        regards,
        anton.

        Comment

        • Jay O'Connor

          #5
          Re: polymorphism in python

          anton muhin wrote:
          [color=blue]
          > Jay O'Connor wrote:
          >[color=green]
          >> martin z wrote:
          >> "Function overloading" is still one form of polymorphism.[/color]
          >
          > I'm not completly agree. Indeed, dispatching on arguments' types can
          > be regarded as polymorphism. However, OP needed to dispatch on
          > *number* of parameters that seems like classic overloading IMHO[/color]


          I didn't really address the OP phrasing because I'm not really convinced
          that overloading on numbers of parameters is really polymophism either,
          I was mostly just addressing the follow up; just pointing out that
          function overloading, in general, is one way of implementing polymorphism.

          Comment

          • Roy Smith

            #6
            Re: polymorphism in python

            Jay O'Connor <joconnor@cyber mesa.com> wrote:[color=blue]
            > Languages like Python [and] Smalltalk [...] are not as good at
            > overloading based on argument types (how do you have two methods
            > that have the same name but one takes a single integer argument
            > and one takes a single string argument?[/color]

            But how often is it actually useful to do such a thing? I can't
            remember the last time I wanted to do something like that in Python.
            Did lots of it in C++, but that's more working around the language than
            anything else. Can you think of a good example of why you would want to
            do something like that in Python?

            If your intent is that you could call foo(4) or foo('4'), you could just
            write:

            def foo (x):
            theRealX = int (x)

            or you could maybe even do:

            def foo (x):
            if type(x) == StringType:
            do string stuff
            else:
            do integer stuff

            but if you're trying to do either of the above, I suspect you're trying
            to write C++ in Python (which is as bad, if not worse, as trying to
            write Fortran in Python).

            Comment

            • KefX

              #7
              Re: polymorphism in python

              >f your intent is that you could call foo(4) or foo('4'), you could just[color=blue]
              >write:
              >
              >def foo (x):
              > theRealX = int (x)
              >
              >or you could maybe even do:
              >
              >def foo (x):
              > if type(x) == StringType:
              > do string stuff
              > else:
              > do integer stuff
              >
              >but if you're trying to do either of the above, I suspect you're trying
              >to write C++ in Python (which is as bad, if not worse, as trying to
              >write Fortran in Python).[/color]

              The second example should use the expression:
              isinstance(x, str)

              and not this:
              type(x) == StringType

              But as you said, it's best to avoid doing it at all ;)

              - Kef

              Comment

              • Jay O'Connor

                #8
                Re: polymorphism in python

                Roy Smith wrote:
                [color=blue]
                >Jay O'Connor <joconnor@cyber mesa.com> wrote:
                >
                >[color=green]
                >>Languages like Python [and] Smalltalk [...] are not as good at
                >>overloading based on argument types (how do you have two methods
                >>that have the same name but one takes a single integer argument
                >>and one takes a single string argument?
                >>
                >>[/color]
                >
                >But how often is it actually useful to do such a thing? I can't
                >remember the last time I wanted to do something like that in Python.
                >
                >[/color]

                I occasionally run into it when I want to display something where you
                can polymorphically ask to display something but how it's displayed
                depends on whether it's a number or not. However, it's a failry
                contrived example as you can do operator overloading on more than just
                strings and integers. Even on objects within a hiearchy. I'm not really
                a big fan off it, just that it can be done; and that it's legitamite
                polymorphism.
                [color=blue]
                >or you could maybe even do:
                >
                >def foo (x):
                > if type(x) == StringType:
                > do string stuff
                > else:
                > do integer stuff
                >
                >[/color]

                Generally, if you are programming to the interface and not to the class
                (which is what you are usually doing in OO programming, or should be if
                your language allows it) than testing for the actual type of an object
                is usually a no-no

                Comment

                Working...