Is there a way to get ad-hoc polymorphism in Python?

1.9k Views Asked by At

Many languages support ad-hoc polymorphism (a.k.a. function overloading) out of the box. However, it seems that Python opted out of it. Still, I can imagine there might be a trick or a library that is able to pull it off in Python. Does anyone know of such a tool?

For example, in Haskell one might use this to generate test data for different types:

-- In some testing library:
class Randomizable a where
   genRandom :: a

-- Overload for different types
instance Randomizable String where genRandom = ...
instance Randomizable Int    where genRandom = ...
instance Randomizable Bool   where genRandom = ...


-- In some client project, we might have a custom type:
instance Randomizable VeryCustomType where genRandom = ...

The beauty of this is that I can extend genRandom for my own custom types without touching the testing library.

How would you achieve something like this in Python?

4

There are 4 best solutions below

6
On BEST ANSWER

A Python function cannot be automatically specialised based on static compile-time typing. Therefore its result can only depend on its arguments received at run-time and on the global (or local) environment, unless the function itself is modifiable in-place and can carry some state.

Your generic function genRandom takes no arguments besides the typing information. Thus in Python it should at least receive the type as an argument. Since built-in classes cannot be modified, the generic function (instance) implementation for such classes should be somehow supplied through the global environment or included into the function itself.

I've found out that since Python 3.4, there is @functools.singledispatch decorator. However, it works only for functions which receive a type instance (object) as the first argument, so it is not clear how it could be applied in your example. I am also a bit confused by its rationale:

In addition, it is currently a common anti-pattern for Python code to inspect the types of received arguments, in order to decide what to do with the objects.

I understand that anti-pattern is a jargon term for a pattern which is considered undesirable (and does not at all mean the absence of a pattern). The rationale thus claims that inspecting types of arguments is undesirable, and this claim is used to justify introducing a tool that will simplify ... dispatching on the type of an argument. (Incidentally, note that according to PEP 20, "Explicit is better than implicit.")

The "Alternative approaches" section of PEP 443 "Single-dispatch generic functions" however seems worth reading. There are several references to possible solutions, including one to "Five-minute Multimethods in Python" article by Guido van Rossum from 2005.

4
On

Does this count for ad hock polymorphism?

class A:
    def __init__(self):
        pass

    def aFunc(self):
        print "In A"

class B:
    def __init__(self):
        pass

    def aFunc(self):
        print "In B"

f = A()
f.aFunc()
f = B()
f.aFunc()

output

In A
In B
1
On

Python is not a strongly typed language, so it really doesn't matter if yo have an instance of Randomizable or an instance of some other class which has the same methods.

One way to get the appearance of what you want could be this:

types_ = {}
def registerType ( dtype , cls ) :
    types_[dtype] = cls
def RandomizableT ( dtype ) :
    return types_[dtype]

Firstly, yes, I did define a function with a capital letter, but it's meant to act more like a class. For example:

registerType ( int , TheLibrary.Randomizable )
registerType ( str , MyLibrary.MyStringRandomizable )

Then, later:

type = ... # get whatever type you want to randomize
randomizer = RandomizableT(type) ()
print randomizer.getRandom()
0
On

Another version of polymorphism

from module import aName

If two modules use the same interface, you could import either one and use it in your code. One example of this is from xml.etree.ElementTree import XMLParser