6.7 Polymorphism and Duck Typing in Python
Polymorphism is a key principle in Object-Oriented Programming (OOP) that allows objects of different classes to be treated as objects of a common superclass. Polymorphism enables different classes to provide a unique implementation of methods that share the same name. In Python, Duck Typing is a specific form of polymorphism, where the type of an object is determined by its behavior (methods and properties) rather than its explicit class.
In this section, we will explore what polymorphism is, how it works in Python, and how Duck Typing plays an essential role in Python’s dynamic nature.
6.7.1 What is Polymorphism?
Polymorphism allows objects of different types to be treated uniformly based on a shared interface or method signature. It refers to the ability of different objects to respond to the same method call in a way that is appropriate for their specific class. This makes code more flexible and reusable, as you can write functions or methods that can work with different types of objects.
Key Points:
- Polymorphism allows different objects to provide different behaviors for the same method.
- It enables writing generic code that can work with objects from different classes as long as they share common methods or attributes.
6.7.2 Method Polymorphism in Python
In Python, polymorphism is typically achieved by defining methods with the same name in different classes. The exact behavior depends on the class of the object that is calling the method. Python’s dynamic typing system makes it easy to implement polymorphism without requiring explicit interfaces or type declarations.
Example:
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class Cow:
def speak(self):
return "Moo!"
# Polymorphic behavior
animals = [Dog(), Cat(), Cow()]
for animal in animals:
print(animal.speak())
Output:
Woof!
Meow!
Moo!
In this example:
- Each class (
Dog
,Cat
,Cow
) defines a methodspeak()
that behaves differently depending on the type of the object. - The
speak()
method is called polymorphically on each object in theanimals
list, resulting in the correct output for each type of animal.
6.7.3 Polymorphism with Inheritance
Polymorphism also works naturally with inheritance. A parent class can define a method that is overridden by child classes, and when the method is called on a child class object, the child class's version is executed. This allows for more flexible and reusable code.
Example:
class Animal:
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Polymorphic behavior with inheritance
def make_animal_speak(animal):
print(animal.speak())
# Using polymorphism
dog = Dog()
cat = Cat()
make_animal_speak(dog) # Output: Woof!
make_animal_speak(cat) # Output: Meow!
In this example:
- The
Animal
class defines an abstract methodspeak()
usingraise NotImplementedError
, which forces subclasses to implement the method. - Both
Dog
andCat
override thespeak()
method, providing specific behavior. - The function
make_animal_speak()
demonstrates polymorphism by accepting any object that implements thespeak()
method, whether it's aDog
or aCat
.
6.7.4 Duck Typing in Python
Duck Typing is a concept in Python where the type or class of an object is determined by its behavior (i.e., the methods and attributes it supports), not by its explicit inheritance or class declaration. The term comes from the phrase:
"If it looks like a duck and quacks like a duck, it must be a duck."
In Duck Typing, if an object implements the required methods or attributes, it is treated as an instance of that class, regardless of its actual type.
Example of Duck Typing:
class Duck:
def quack(self):
return "Quack!"
class Person:
def quack(self):
return "Person imitating a duck: Quack!"
# Polymorphic function using Duck Typing
def make_it_quack(duck_like_object):
print(duck_like_object.quack())
# Both Duck and Person can "quack"
duck = Duck()
person = Person()
make_it_quack(duck) # Output: Quack!
make_it_quack(person) # Output: Person imitating a duck: Quack!
In this example:
- Both
Duck
andPerson
implement thequack()
method, even though they are not related by inheritance. - The
make_it_quack()
function accepts any object that implements aquack()
method, demonstrating Duck Typing. - Duck Typing allows Python to focus on what an object can do (its behavior) rather than what it is (its class).
6.7.5 Polymorphism in Functions
Python functions can exhibit polymorphism by accepting different types of objects as parameters and calling methods that are implemented differently across the various classes.
Example:
class Rectangle:
def area(self, length, width):
return length * width
class Circle:
def area(self, radius):
return 3.14159 * radius ** 2
# Polymorphic function
def print_area(shape, *args):
print(f"The area is: {shape.area(*args)}")
# Using polymorphism in a function
rect = Rectangle()
circle = Circle()
print_area(rect, 10, 5) # Output: The area is: 50
print_area(circle, 7) # Output: The area is: 153.93791
In this example:
- Both
Rectangle
andCircle
classes define anarea()
method, but they calculate the area in different ways based on their shapes. - The
print_area()
function exhibits polymorphism by calling thearea()
method on objects of different classes without needing to know the exact type of the object.
6.7.6 Polymorphism with Abstract Base Classes
Python provides Abstract Base Classes (ABC) in the abc
module, which is a way to define a common interface for a group of related classes. Using ABCs ensures that subclasses implement certain methods, thus enforcing polymorphism more strictly.
Example:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Polymorphic behavior using abstract base classes
animals = [Dog(), Cat()]
for animal in animals:
print(animal.speak())
In this example:
- The
Animal
class is defined as an abstract base class using theABC
module. - The
speak()
method is marked as an abstract method with the@abstractmethod
decorator, meaning any subclass ofAnimal
must implement thespeak()
method. - This ensures that all subclasses (
Dog
,Cat
) conform to the same interface, making polymorphism more structured.
6.7.7 Benefits of Polymorphism and Duck Typing
- Flexibility: Polymorphism and Duck Typing allow you to write code that can work with objects of different types without knowing their specific class.
- Code Reusability: You can write generic functions and methods that operate on different objects, leading to reusable code.
- Extensibility: You can extend existing code by adding new classes that implement the same methods, without modifying the existing codebase.
- Simplicity: Duck Typing in Python allows you to focus on an object’s behavior rather than its type, making your code cleaner and more Pythonic.
6.7.8 Summary
- Polymorphism is the ability to use a common interface for objects of different types, allowing for flexible and reusable code.
- Method overriding allows subclasses to provide specific implementations of methods defined in a parent class, enabling polymorphism through inheritance.
- Duck Typing is a form of polymorphism where the type of an object is determined by its behavior rather than its explicit class. In Python, if an object implements the required methods, it can be used in place of any other object with the same methods.
- Abstract Base Classes (ABC) can enforce polymorphism by requiring subclasses to implement certain methods, ensuring that all subclasses conform to the same interface.
Polymorphism and Duck Typing are powerful tools that allow you to write flexible, extensible, and maintainable code in Python. These concepts enable you to build systems where different objects can interact seamlessly, enhancing code reusability and simplicity.