Check the Type of a Variable in Python: type() vs isinstance()

Checking a variable’s type in Python takes one of two built-ins. type(x) tells you exactly what x is, and isinstance(x, SomeType) tells you whether it’s that type or a subclass of it:

price = 19.99
type(price)                  # <class 'float'>
isinstance(price, float)     # True
isinstance(price, (int, float))   # True: either type

I tested everything below on Python 3.12.5 with NumPy 2.5.3; the screenshots are the real Command Prompt output.

Use isinstance() for checks in your code and type() when you want to see or print the type. Below you’ll see why, plus numbers, floats, dates and the surprise that True counts as an integer.

Using type() to see a variable’s type

type() returns the class of the object. Add .__name__ when you only want the name as text:

values = [42, 19.99, "Austin", True, None, [1, 2], {"TX": 1}]

for v in values:
    print(f"{v!r:12} -> {type(v).__name__}")

print(type(42) is int)           # compare a type with `is`

Output:

42           -> int
19.99        -> float
'Austin'     -> str
True         -> bool
None         -> NoneType
[1, 2]       -> list
{'TX': 1}    -> dict
True
Command Prompt output of Python type() on an int, float, string, bool, None, list and dict
type() on seven common values. Note that None has its own type, NoneType.

When you compare types directly, use is, as in type(x) is int. There’s exactly one int class, so identity is the right test.

type() vs isinstance(): which should you use?

isinstance() accepts subclasses and a tuple of types, which makes it the better choice almost every time you’re making a decision in code:

price = 19.99

print(isinstance(price, float))             # True
print(isinstance(price, (int, float)))      # either one: True
print(isinstance("19.99", (int, float)))    # a string isn't a number

# isinstance also accepts subclasses, type() doesn't
class Discount(float):
    pass

d = Discount(0.15)
print("isinstance:", isinstance(d, float), "| type() is float:", type(d) is float)

Output:

True
True
False
isinstance: True | type() is float: False

Discount is a kind of float, so isinstance() says yes while type() is float says no. Code that rejects subclasses usually breaks for no good reason. isinstance and issubclass in Python has more examples.

Why does isinstance(True, int) return True?

Because bool is a subclass of int: True behaves like 1 and False like 0. That’s useful for counting, but it means a plain isinstance(x, int) lets booleans through:

print("isinstance(True, int):", isinstance(True, int))   # bool is a subclass of int
print("type(True) is int    :", type(True) is int)
print("True + True          :", True + True)

def is_int(value):
    return isinstance(value, int) and not isinstance(value, bool)

for v in [7, True, 7.0]:
    print(f"is_int({v!r}) -> {is_int(v)}")

Output:

isinstance(True, int): True
type(True) is int    : False
True + True          : 2
is_int(7) -> True
is_int(True) -> False
is_int(7.0) -> False
Command Prompt output showing Python isinstance True int returns True because bool is a subclass of int, and an is_int helper that excludes bool
True + True is 2. The helper excludes booleans explicitly.

If a form field or JSON value should be a whole number, exclude bool as shown. Checking if a variable is an integer and checking for a boolean go further.

How to check if a variable is a number in Python

numbers.Number covers every numeric type, including Decimal, complex numbers and your own classes, as long as they register. Exclude bool again unless you want it:

import numbers
from decimal import Decimal

def is_number(value):
    return isinstance(value, numbers.Number) and not isinstance(value, bool)

for v in [7, 19.99, Decimal("19.99"), 2 + 3j, "19.99", True, None]:
    print(f"{v!r:18} -> {is_number(v)}")

print("Decimal is a Real?", isinstance(Decimal("19.99"), numbers.Real))

Output:

7                  -> True
19.99              -> True
Decimal('19.99')   -> True
(2+3j)             -> True
'19.99'            -> False
True               -> False
None               -> False
Decimal is a Real? False
Command Prompt output of a Python is_number function using numbers.Number for int, float, Decimal, complex, a string, a bool and None
Strings, booleans and None are rejected. Decimal is a number but not a numbers.Real.

That last line catches people out: Decimal deliberately isn’t registered as Real, so check numbers.Number if money values stored as Decimal should pass.

Check if a variable is a float

isinstance(x, float) answers the type question. Whether a string could become a float is a different question, and float() inside try answers it best:

for v in [19.99, 20.0, 20, "19.99"]:
    print(f"{v!r:8} isinstance float: {isinstance(v, float)}")

print((20.0).is_integer())               # a float with no fractional part

def looks_like_float(text):
    try:
        float(text)
        return True
    except ValueError:
        return False

