Image by the author | DALLE-3 and Canva
What is Duck Typing?
Duck typing is a programming concept often associated with dynamic languages like Python that puts more emphasis on the behavior of the object than its type or class. When using duck typing, an object is checked to see if it has certain methods or attributes, rather than checking 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 Python programming. It allows for more flexible and reusable code and supports polymorphism, allowing different types of objects to be used interchangeably as long as they provide the necessary interface. This results in simpler and more concise code. However, duck typing also has its disadvantages. One major disadvantage is the potential for runtime errors. Additionally, it can make code difficult to understand.
Understanding Dynamic Behavior in Python
In dynamically typed languages, the types of variables are not fixed, but are determined at runtime based on the assigned values. In contrast, statically typed languages check the types of variables at compile time. For example, if you try to reassign a variable to a value of a different type in static typing, an error will occur. Dynamic typing provides greater flexibility in the use of variables and objects.
Let us consider the *
Python operator; behaves differently depending on the type of object it is used with. When used between two integers, it performs a 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 does Duck typing work 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 the objects can do. In duck typing, more importance is given to the methods defined within the class than to the object itself. Let us clarify this with a basic example.
Example No: 01
We have two classes: Duck and Person. Ducks can make a quacking sound, while people can talk. 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 through 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
The function will work correctly.
However, duck typing can lead to runtime errors. For example, changing the name of the method sound
In class the person who speaks will raise a AttributeError
at runtime. This is because the function make_it_sound
expects all 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)
Production:
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)
Production:
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 example above, we have a print_areas
function that takes a list of shapes and prints their names along with their calculated areas. Note that we do not need to check the type of each shape explicitly before calculating its area. As the method circumference
It is only present for the Circle
Class, 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, allowing 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 technical writer with a deep passion for data science and the intersection of ai with medicine. She is the co-author of the eBook “Maximizing Productivity with ChatGPT.” As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She is also recognized as a Teradata Diversity in tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change and founded FEMCodes to empower women in STEM fields.