Skip to content

How to Split a Heterogeneous Type List in Python

Learn how to separate mixed-type Python lists into homogeneous groups by data type using list comprehensions, defaultdict, or filter functions for efficient data processing.

Python lists can hold elements of different data types: integers, strings, floats, and more, all in the same list. When processing such mixed-type lists, you often need to separate elements by their type into individual homogeneous lists for type-specific operations like arithmetic on numbers or string manipulation on text.

In this guide, you will learn multiple methods to split a heterogeneous list by data type, from clean Pythonic approaches to flexible solutions that handle any number of types.

Understanding the Problem

Given a list containing mixed data types:

data = ["hello", 1, 2.5, "world", 3, True, "python", 4.0]

Split it into separate lists based on type:

strings:  ["hello", "world", "python"]
integers: [1, 3]
floats:   [2.5, 4.0]
booleans: [True]

The most Pythonic approach uses list comprehensions to filter elements by type:

data = ["tutorialreference", 1, 2, "is", "best", 3.14]

str_list = [x for x in data if isinstance(x, str)]
int_list = [x for x in data if isinstance(x, int) and not isinstance(x, bool)]
float_list = [x for x in data if isinstance(x, float)]

print(f"Strings:  {str_list}")
print(f"Integers: {int_list}")
print(f"Floats:   {float_list}")

Output:

Strings:  ['tutorialreference', 'is', 'best']
Integers: [1, 2]
Floats:   [3.14]

Method 2: Using defaultdict for Automatic Type Grouping

The defaultdict approach is highly flexible; it automatically groups elements by their type without requiring you to know the types in advance:

from collections import defaultdict

data = ["tutorialreference", 1, 2, "is", "best", 3.14, True, (1, 2)]

grouped = defaultdict(list)
for item in data:
    grouped[type(item)].append(item)

for dtype, items in grouped.items():
    print(f"{dtype.__name__:>10}: {items}")

Output:

       str: ['tutorialreference', 'is', 'best']
       int: [1, 2]
     float: [3.14]
      bool: [True]
     tuple: [(1, 2)]

How it works:

  1. defaultdict(list) creates a dictionary where each missing key automatically initializes with an empty list.
  2. type(item) returns the exact type of each element (e.g., <class 'str'>).
  3. Each element is appended to the list corresponding to its type.

Accessing Specific Type Groups

from collections import defaultdict

data = ["hello", 1, 2.5, "world", 3, 4.0]

grouped = defaultdict(list)
for item in data:
    grouped[type(item)].append(item)

print("Strings:", grouped[str])
print("Integers:", grouped[int])
print("Floats:", grouped[float])
print("Missing type:", grouped[complex])  # Returns empty list (no error)

Output:

Strings: ['hello', 'world']
Integers: [1, 3]
Floats: [2.5, 4.0]
Missing type: []

Method 3: Using filter() with lambda

The filter() function provides a functional programming approach:

data = ["tutorialreference", 1, 2, "is", "best"]

int_list = list(filter(lambda x: isinstance(x, int), data))
str_list = list(filter(lambda x: isinstance(x, str), data))

print(f"Integers: {int_list}")
print(f"Strings:  {str_list}")

Output:

Integers: [1, 2]
Strings:  ['tutorialreference', 'is', 'best']

Method 4: Creating a Reusable Splitter Function

For production code, encapsulate the logic in a reusable function:

from collections import defaultdict

def split_by_type(data):
    """Split a heterogeneous list into groups by data type.

    Args:
        data: A list containing elements of mixed types.

    Returns:
        A dictionary mapping type names to lists of elements.
    """
    grouped = defaultdict(list)
    for item in data:
        grouped[type(item).__name__].append(item)
    return dict(grouped)


data = ["hello", 1, 2.5, "world", 3, True, None, (1, 2)]

result = split_by_type(data)

for type_name, items in result.items():
    print(f"{type_name:>10}: {items}")

Output:

       str: ['hello', 'world']
       int: [1, 3]
     float: [2.5]
      bool: [True]
  NoneType: [None]
     tuple: [(1, 2)]

Splitting into Specific Types Only

def split_into(data, *types):
    """Split a list into groups for specified types only.

    Args:
        data: Input list with mixed types.
        *types: Type classes to filter for.

    Returns:
        Tuple of lists, one per requested type, plus an 'other' list.
    """
    results = {t: [] for t in types}
    other = []

    for item in data:
        placed = False
        for t in types:
            if isinstance(item, t):
                results[t].append(item)
                placed = True
                break
        if not placed:
            other.append(item)

    return tuple(results[t] for t in types) + (other,)


data = ["hello", 1, 2.5, "world", 3, None, True]

strings, numbers, others = split_into(data, str, (int, float))

print(f"Strings: {strings}")
print(f"Numbers: {numbers}")
print(f"Others:  {others}")

Output:

Strings: ['hello', 'world']
Numbers: [1, 2.5, 3, True]
Others:  [None]

Common Mistake: Using type() Instead of isinstance()

A subtle but important distinction:

type() checks the exact type:

class MyInt(int):
    pass

val = MyInt(5)
print(type(val) == int)       # False: MyInt is not exactly int
print(isinstance(val, int))   # True: MyInt inherits from int

isinstance() respects inheritance, making it the better choice for type checking in most cases:

data = [1, True, 2, False]

by_type = [x for x in data if type(x) == int]
print(f"type() == int: {by_type}")  # [1, 2]: booleans are excluded

by_isinstance = [x for x in data if isinstance(x, int)]
print(f"isinstance(int): {by_isinstance}")  # [1, True, 2, False]: booleans are included

Output:

type() == int: [1, 2]
isinstance(int): [1, True, 2, False]

Choose based on your needs:

  • Use isinstance() when you want inclusive type checking (recommended for most cases).
  • Use type(x) == when you need exact type matching.

Performance Comparison

Method Time Complexity Handles Any Type Scales to N Types
List comprehension O(n × k) ✅ Yes ❌ One comprehension per type
defaultdict O(n) ✅ Yes ✅ Automatic
filter() + lambda O(n × k) ✅ Yes ❌ One filter per type
Reusable function O(n) ✅ Yes ✅ Configurable

Where n = list length, k = number of types to check.

Summary

Splitting a heterogeneous list by data type is a common task in Python data processing. Key takeaways:

  • Use list comprehension with isinstance() for splitting into 2-3 known types: clean and readable.
  • Use defaultdict(list) for automatic grouping by any number of types in a single pass: the most flexible and efficient approach.
  • Use filter() with lambda for a functional programming style.
  • Remember that bool is a subclass of int: add explicit bool checks if you need to separate them.
  • Prefer isinstance() over type() for type checking, as it respects inheritance.
  • Create a reusable function when you need this pattern frequently across your codebase.
Navigation