Python: Defining type for a list that can contain both numbers and strings

Updated: February 17, 2024 By: Guest Contributor Post a comment

Introduction

In modern Python programming, especially with Python 3.5 and later versions, type hinting has become increasingly significant. Type hinting aids in the documentation of code and improves IDEs and linters’ ability to catch errors early. Recently, the need to define types for complex structures like lists that can contain both numbers and strings has become apparent. This tutorial will guide you through how to achieve this in Python.

First, let’s examine why you might need a list containing both numbers and strings. Suppose you have a dataset that includes measurements (as numbers) and their units (as strings) in the same list, or maybe your function is designed to process both numeric IDs and string names. In these cases, accurately hinting the list’s type becomes necessary but tricky.

Understanding Type Hinting

Before diving into mixed lists, let’s briefly go over what type hinting is. Type hinting allows you to indicate the expected data type of variables, function return values, and function arguments. For simple types like int or str, you could hint a variable like this:

x: int = 5
name: str = "Alice"

For lists containing elements of the same type, you would use:

from typing import List

numbers: List[int] = [1, 2, 3]
names: List[str] = ["Alice", "Bob", "Charlie"]

Typing Mixed Lists

To indicate a list that can contain both numbers and strings, Python’s typing system offers several solutions. The first and most straightforward way is to use Union from the typing module:

from typing import List, Union

mixed_list: List[Union[int, str]] = [1, "two", 3, "four"]

This signifies that each element in mixed_list can either be an int or a str.

An Advanced Approach

For more complex scenarios where you might have to indicate additional types in the list or ensure compatibility with future versions of Python, the from __future__ import annotations can be used alongside list type directly instead of List from typing. Combined with Union, you could define a similar mixed list as:

from __future__ import annotations
from typing import Union

mixed_list: list[int | str] = [1, "3", 45, "eight"]

This syntax, which utilizes the vertical bar (|) for union types, is cleaner and is the recommended approach in Python 3.10 and later.

Practical Example

Let’s consider a practical example where mixed lists might be utilized. Imagine you’re writing a function that processes a list of items where each item could either be a product ID (an integer) or a product name (a string). Your function needs to differentiate between IDs and names to correctly handle the item. Here’s how you could hint and implement such a function:

def process_items(items: list[int | str]) -> None:
    for item in items:
        if isinstance(item, int):
            print(f"Processing ID {item}")
        else:
            print(f"Processing name '{item}'")

process_items([101, "Widget", 202, "Gadget"])

This example demonstrates the function’s ability to dynamically handle both integer IDs and string names, operating differently based on the item’s type.

Limitations and Considerations

While type hinting for mixed lists enhances code clarify and linting accuracy, it’s important to remember that Python’s runtime does not enforce these types. They are primarily for static analysis tools and to aid developers in understanding the code. Hence, always include runtime checks (like isinstance()) when the precise type of list elements affects program behavior.

Moreover, using mixed types in a list can sometimes be a sign of poor data structuring. Always evaluate if there’s a more suitable data structure or design pattern for your needs.

Conclusion

In conclusion, accurately typing a list that can contain both numbers and strings in Python is straightforward with the use of Union in combination with List or the newer syntax available in Python 3.10+. This capability allows for better-code documentation and analysis, helping to catch potential issues early in the development process. While powerful, remember to use mixed types judiciously and consider the implications on code clarity and structure.