If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
Dynamic Typed languages like Python and JavaScript have an interesting characteristic of letting an object respond to a call according to the definitions of methods and attributes it has independently of its type.
This means that the type of the object, i.e, its own class, or the class it inherits from doesn't matter, the interpreter only cares about the interfaces of the object.
Code Example
class Duck:
def quack_and_swim(self):
print('quack and swim')
class Bird:
def quack_and_swim(self):
print('quack and swim')
class Frog:
def ribbit_and_swim(self):
print('ribbit and swim')
def check_duck(animal):
animal.quack_and_swim()
if __name__ == '__main__':
duck = Duck()
bird = Bird()
frog = Frog
check_duck(duck)
check_duck(bird)
check_duck(frog)
The output of the code above is:
quack and swim
quack and swim
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
File "<stdin>", line 2, in is_duck
AttributeError: type object 'Frog' has no attribute 'quack_and_swim'
The first and second objects duck
and bird
could successfully respond to the of quack_and_swim()
while the frog
object couldn't and throws an exception.
In terms of code, the classes Duck
and Bird
are not related at all, they don't share a parent class through inheritance, but they have the same interface for the function quack_and_swim()
.
The type of the object is not important, the interface of the object when you use it is what matters.
In the end, if the object behaves like a duck, for practical purposes, it is a duck, no matter what bird it is, as long as it has the right interface.
But what do we do about the thrown exception?
Since we don't check the type of the object or if it has certain attributes, the Pythonic way to handle any exception is to use a try/except
block:
try:
check_duck(frog)
except AttributeError as error:
print(error)
The treated exception above will output:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: name 'check_duck' is not defined
So we always assume the object has the method or attribute we need, if it doesn't, we catch the exception with the try
block and treat it accordingly.
If you had any trouble following this article, I recommend this series of articles about Object-Oriented Programming:
- Classes and Objects in Python
- Object-Oriented Programming: Encapsulation in Python
- Inheritance in Python
- Object-Oriented Programming: Polymorphism in Python
Also, consider reading Handling Exceptions in Python to learn more about how to deal with exceptions in Python.
Top comments (0)