2127

Is there a simple way to determine if a variable is a list, dictionary, or something else?

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
  • 2
    You shouldn't need to "tell the difference". Ever. The point of Python (and duck typing) is that you never need to know. Your function from which your "getting an object back" isn't designed properly if it returns objects of random, inconsistent types. – S.Lott Feb 09 '10 at 11:21
  • 50
    While in general I agree with you, there are situations where it is helpful to know. In this particular case I was doing some quick hacking that I eventually rolled back, so you are correct this time. But in some cases - when using reflection, for example - it is important to know what type of object you are dealing with. – Justin Ethier Feb 09 '10 at 13:10
  • You'd have to provide some more concrete examples of when you think you need to know the data type. Usually, that's a sign of (a) a poorly-defined function or (b) pretty-poor polymorphism. It's usually fixable with a simple design change. – S.Lott Feb 09 '10 at 16:51
  • 70
    @S.Lott I'd disagree with that; by being able to know the type, you can deal with some pretty variant input and still do the right thing. It lets you work around interface issues inherent with relying on pure duck-typing (eg, the .bark() method on a Tree means something entirely different than on a Dog.) For example, you could make a function that does some work on a file that accepts a string (eg, a path), a path object, or a list. All have different interfaces, but the final result is the same: do some operation on that file. – Robert P Jul 21 '11 at 21:33
  • 1
    @Robert P: "the .bark() method on a Tree means something entirely different than on a Dog". While true, I find this example contrived. If your application is so hopelessly confused that (1) Dogs and Trees are appearing willy-nilly inside lists or tuples, and (2) you've semantically overloaded a method name like that, then type-checking isn't going to be much help. The "string, path object or a list" example is also easily handled through simple `try:` blocks without resorting to type checking. Type checking imposes bad limits. It stifles the "open" of the open-closed principle. – S.Lott Jul 21 '11 at 21:41
  • 24
    @S.Lott I hoped it would be obvious that it's a contrived example; nonetheless it's a major failing point of duck typing, and one that `try` doesn't help with. For example, if you knew that a user could pass in a string or an array, both are index-able, but that index means something completely different. Simply relying on a try-catch in those cases will fail in unexpected and strange ways. One solution is to make a separate method, another to add a little type checking. I personally prefer polymorphic behavior over multiple methods that do almost the same thing...but that's just me :) – Robert P Jul 22 '11 at 00:57
  • @Robert P: It's not a "major failing point", since it doesn't actually matter. "if you knew that a user could pass in a string or an array," is contrived. **You** define an API to accept one or the other. What a user "could pass in" doesn't matter. when they provide the wrong one, something **must** eventually break. That's the definition of "wrong type" -- something breaks. Which leads to an exception. Which leads to a `try:` detecting the wrong type. The definition of "wrong type" is that an exception **must** the raised somewhere. – S.Lott Jul 22 '11 at 10:14
  • 23
    @S.Lott, what about unit testing? Sometimes you want your tests to verify that a function is returning something of the right type. A very real example is when you have class factory. – Elliot Cameron Sep 24 '12 at 16:23
  • 18
    For a less contrived example, consider a serializer/deserializer. By definition you are converting between user-supplied objects and a serialized representation. The serializer needs to determine the type of object you passed in, and you may not have adequate information to determine the deserialized type without asking the runtime (or at the very least, you may need it for sanity checking to catch bad data before it enters your system!) – Karl May 13 '13 at 02:35
  • 1
    Another possible scenario: Catching several exception types in a single "except" block, do some processing (like adding info to the error message), and then raise the exception with the additional info. You can "Except (Ex1, Ex2) as err:" and later on, raise type(err)(some_better_error_message). Using this method, one can also catch-all on a base exception type (e.g.: requests.exceptions.RequestsException as err) and then raise it again using the right sub class (again: raise type(err)(new_message)). – Joe Dec 01 '14 at 21:06
  • 2
    Another reason to explicitly discover the type of an object is that the documentation doesn't always tell you -- and sometimes it's just useful to know. So not for control-flow, but just so you can reverse-engineer the return values of a function under different conditions. Example: Python3 [`subprocess.check_output()`](https://docs.python.org/3/library/subprocess.html#subprocess.check_output) returns different types depending on the `universal_newlines` parameter. – Brent Bradburn Feb 19 '15 at 16:50
  • Also: [How to determine a Python variable's type?](https://stackoverflow.com/questions/402504/how-to-determine-a-python-variables-type) – Georgy Jun 19 '19 at 12:33
  • If it's a simple case, you might wanna use type() or isinstance() and a neat if/else ladder. –  Oct 30 '20 at 09:26

15 Answers15

2294

There are two built-in functions that help you identify the type of an object. You can use type() if you need the exact type of an object, and isinstance() to check an object’s type against something. Usually, you want to use isinstance() most of the times since it is very robust and also supports type inheritance.


To get the actual type of an object, you use the built-in type() function. Passing an object as the only parameter will return the type object of that object:

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

This of course also works for custom types:

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

Note that type() will only return the immediate type of the object, but won’t be able to tell you about type inheritance.

>>> type(b) is Test1
False

To cover that, you should use the isinstance function. This of course also works for built-in types:

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance() is usually the preferred way to ensure the type of an object because it will also accept derived types. So unless you actually need the type object (for whatever reason), using isinstance() is preferred over type().

The second parameter of isinstance() also accepts a tuple of types, so it’s possible to check for multiple types at once. isinstance will then return true, if the object is of any of those types:

>>> isinstance([], (tuple, list, set))
True
rayryeng
  • 102,964
  • 22
  • 184
  • 193
poke
  • 369,085
  • 72
  • 557
  • 602
  • 73
    I think it's clearer to use `is` instead of `==` as the types are singletons – John La Rooy Feb 08 '10 at 22:01
  • 20
    @gnibbler, In the cases you would be typechecking (which you shouldn't be doing to begin with), `isinstance` is the preferred form anyhow, so neither `==` or `is` need be used. – Mike Graham Feb 08 '10 at 22:50
  • 26
    @Mike Graham, there are times when `type` is the best answer. There are times when `isinstance` is the best answer and there are times when duck typing is the best answer. It's important to know all of the options so you can choose which is more appropriate for the situation. – John La Rooy Feb 08 '10 at 23:13
  • 7
    @gnibbler, That may be, though I haven't yet ran into the situation where `type(foo) is SomeType` would be better than `isinstance(foo, SomeType)`. – Mike Graham Feb 09 '10 at 16:45
  • 1
    I used constructs like `type( param ) in ( list, tuple )` in the past, when working with different parameter types, where a completely different approach was required based on the type. – poke Feb 09 '10 at 20:48
  • 2
    @poke: Maybe I'm a bit late to the party, but I don't think there is ever a situation where you should prefer `type(x) is X` over `isinstance(x, X)`. Even the example in your last comment is better written as `isinstance(param, (list, tuple))`. PEP 8 explicitly discourages the use of `type(x) is X`. – Sven Marnach Oct 27 '11 at 13:01
  • 1
    @SvenMarnach PEP8 is a style guideline though, not a rule that has to be enforced. The example still works and can be useful. – poke Oct 27 '11 at 13:48
  • 6
    @poke: i totally agree about PEP8, but you’re attacking a strawman here: the important part of Sven’s argument wasn’t PEP8, but that you can use `isinstance` for your usecase (checking for a range of types) as well, and with as clean a syntax as well, which has the great advantage that you can capture subclasses. someone using `OrderedDict` would hate your code to fail because it just accepts pure dicts. – flying sheep Oct 27 '12 at 11:12
  • i have some code where "type(x) is Y" returns false but "isinstance(x,Y)" returns true – don bright Jul 23 '13 at 03:33
  • @donbright That’s when `type(x)` is `Z` and `Z` is a subtype of `Y` (meaning that every instance of `Z` is also an instance of `Y` but the type of that instance is not identical to `Y`). – poke Jul 23 '13 at 10:36
  • CAREFUL! You can clobber types and your checks will fail: >>> the_d = {} >>> t = lambda x: "aight" if type(x) is dict else "NOPE" >>> t(the_d) 'aight' >>> dict = "dude." >>> t(the_d) 'NOPE' – deed02392 May 31 '14 at 11:38
  • 1
    Oddly, I just implemented `isinstance()` in my project and was momentarily tripped up until I realized that `isinstance(True, int)` yields `True`. – Mike O'Connor May 24 '16 at 08:57
  • @MikeO'Connor That’s because `True` is of type `bool` and `bool` is a subtype of `int`. – poke May 24 '16 at 10:19
  • @poke, I quite found that to be the case. My point was that you need to work around that if you want to distinguish between ints and bools; I did. So I had to add `'bool' in str(type())` to my Boolean expression. – Mike O'Connor May 26 '16 at 00:46
  • 1
    On the other hand, using type() in Python 2.x for instances of classic classes (not inheriting from object) is just plain useless, since the type is always instance. – Alan Franzoni Jun 17 '16 at 07:52
  • 4
    Example from CPython implentation: [`collections.namedtuple`](https://github.com/python/cpython/blob/e4c06bcca358c6dcb6393a75a1589ff6a2d45cde/Lib/collections/__init__.py#L398): `type(name) is str` as attribute names that are not strings (exactly) make no sense, and are probably an error. But to check for `int`s that are not `bool`s, you would do `isinstance(x, int) and not isinstance(x, bool)`, so as to not exclude `int` subclasses, but to exclude `bool`. Note, that `bool` is not subclassable, so `type(x) is not bool` works the same, but that would be inconsistent. – Artyer Jul 31 '17 at 20:36
  • this will not work if you are trying to output the type to a log... – Matthaeus Gaius Caesar Mar 27 '23 at 15:16
216

Use type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
brettkelly
  • 27,655
  • 8
  • 56
  • 72
44

It might be more Pythonic to use a try...except block. That way, if you have a class which quacks like a list, or quacks like a dict, it will behave properly regardless of what its type really is.

To clarify, the preferred method of "telling the difference" between variable types is with something called duck typing: as long as the methods (and return types) that a variable responds to are what your subroutine expects, treat it like what you expect it to be. For example, if you have a class that overloads the bracket operators with getattr and setattr, but uses some funny internal scheme, it would be appropriate for it to behave as a dictionary if that's what it's trying to emulate.

The other problem with the type(A) is type(B) checking is that if A is a subclass of B, it evaluates to false when, programmatically, you would hope it would be true. If an object is a subclass of a list, it should work like a list: checking the type as presented in the other answer will prevent this. (isinstance will work, however).

Seth Johnson
  • 14,762
  • 6
  • 59
  • 85
  • 16
    Duck typing isn't really about telling the difference, though. It is about using a common interface. – Justin Ethier Nov 18 '11 at 19:31
  • 6
    Be careful -- most coding style guides recommend not using exception handling as part of the normal control flow of code, usually because it makes code difficult to read. `try`... `except` is a good solution when you want to deal with errors, but not when deciding on behavior based on type. – Rens van der Heijden Mar 12 '16 at 12:02
37

On instances of object you also have the:

__class__

attribute. Here is a sample taken from Python 3.3 console

>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>

Beware that in python 3.x and in New-Style classes (aviable optionally from Python 2.6) class and type have been merged and this can sometime lead to unexpected results. Mainly for this reason my favorite way of testing types/classes is to the isinstance built in function.

Lorenzo Persichetti
  • 1,480
  • 15
  • 24
  • 2
    Your point at the end is very important. type(obj) is Class wasn't working correctly, but isinstance did the trick. I understand that isinstance is preferred anyway, but it's more beneficial than just checking derived types, as suggested in the accepted answer. – mstbaum Feb 24 '16 at 22:58
  • `__class__` is mostly OK on Python 2.x, the only objects in Python which don't have `__class__` attribute are old-style classes AFAIK. I don't understand your Python 3 concern, by the way - on such version, just every object has a `__class__` attribute that points to the proper class. – Alan Franzoni Jun 17 '16 at 08:38
24

Determine the type of a Python object

Determine the type of an object with type

>>> obj = object()
>>> type(obj)
<class 'object'>

Although it works, avoid double underscore attributes like __class__ - they're not semantically public, and, while perhaps not in this case, the builtin functions usually have better behavior.

>>> obj.__class__ # avoid this!
<class 'object'>

type checking

Is there a simple way to determine if a variable is a list, dictionary, or something else? I am getting an object back that may be either type and I need to be able to tell the difference.

Well that's a different question, don't use type - use isinstance:

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

This covers the case where your user might be doing something clever or sensible by subclassing str - according to the principle of Liskov Substitution, you want to be able to use subclass instances without breaking your code - and isinstance supports this.

Use Abstractions

Even better, you might look for a specific Abstract Base Class from collections or numbers:

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

Or Just Don't explicitly Type-check

Or, perhaps best of all, use duck-typing, and don't explicitly type-check your code. Duck-typing supports Liskov Substitution with more elegance and less verbosity.

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

Conclusion

  • Use type to actually get an instance's class.
  • Use isinstance to explicitly check for actual subclasses or registered abstractions.
  • And just avoid type-checking where it makes sense.
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
  • There's always `try`/`except` instead of checking explicitly. – toonarmycaptain Sep 29 '17 at 20:36
  • Presumably that's what the user will do if they aren't sure about the types they'll be passing in. I don't like to clutter a correct implementation with exception handling unless I have something very good to do with the exception. The exception raised should be enough to inform the user that they need to correct their usage. – Russia Must Remove Putin Sep 29 '17 at 22:43
  • Lovely reminder here about why not to use `__class__`, which, when the object is a `Mock` is the only way I could get the supposed type, as `type` would just say `Mock`. There is a much better assertion to be had by using the `return_value` of the mocked class, BTW. – John Jul 23 '22 at 18:01
13

You can use type() or isinstance().

>>> type([]) is list
True

Be warned that you can clobber list or any other type by assigning a variable in the current scope of the same name.

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

Above we see that dict gets reassigned to a string, therefore the test:

type({}) is dict

...fails.

To get around this and use type() more cautiously:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
deed02392
  • 4,799
  • 2
  • 31
  • 48
  • 2
    I'm not sure it's necessary to point out that shadowing the name of a builtin data type is bad for this case. Your `dict` string will also fail for lots of other code, like `dict([("key1", "value1"), ("key2", "value2")])`. The answer for those kinds of issues is ["Then don't do that"](http://www.youtube.com/watch?v=zCh7z5EwYF8). Don't shadow builtin type names and expect things to work properly. – Blckknght May 31 '14 at 22:12
  • 3
    I agree with you on the "don't do that" part. But indeed to tell someone not to do something you should at least explain why not and I figured this was a relevant opportunity to do just that. I meant for the cautious method to look ugly and illustrate why they might not want to do it, leaving them to decide. – deed02392 Jun 04 '14 at 15:39
  • type() doesn't work as expected on Python 2.x for classic instances. – Alan Franzoni Jun 17 '16 at 08:35
8

be careful using isinstance

isinstance(True, bool)
True
>>> isinstance(True, int)
True

but type

type(True) == bool
True
>>> type(True) == int
False
tnusraddinov
  • 660
  • 7
  • 13
6

using type()

x='hello this is a string'
print(type(x))

output

<class 'str'>

to extract only the str use this

x='this is a string'
print(type(x).__name__)#you can use__name__to find class

output

str

if you use type(variable).__name__ it can be read by us

ItsMe
  • 395
  • 2
  • 13
5

While the questions is pretty old, I stumbled across this while finding out a proper way myself, and I think it still needs clarifying, at least for Python 2.x (did not check on Python 3, but since the issue arises with classic classes which are gone on such version, it probably doesn't matter).

Here I'm trying to answer the title's question: how can I determine the type of an arbitrary object? Other suggestions about using or not using isinstance are fine in many comments and answers, but I'm not addressing those concerns.

The main issue with the type() approach is that it doesn't work properly with old-style instances:

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

Executing this snippet would yield:

Are o and t instances of the same class? True

Which, I argue, is not what most people would expect.

The __class__ approach is the most close to correctness, but it won't work in one crucial case: when the passed-in object is an old-style class (not an instance!), since those objects lack such attribute.

This is the smallest snippet of code I could think of that satisfies such legitimate question in a consistent fashion:

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type
Alan Franzoni
  • 3,041
  • 1
  • 23
  • 35
5

In many practical cases instead of using type or isinstance you can also use @functools.singledispatch, which is used to define generic functions (function composed of multiple functions implementing the same operation for different types).

In other words, you would want to use it when you have a code like the following:

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

Here is a small example of how it works:

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>

Additionaly we can use abstract classes to cover several types at once:

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!
Georgy
  • 12,464
  • 7
  • 65
  • 73
3

As an aside to the previous answers, it's worth mentioning the existence of collections.abc which contains several abstract base classes (ABCs) that complement duck-typing.

For example, instead of explicitly checking if something is a list with:

isinstance(my_obj, list)

you could, if you're only interested in seeing if the object you have allows getting items, use collections.abc.Sequence:

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

if you're strictly interested in objects that allow getting, setting and deleting items (i.e mutable sequences), you'd opt for collections.abc.MutableSequence.

Many other ABCs are defined there, Mapping for objects that can be used as maps, Iterable, Callable, et cetera. A full list of all these can be seen in the documentation for collections.abc.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
3
value = 12
print(type(value)) # will return <class 'int'> (means integer)

or you can do something like this

value = 12
print(type(value) == int) # will return true
Yash Marmat
  • 1,077
  • 10
  • 18
1

type() is a better solution than isinstance(), particularly for booleans:

True and False are just keywords that mean 1 and 0 in python. Thus,

isinstance(True, int)

and

isinstance(False, int)

both return True. Both booleans are an instance of an integer. type(), however, is more clever:

type(True) == int

returns False.

Alec
  • 8,529
  • 8
  • 37
  • 63
1

In general you can extract a string from object with the class name,

str_class = object.__class__.__name__

and using it for comparison,

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..
1

For the sake of completeness, isinstance will not work for type checking of a subtype that is not an instance. While that makes perfect sense, none of the answers (including the accepted one) covers it. Use issubclass for that.

>>> class a(list):
...   pass
... 
>>> isinstance(a, list)
False
>>> issubclass(a, list)
True
Adnan Y
  • 2,982
  • 1
  • 26
  • 29