print(looks_like_float("19.99"), looks_like_float("$19.99"), "12.5".isdigit())

Output:

19.99    isinstance float: True
20.0     isinstance float: True
20       isinstance float: False
'19.99'  isinstance float: False
True
True False False

"12.5".isdigit() is False because the dot isn’t a digit, which is why string methods are the wrong tool here. Checking if a string is an integer or float covers that case properly.

Check if a variable is a datetime or a date

Here’s a subtle one: datetime is a subclass of date, so every datetime also passes isinstance(x, date). Check for the subclass first:

from datetime import date, datetime

launch = datetime(2026, 10, 8, 14, 0)        # a date AND a time
holiday = date(2026, 11, 26)                 # Thanksgiving 2026

print("isinstance(launch, date) :", isinstance(launch, date))     # True!
print("isinstance(launch, datetime):", isinstance(launch, datetime))
print("type(launch) is date     :", type(launch) is date)
print("isinstance(holiday, datetime):", isinstance(holiday, datetime))

def kind(value):
    if isinstance(value, datetime):       # check the subclass FIRST
        return "datetime"
    if isinstance(value, date):
        return "date"
    return "neither"

print(kind(launch), kind(holiday), kind("2026-11-26"))

Output:

isinstance(launch, date) : True
isinstance(launch, datetime): True
type(launch) is date     : False
isinstance(holiday, datetime): False
datetime date neither
Command Prompt output showing a Python datetime passing isinstance date, and a kind function that checks datetime before date
The launch time is a datetime and a date. Checking datetime first keeps the two apart.

The same “most specific first” rule applies to bool and int, and to any class hierarchy of your own.

Checking types with match-case (Python 3.10+)

When you branch on several types, structural pattern matching reads more cleanly than a chain of isinstance() calls:

def describe(value):
    match value:
        case bool():                 # before int, because bool is an int
            return "yes/no"
        case int() | float():
            return "number"
        case str():
            return "text"
        case None:
            return "missing"
        case _:
            return type(value).__name__

print([describe(v) for v in [True, 7, 2.5, "TX", None, [1]]])

Output:

['yes/no', 'number', 'number', 'text', 'missing', 'list']

Put bool() before int(), for the reason you’ve already seen. Otherwise True would match the int() case.

NumPy types: why np.int64 isn’t an int

Values that come out of NumPy arrays have NumPy types. np.float64 happens to subclass float, but np.int64 doesn’t subclass int:

import numbers
import numpy as np

count = np.int64(5)
price = np.float64(19.99)

print("isinstance(np.int64, int)       :", isinstance(count, int))
print("isinstance(np.int64, Integral)  :", isinstance(count, numbers.Integral))
print("isinstance(np.float64, float)   :", isinstance(price, float))
print("np.issubdtype(int64, integer)   :", np.issubdtype(count.dtype, np.integer))

Output:

isinstance(np.int64, int)       : False
isinstance(np.int64, Integral)  : True
isinstance(np.float64, float)   : True
np.issubdtype(int64, integer)   : True

If your code receives values from NumPy or pandas, check against numbers.Integral or numbers.Real rather than int and float, and both kinds will pass.

How to check the type of a variable: cheat sheet

QuestionCheck
What type is it?type(x) or type(x).__name__
Is it a string?isinstance(x, str)
Is it a whole number (not bool)?isinstance(x, int) and not isinstance(x, bool)
Is it any number?isinstance(x, numbers.Number)
Is it a float?isinstance(x, float)
Can this text become a float?float(text) inside try
Datetime or plain date?Check datetime first, then date
Is it None?x is None
Several types at onceisinstance(x, (int, float))

For None specifically, use is rather than a type check; checking if a variable is None explains why. More type-checking guides:

Frequently asked questions

How do I check the type of a variable in Python?

Use type(x) to see it, or isinstance(x, SomeType) to test it. isinstance() is the better choice for checks because it accepts subclasses.

What’s the difference between type() and isinstance()?

type() returns the exact class. isinstance() returns True for that class or any subclass, and accepts a tuple of types.

Why is isinstance(True, int) True?

bool is a subclass of int in Python. Add and not isinstance(x, bool) when booleans shouldn’t count.

How do I check if a variable is a number?

isinstance(x, numbers.Number) covers int, float, complex, Decimal and Fraction. Exclude bool if needed.

How do I check if a variable is a datetime?

isinstance(x, datetime). Because datetime is a subclass of date, test for datetime before date.

Can I check a variable’s type with ==?

type(x) == int works, but type(x) is int is the idiomatic form, and isinstance() is usually better still. The isinstance() documentation describes the exact rules.