Check Data Types with Python type()

Learn how to check the type of any Python value using the type() built-in function, the isinstance() function for flexible checks, and when to use each approach.

5 min read

Every Python value has a type, and the Python type function is your primary tool for checking that type at runtime. This skill is essential when validating function inputs, debugging unexpected behavior, and writing code that must handle multiple kinds of data gracefully. Python gives you two built-in tools for type introspection: the type function, which returns the exact type of any object as a class, and the isinstance function, which tests whether an object belongs to a given type or any type that inherits from it. While both functions answer the question "what kind of value is this," they answer it differently, and understanding when to use each one separates clean, maintainable code from brittle type checks that break when code evolves.

If you have read the overview of Python data types and understand the difference between immutable and mutable types, type checking is the next logical skill to add to your toolkit. Knowing what types your data has is essential before you attempt to convert data types or debug type-related errors in your programs. This article covers the type function, the isinstance function, the critical difference between them, and the practical patterns that experienced Python programmers use to validate types in real code.

The type() function: exact type identity

The type function accepts a single argument, any Python object, and returns that object's type as a class object. When you call type on an integer, you get back the class int. When you call it on a string, you get str. When you call it on a list, you get list. The return value is itself an object that you can compare with the double-equals operator or print for inspection. This makes type useful for quick interactive exploration when you are trying to understand what kind of data a variable holds.

pythonpython
print(type(42))
print(type("hello"))
print(type([1, 2, 3]))

Running this code prints class int, class str, and class list, each with the angle-bracket notation that Python uses for type representations. The type function always returns the most specific type, which is one of the reasons that type-based checks can be too narrow for many practical situations. The bool type is particularly interesting because type returns class bool, not class int, even though bool is technically a subclass of int.

The most common pattern you will see with type is comparing its return value to a known type using the equality operator: type(x) == int checks whether x is exactly an integer. This pattern works correctly for simple built-in types like int, str, float, list, dict, tuple, and set. However, it fails in any situation involving inheritance. If you define a custom class that inherits from list, comparing type to list returns False even though your custom object behaves like a list in every meaningful way. For that reason, type equality checks are considered brittle and are rarely the right choice in application code that may evolve over time.

The isinstance() function: flexible type checking with inheritance

The isinstance function takes two arguments: an object and a type or tuple of types. It returns True if the object is an instance of the given type or of any class that inherits from that type. This means isinstance respects Python's class hierarchy, which makes it more flexible and more aligned with the way object-oriented code is designed to work. If you check isinstance against a parent class, all subclasses pass the check automatically.

The second argument to isinstance can be a tuple of multiple types. When you provide a tuple, isinstance returns True if the object matches any of the types in the tuple. This is the idiomatic way to check whether a value is numeric. Passing (int, float, complex) as the second argument catches all three numeric types in a single expression. The same pattern works for checking string-like types, collection types, or any family of related types that your function might accept.

pythonpython
def double(value):
    if isinstance(value, (int, float)):
        return value * 2
    if isinstance(value, str):
        return value + value
    raise TypeError("Expected a number or a string")
 
print(double(21))
print(double("Hi"))

This example demonstrates the isinstance-based validation pattern that appears in countless Python functions. The function checks for numeric types first, then for strings, and raises a descriptive TypeError for anything else. Using a tuple of types for the numeric check means the function works with integers, floats, and complex numbers without needing separate branches for each. If someone later passes a custom numeric type that inherits from float, isinstance handles it correctly without changes to the function.

When type() beats isinstance()

There are a small number of situations where an exact type match using type is preferable to the flexible isinstance check. The most common case is when you are writing a serialization or deserialization function that must reconstruct objects of the exact same type that was originally serialized. If you store an integer and a boolean value, isinstance would conflate the two because bool is a subclass of int, but type distinguishes them correctly. Framework code that introspects classes at runtime also tends to use type because it needs to know the exact class, not just whether the class belongs to a category.

Another legitimate use of type is in testing code that verifies an object's exact type for correctness. If a function is documented to return a list, not a subclass of list, a test that checks type equality provides a stronger guarantee than one that uses isinstance. In everyday application code, however, isinstance is almost always the better choice. It is more flexible, more aligned with Python's design philosophy, and less likely to produce false negatives when code is extended with subclasses.

Practical type-checking patterns

The most robust pattern for validating function inputs combines isinstance checks with descriptive error messages. When a function receives an argument of the wrong type, raising a TypeError with a message that explains what was expected makes debugging easier for anyone calling your function. The error message should name the expected types and, if helpful, include the actual type that was received. This is the pattern used by Python's own standard library functions, and adopting it makes your code feel native to the language.

Type checking with isinstance also pairs well with the concept of duck typing, which is the Python philosophy that an object's suitability is determined by what it can do rather than what type it is. Sometimes the best approach is not to check the type at all but to try the operation and catch the exception if it fails. For example, instead of checking whether a value is a number before multiplying it, you can attempt the multiplication and catch a TypeError if it fails. This approach, known as "easier to ask forgiveness than permission," is the most Pythonic pattern of all. Use isinstance when you need to dispatch to different behaviors based on type. Use try-except when you just need to attempt an operation and handle failure gracefully.

Rune AI

Rune AI

Key Insights

  • type(obj) returns the exact type of obj as a class object.
  • isinstance(obj, SomeType) returns True if obj is an instance of SomeType or any of its subclasses.
  • isinstance(obj, (TypeA, TypeB)) checks against multiple types at once using a tuple.
  • Prefer isinstance() over type() == for most type checks because it respects inheritance.
  • Use type() only when you need an exact match and want to reject subclasses.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between type() and isinstance()?

The type() function returns the exact type of an object and does not consider inheritance. If you have a class Dog that inherits from Animal, type(my_dog) returns Dog, not Animal. The isinstance() function checks whether an object is an instance of a given type or any of its parent types. isinstance(my_dog, Animal) returns True because Dog inherits from Animal. For most practical type checks, isinstance() is preferred because it respects inheritance hierarchies.

Can I check multiple types at once with isinstance()?

Yes. Pass a tuple of types as the second argument to isinstance(). For example, isinstance(value, (int, float)) returns True if value is either an integer or a float. This is cleaner than chaining multiple isinstance calls with or operators and is the idiomatic way to check for numeric types, string-like types, or any group of related types.

When should I use type() instead of isinstance()?

Use type() when you need an exact type match and want to reject subclasses. This is rare in application code but appears in some frameworks and serialization libraries where exact type identity matters. In everyday Python programming, isinstance() is almost always the better choice because it is more flexible and respects the object-oriented principle that a subclass should be usable wherever a parent class is expected.

Conclusion

The type() function tells you exactly what type an object is, while isinstance() tells you whether an object belongs to a type or any of its subtypes. For most practical purposes, isinstance() is the right tool because it respects Python's inheritance model and produces more flexible, maintainable code. Reserve type() for the rare cases where you need an exact type match and want to explicitly reject subclasses.