As you have found, there are many different definitions of polymorphism. This answer will address it from a practical perspective and may not align with academic definitions.
Sub-type polymorphism (via. inheritance)
This is the kind of polymorphism I believe most people think of with regard to the term polymorphism. This is where methods are inherited by sub-classes and then can be overridden. For example (Python 2.7):
class Foo(object):
def test(self):
print self.name()
def name(self):
return "Foo"
class Bar(Foo):
def name(self):
return "Bar"
foo = Foo()
bar = Bar()
print type(foo)
print type(bar)
print isinstance(bar, type(foo))
Foo().test()
Bar().test()
Output:
<class '__main__.Foo'>
<class '__main__.Bar'>
True
Foo
Bar
This works just sub-type polymorphism would in Java or C#.
Duck Typing
Python also has a feature called duck-typing. Technically this is also sub-type inheritance if you distinguish sub-types and inheritance (which I think is the right way to think about Python) The term comes from the idiomatic saying: "if it looks like a duck and quacks like a duck, it's probably a duck" which is often shortened to "if it quacks like a duck..." I would argue that whether or not this is polymorphism in the academic literature doesn't really matter because it can be used to solve the same problems:
class Foo(object):
def test(self):
print "Foo"
class Bar(object):
def test(self):
print "Bar"
foo = Foo()
bar = Bar()
print type(foo)
print type(bar)
print isinstance(bar, type(foo))
Foo().test()
Bar().test()
Output:
<class '__main__.Foo'>
<class '__main__.Bar'>
False
Foo
Bar
As you see we can get the same kind of behavior as before but now Bar is not a Foo. Duck-typing is very commonly used in Python programs where sub-typing is not. For example, it's common to create list-like objects where all (really: most) of the operations of a list
are implemented and it is passed into methods as if it is as list
even though from a pure typing perspective, it is not actually a type derived from list.
Parametric Polymorphism
I take this to mean stuff like generics in Java or C#. I don't think this is relevant to Python but I'm not up to speed on Python 3.
Method overloading
This an interesting case. From a pure Python perspective, this isn't meaningful because you can't overload methods. That is, you can't define the same method name twice at all. What's interesting is that in Jython (python that compiles to run on the JVM) you get a feature that neither Java nor Python have: double-dispatch. When determining what version of an overloaded Java method to call from a python statement, the runtime will look at the actual types of the parameters of the call and select the version dynamically. In Java overloaded methods are bound to calls at compile time instead which is technically polymorphic but in a really uninteresting way. Double-dispatch overloading can actually be used to implement some interesting effects. I expect that this would work similarly in IronPython and calls out to C# libraries.
I think I've covered most of the bases here. Comments are welcome for additional features that might be considered polymorphic. One thing that I'm not sure about is the ability to create synthetic or virtual properties/methods using __getattr__
or __setattr__
et al. Also, I think you could make the argument that passing method references around is type of polymorphism but maybe that's stretching things a bit too far.