Abstract


# Bad 
if isinstance(obj, list):
    for x in obj:
 
# Good (Avoid `isinstance`, `type`)
try:
    for x in obj:
        process(x)
except TypeError:
    return  # or log/skip silently
  • We use duck typing in Python instead of relying on isinstance because Python is dynamically typed. Type checks don’t actually guarantee safety even if an isinstance check passes, the object may be a subclass that overrides methods in unexpected ways, causing subtle bugs. For example, a subclass could pass isinstance(obj, list) but override append or __getitem__ so that the behavior isn’t what you expect
  • With duck typing, we focus on if the object supports the operations we need, rather than forcing it to be a specific type. This avoids false confidence from rigid type checks and makes code more flexible. It fits Python’s philosophy: “if it quacks like a duck, it’s a duck.”
  • By contrast, languages like Java don’t face this issue as much because they use static type checking. In Java, the compiler enforces type contracts at compile time, ensuring that a subclass must still satisfy the interface or superclass contract. This prevents most “type narrowing” problems. In Python issues only surface at runtime, so duck typing (operation-based checks) tends to be safer than isinstance

But Java has instanceof!

In Java, instanceof is backed by compile-time contracts (the method signatures (parameters, return types) must match exactly.), so while subclasses may behave differently, they cannot break the type’s promises. That’s why it’s less dangerous.