Skip to main content

How to Validate Sequence Types (Ordered Collections) in Python

In Python, a "sequence" is a generic term for ordered collections like lists, tuples, strings, and ranges. Often, a function is designed to work with any sequence but fails if passed a non-sequence object (like an integer or a dictionary).

This guide explores how to validate sequence types robustly using isinstance, Abstract Base Classes (ABCs), and duck typing.

Method 1: Using isinstance() (Standard Check)

The most direct way to check if an object is a sequence is to verify if it is an instance of list, tuple, str, or range.

def validate_sequence(data):
# Pass a tuple of types to check against ANY of them
if isinstance(data, (list, tuple, str, range)):
return True
return False

# Test Cases
print(f"List valid? {validate_sequence([1, 2])}")
print(f"Int valid? {validate_sequence(42)}")

Output:

List valid? True
Int valid? False
note

This method is explicit but brittle. If you use a custom class that behaves like a sequence but doesn't inherit from these types, validation will fail.

Method 2: Using collections.abc.Sequence (Robust Check)

The standard library provides Abstract Base Classes (ABCs) that define what a sequence is (supports indexing, length, iteration). Checking against Sequence is the most "correct" way to support lists, tuples, strings, and custom sequence-like objects.

from collections.abc import Sequence

def is_sequence(obj):
return isinstance(obj, Sequence)

# Test Cases
print(f"Tuple: {is_sequence((1, 2))}")
print(f"String: {is_sequence('Hello')}")
print(f"Dict: {is_sequence({'a': 1})}") # Dict is Mapping, not Sequence

Output:

Tuple: True
String: True
Dict: False

Method 3: Duck Typing (Behavioral Check)

"If it walks like a duck and quacks like a duck, it's a duck." Instead of checking types, you check if the object behaves like a sequence (i.e., has __iter__, __len__, and __getitem__).

def is_sequence_duck(obj):
try:
# Check required behaviors
_ = len(obj)
_ = obj[0] # Try indexing
iter(obj)
return True
except (TypeError, IndexError, KeyError):
return False

print(f"List: {is_sequence_duck([1, 2])}")
print(f"Int: {is_sequence_duck(10)}")

Output:

List: True
Int: False

Advanced: Using Validation Decorators

To keep your code DRY (Don't Repeat Yourself), you can create a decorator that enforces sequence validation on function arguments.

from collections.abc import Sequence
import functools

def require_sequence(func):
@functools.wraps(func)
def wrapper(arg, *args, **kwargs):
if not isinstance(arg, Sequence):
raise TypeError(f"Argument must be a sequence, got {type(arg).__name__}")
return func(arg, *args, **kwargs)
return wrapper

@require_sequence
def process_data(data):
print(f"Processing {len(data)} items...")

# ✅ Valid
process_data([1, 2, 3])

# ⛔️ Invalid
try:
process_data(100)
except TypeError as e:
print(e)

Output:

Processing 3 items...
Argument must be a sequence, got int

Conclusion

To validate sequence types in Python:

  1. Use isinstance(obj, (list, tuple)) for quick, simple scripts.
  2. Use isinstance(obj, collections.abc.Sequence) for robust, professional code that supports all sequence types (including custom ones).
  3. Use Duck Typing if you prefer validating behavior over type definitions.