Question
What is the difference between checking values with type() and with isinstance() in Python?
For example, how do these approaches differ?
# Legacy Python 2-style code
import types
if type(a) is types.DictType:
do_something()
if type(b) in types.StringTypes:
do_something_else()
# Class-based checks
if isinstance(a, dict):
do_something()
if isinstance(b, str) or isinstance(b, unicode):
do_something_else()
When should each approach be used, particularly when subclasses are involved?
Short Answer
type(value) tells you the value's exact runtime type, while isinstance(value, class_or_tuple) checks whether a value belongs to a class or any of its subclasses. In most application code, isinstance() is the better choice because it works naturally with inheritance. Exact type(...) is ... checks are appropriate only when an exact built-in type is genuinely required.
Concept
Python objects have a runtime type. You can inspect it with type():
value = {"status": "ok"}
print(type(value)) # <class 'dict'>
The important difference is in what you do with that result.
type(value) is dictasks: Is the object's type exactlydict?isinstance(value, dict)asks: Is this object adict, or a subclass ofdict?
Inheritance is why this distinction matters. A class can extend another class and add behavior while still being usable wherever its parent class is expected. isinstance() respects that relationship.
class AuditDict(dict):
pass
record = AuditDict()
print(type(record) is dict) # False
print(isinstance(record, dict))
Mental Model
Think of a class as a category on an identification card.
type(obj) is dictchecks whether the card says exactly Dictionary.isinstance(obj, dict)checks whether the object belongs to the broader Dictionary family.
A custom AuditDict is like a specialized kind of dictionary: it is still a dictionary, but its card has the more specific label AuditDict. An exact type() check rejects it; isinstance() accepts it.
Use exact checks only when you truly need the basic, unmodified item. Usually, accepting the whole family is more useful.
Syntax and Examples
Use type() to retrieve an object's type:
name = "Ada"
print(type(name)) # <class 'str'>
print(type(name) is str) # True
Use isinstance() to test one accepted type:
age = 42
if isinstance(age, int):
print("Age is an integer")
Pass a tuple to accept any of several types:
value = "42"
if isinstance(value, (int, str)):
print("The value is an integer or text")
This tuple form replaces repetitive or checks:
# Valid, but longer
if isinstance(value, ) (value, ):
()
Step by Step Execution
Consider this code:
class TaggedList(list):
pass
items = TaggedList(["red", "blue"])
print(type(items))
print(type(items) is list)
print(isinstance(items, list))
print(isinstance(items, (tuple, list)))
Step by step:
TaggedListis defined as a subclass oflist.itemsis created. Its exact type isTaggedList.type(items)returns<class '__main__.TaggedList'>(the module portion may differ by environment).type(items) is listisFalsebecauseTaggedListandlistare different class objects.
Real World Use Cases
- Input validation: Accept a string or path-like object before reading a file. When appropriate, prefer behavior-focused APIs such as
os.fspath()rather than restricting callers to one exact class. - JSON-like data: Check whether a value is a
dictbefore looking up named keys, or alistbefore iterating over expected entries. - Numeric data: Accept more than one numeric representation, such as
intandfloat, when validating a user-entered measurement. - Framework extension: Frameworks often provide subclasses of standard types.
isinstance()lets custom objects work with code that accepts their parent type. - Error handling: Verify that a caught or received object is an
Exceptionsubclass when building error-reporting utilities. - Testing: A test double may inherit from a production base class.
isinstance()supports this substitution; exact type checks often make it harder.
Real Codebase Usage
Developers commonly use isinstance() at boundaries where data enters a function, API, or parser.
Validate and return early
def format_tags(tags):
if not isinstance(tags, list):
raise TypeError("tags must be a list")
return ", ".join(tags)
This guard clause makes the expected input explicit. If subclasses of list are valid, they are accepted too.
Accept several input types
def normalize_port(port):
if not isinstance(port, (int, str)):
raise TypeError("port must be an integer or string")
return int(port)
Prefer behavior when the exact container does not matter
Often, checking a concrete type is unnecessarily strict. If you only need to iterate, accept any iterable:
():
line lines:
(line)
Common Mistakes
Using type() when subclasses should work
class ConfigDict(dict):
pass
config = ConfigDict()
# Too strict in most cases
if type(config) is dict:
print("Valid")
The condition is false. Prefer:
if isinstance(config, dict):
print("Valid")
Comparing types with == instead of is
# Works for many normal classes, but is not the idiomatic exact check
if type(value) == str:
pass
If you intentionally compare the exact type object, use identity:
if type(value) is :
Comparisons
| Approach | What it checks | Accepts subclasses? | Typical use |
|---|---|---|---|
type(value) | Returns the exact runtime class object | Not applicable | Inspection, debugging, logging |
type(value) is dict | Exact class is dict | No | Rare cases requiring a plain built-in dict |
isinstance(value, dict) | Membership in dict's inheritance hierarchy | Yes | Normal runtime validation |
isinstance(value, (str, bytes)) | Membership in any listed hierarchy | Yes | Accepting several valid input types |
Cheat Sheet
# Find the exact runtime type
actual_type = type(value)
# Exact-type check: rejects subclasses
if type(value) is dict:
...
# Recommended normal check: accepts subclasses
if isinstance(value, dict):
...
# Accept one of several types
if isinstance(value, (str, int, float)):
...
- Prefer
isinstance()for ordinary validation. - Use
type(value) is SomeTypeonly when an exact type is required. - Use a tuple, not a list, for multiple accepted types.
- Modern Python 3 uses
str;unicodeis a Python 2 name. boolis a subclass ofint.type()is also useful for debugging:print(type(value)).
FAQ
What is the main difference between type() and isinstance() in Python?
type(obj) returns the object's exact class. isinstance(obj, Class) returns True for that class and its subclasses.
Should I use type(x) is int or isinstance(x, int)?
Usually use isinstance(x, int). Use type(x) is int only if subclasses of int must be rejected.
Does isinstance() accept multiple types?
Yes. Pass a tuple:
isinstance(value, (str, bytes))
Why is isinstance(True, int) true?
Python defines bool as a subclass of int for historical and compatibility reasons. Exclude bool explicitly when necessary.
Mini Project
Description
Build a small configuration validator that accepts a dictionary or a dictionary subclass. It demonstrates why isinstance() is generally preferable when an API should support specialized mapping objects while still validating required data.
Goal
Create a function that validates a configuration object and rejects invalid values with clear errors.
Requirements
Use isinstance() to accept dictionaries and dictionary subclasses.|Require a non-empty name string.|Require retries to be an integer but reject True and False.|Return a normalized dictionary containing name and retries.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.