Here, the type or class of an object is determined by its behavior (methods and properties), not by its actual class

class Duck:
    def quack(self):
        print("Quack!")
 
class Person:
    def quack(self):
        print("I'm pretending to be a duck!")
 
def make_it_quack(thing):
    thing.quack()
 
duck = Duck()
person = Person()
 
make_it_quack(duck)    # Output: Quack!
make_it_quack(person)  # Output: I'm pretending to be a duck!
 

Little difference from Abstraction: Abstraction:

from abc import ABC, abstractmethod
 
class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass
 
class Dog(Animal):
    def speak(self):
        print("Bark!")
 
# You *must* implement speak() or Python will raise an error
 

“I don’t care how it speaks, but every Animal must be able to speak() duck typing:“I don’t even care what the object is. If it walks like a duck and quacks like a duck, just let it go!”