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 anisinstance
check passes, the object may be a subclass that overrides methods in unexpected ways, causing subtle bugs. For example, a subclass could passisinstance(obj, list)
but overrideappend
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.