Image by Author | DALLE-3 & Canva
What is Duck Typing?
Duck typing is a concept in programming often related to dynamic languages like Python, that emphasizes more on the object’s behavior over its type or class. When you use duck typing, you check whether an object has certain methods or attributes, rather than checking for the exact class. The name comes from the saying,
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
Duck typing brings several advantages to programming in Python. It allows for more flexible and reusable code and supports polymorphism, enabling different object types to be used interchangeably as long as they provide the required interface. This results in simpler and more concise code. However, duck typing also has its disadvantages. One major drawback is the potential for runtime errors. Additionally, it can make your code challenging to understand.
Understanding Dynamic Behavior in Python
In dynamically typed languages, variable types are not fixed. Instead, they are determined at runtime based on the assigned values. In contrast, statically typed languages check variable types at compile time. For instance, if you attempt to reassign a variable to a value of a different type in static typing, you will encounter an error. Dynamic typing provides greater flexibility in how variables and objects are used.
Let’s consider the *
Python operator; it behaves differently based on the type of the object it is used with. When used between two integers, it performs multiplication.
# Multiplying two integers
a = 5 * 3
print(a) # Outputs: 15
When used with a string and an integer, it repeats the string. This demonstrates Python’s dynamic typing system and adaptable nature.
# Repeating a string
a="A" * 3
print(a) # Outputs: AAA
How Duck Typing Works in Python?
Duck typing is preferred in dynamic languages because it encourages a more natural coding style. Developers can focus on designing interfaces based on what objects can do. In duck typing, methods defined inside the class are given more importance than the object itself. Let’s clarify this with a basic example.
Example No: 01
We have two classes: Duck and Person. Ducks can make a quack sound, while people can speak. Each class has a method called sound that prints their respective sounds. The function make_it_sound
takes any object that has a sound method and calls it.
class Duck:
def sound(self):
print("Quack!")
class Person:
def sound(self):
print("I'm quacking like a duck!")
def make_it_sound(obj):
obj.sound()
Now, let’s see how we can use duck typing to work for this example.
# Using the Duck class
d = Duck()
make_it_sound(d) # Output: Quack!
# Using the Person class
p = Person()
make_it_sound(p) # Output: I'm quacking like a duck!
In this example, both Duck
and Person
classes have a sound
method. It doesn’t matter if the object is a Duck or a Person; as long as it has a sound
method, the make_it_sound
function will work correctly.
However, duck typing can lead to runtime errors. For instance, changing the name of the method sound
in the class Person to speak will raise an AttributeError
on runtime. This is because the function make_it_sound
expects all the objects to have a sound function.
class Duck:
def sound(self):
print("Quack!")
class Person:
def speak(self):
print("I'm quacking like a duck!")
def make_it_sound(obj):
obj.sound()
# Using the Duck class
d = Duck()
make_it_sound(d)
# Using the Person class
p = Person()
make_it_sound(p)
Output:
AttributeError: 'Person' object has no attribute 'sound'
Example No: 02
Let’s explore another program that deals with calculating areas of different shapes without worrying about their specific types. Each shape (Rectangle, Circle, Triangle) has its own class with a method called area to calculate its area.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
self.name = "Rectangle"
def area(self):
return self.width * self.height
class Circle:
def __init__(self, radius):
self.radius = radius
self.name = "Circle"
def area(self):
return 3.14 * self.radius * self.radius
def circumference(self):
return 2 * 3.14 * self.radius
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
self.name = "Triangle"
def area(self):
return 0.5 * self.base * self.height
def print_areas(shapes):
for shape in shapes:
print(f"Area of {shape.name}: {shape.area()}")
if hasattr(shape, 'circumference'):
print(f"Circumference of the {shape.name}: {shape.circumference()}")
# Usage
shapes = [
Rectangle(4, 5),
Circle(3),
Triangle(6, 8)
]
print("Areas of different shapes:")
print_areas(shapes)
Output:
Areas of different shapes:
Area of Rectangle: 20
Area of Circle: 28.259999999999998
Circumference of the Circle: 18.84
Area of Triangle: 24.0
In the above example, we have a print_areas
function that takes a list of shapes and prints their names along with their calculated areas. Notice that we don’t need to check the type of each shape explicitly before calculating its area. As the method circumference
is only present for the Circle
class, it gets implemented only once. This example shows how duck typing can be used to write flexible code.
Final Thoughts
Duck typing is a powerful feature of Python that makes your code more dynamic and versatile, enabling you to write more generic and adaptable programs. While it brings many benefits, such as flexibility and simplicity, it also requires careful documentation and testing to avoid potential errors.
Kanwal Mehreen Kanwal is a machine learning engineer and a technical writer with a profound passion for data science and the intersection of AI with medicine. She co-authored the ebook “Maximizing Productivity with ChatGPT”. As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She’s also recognized as a Teradata Diversity in Tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change, having founded FEMCodes to empower women in STEM fields.