Python
Programming
Data Types
Lists
Tuples

Test if a variable is a list or tuple

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

If you want to know whether a Python value is specifically a list or a tuple, the usual answer is isinstance(value, (list, tuple)). That check is simple, readable, and handles subclasses correctly. The harder part is deciding whether you truly want those two concrete types or whether you actually want to accept any sequence-like object.

The Basic Check

Python's isinstance accepts a tuple of types, so checking for either list or tuple is direct:

python
1def is_list_or_tuple(value):
2    return isinstance(value, (list, tuple))
3
4
5print(is_list_or_tuple([1, 2, 3]))
6print(is_list_or_tuple((1, 2, 3)))
7print(is_list_or_tuple("abc"))

This prints:

text
True
True
False

That is the idiomatic answer when the question is literally about lists and tuples.

Why type(value) in (...) Is Usually Worse

You could write:

python
def is_exact_list_or_tuple(value):
    return type(value) in (list, tuple)

But that is stricter. It returns False for subclasses:

python
1class MyList(list):
2    pass
3
4
5value = MyList([1, 2, 3])
6print(isinstance(value, list))
7print(type(value) is list)

In most Python code, subclass-friendly behavior is desirable, so isinstance is preferred.

When You Might Want a Broader Check

Sometimes "list or tuple" is just shorthand for "something sequence-like." If that is the real requirement, checking only those two types can be too narrow.

For example, these are sequence-like in many contexts:

  • 'range'
  • 'array.array'
  • 'numpy.ndarray'
  • custom sequence classes

If your function only needs indexing and length, you may want collections.abc.Sequence instead:

python
1from collections.abc import Sequence
2
3def is_sequence(value):
4    return isinstance(value, Sequence)
5
6
7print(is_sequence([1, 2, 3]))
8print(is_sequence((1, 2, 3)))
9print(is_sequence(range(5)))
10print(is_sequence("abc"))

Notice the last line: strings are also sequences. That is often not what you want.

Excluding Strings and Bytes

When developers broaden a check to "sequence," they often accidentally accept text types. If a string should not count as a list-like input, exclude it explicitly.

python
1from collections.abc import Sequence
2
3def is_non_string_sequence(value):
4    return isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray))
5
6
7print(is_non_string_sequence([1, 2]))
8print(is_non_string_sequence((1, 2)))
9print(is_non_string_sequence("12"))

This is a more useful check for many data-processing functions than the narrower list-or-tuple test.

Match the Check to the Intent

A good rule is:

  • use (list, tuple) when the code truly depends on those concrete container types
  • use a sequence protocol when the code depends on sequence behavior

For example, if your function mutates the input with append, then tuple input is not actually acceptable. In that case, checking for (list, tuple) is already too broad.

python
1def add_item(values, item):
2    if not isinstance(values, list):
3        raise TypeError("values must be a list")
4    values.append(item)
5
6
7items = [1, 2]
8add_item(items, 3)
9print(items)

This is clearer than pretending tuples are supported when they are not.

Duck Typing Versus Type Checking

Python often encourages duck typing: try to use the object in the required way instead of checking its type first. That can be a better design when the operation is simple.

python
1def first_item(value):
2    try:
3        return value[0]
4    except (TypeError, IndexError, KeyError):
5        return None
6
7
8print(first_item([10, 20]))
9print(first_item((10, 20)))
10print(first_item("hi"))

This approach is flexible, but it should be used intentionally. If the function contract really is "must be list or tuple," say so with isinstance.

Practical Recommendation

If the question came from a bug or validation rule, start by clarifying the real input contract. Many type checks are symptoms of an imprecise API boundary.

For most direct answers, this is enough:

python
1if isinstance(value, (list, tuple)):
2    print("accepted")
3else:
4    print("rejected")

Just be sure that concrete-type check matches what the rest of the function actually expects.

Common Pitfalls

  • Using type(value) is list when subclass-friendly behavior is desired.
  • Checking for list or tuple when the code really accepts any sequence-like object.
  • Broadening the check to Sequence and accidentally accepting strings.
  • Accepting tuples in validation even though the function later mutates the container.
  • Adding type checks where duck typing would make the interface simpler.

Summary

  • 'isinstance(value, (list, tuple)) is the standard way to test for either type.'
  • Prefer isinstance over direct type(...) comparisons in most cases.
  • Consider collections.abc.Sequence if the real requirement is broader than just list and tuple.
  • Exclude str and bytes explicitly when sequence checks should not treat text as list-like.
  • Make sure the type check matches what the function actually does with the value.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.