Duck Typing in Python
Tags: python, beginners, codenewbie, tutorial
Published: 2020-12-06 22:01:27

> 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

```python
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](https://renanmf.com/handling-exceptions-python/) is to use a `try/except` block:

```python
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](https://renanmf.com/classes-objects-python/)
* [Object-Oriented Programming: Encapsulation in Python](https://renanmf.com/object-oriented-programming-encapsulation-in-python/)
* [Inheritance in Python](https://renanmf.com/inheritance-python/)
* [Object-Oriented Programming: Polymorphism in Python](https://renanmf.com/object-oriented-programming-polymorphism-in-python/)

Also, consider reading [Handling Exceptions in Python](https://renanmf.com/handling-exceptions-python/) to learn more about how to deal with exceptions in Python.