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
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:
- Use
isinstance(obj, (list, tuple))for quick, simple scripts. - Use
isinstance(obj, collections.abc.Sequence)for robust, professional code that supports all sequence types (including custom ones). - Use Duck Typing if you prefer validating behavior over type definitions.