Python: Typing a function that can return multiple types

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

Introduction

Python, as a dynamically typed language, offers significant flexibility regarding variable types. A function in Python can return different types of data, making it versatile but challenging for type checking and code clarity. Fortunately, the typing module in Python 3.5 and later versions provides tools to declare the types of variables, function arguments, and return values, even for functions with multiple return types.

This tutorial covers how to use Python’s typing module to specify types for functions that can return multiple types. We’ll discuss different strategies such as using Union, Any, Optional, and the newer Literal and TypedDict for Python 3.8 and later, including practical examples to illustrate their use.

Understanding Static Typing in Python

Before diving into the specifics of typing a function with multiple return types, it’s essential to understand the concept of static typing in Python. Static typing involves explicitly specifying the type of a variable at compile time, allowing for type checking before runtime. This is opposed to Python’s default dynamic typing, where types are inferred at runtime. Python’s typing module allows for a hybrid approach, adding static type hints without sacrificing Python’s dynamic nature.

Using Union for Multiple Return Types

The Union type hint from the typing module lets you specify that a function may return more than one type. Consider the following example:

from typing import Union
def get_data(flag: bool) -> Union[int, str]:
    if flag:
        return 34
    else:
        return "Not an integer"

This function returns either an integer or a string, based on the flag parameter.

Using Any When Any Type is Possible

When a function can return elements of any type, the Any type hint indicates a return value of any type. Here’s how you might use Any:

from typing import Any
def get_anything(flag: int) -> Any:
    if flag == 1:
        return 42
    elif flag == 2:
        return "The answer"
    else:
        return None

The Role of Optional in Typing

The Optional type hint implies that a function may return a certain type or None. It is a shorthand for Union[T, None], useful for functions that may not return a value:

from typing import Optional
def maybe_return(flag: bool) -> Optional[int]:
    if flag:
        return 123
    else:
        return None

Exploring Newer Typing Options

Python 3.8 introduced Literal and TypedDict, offering more granularity in specifying function return types. Literal allows specifying literal values a function can return, while TypedDict enables detailing the types of keys and values a dictionary function may return. See the examples below for how to use these in your code:

from typing import Literal, TypedDict
class NumbersDict(TypedDict):
    number: int
    description: str
def get_specific_value(flag: int) -> Literal['yes', 'no', 'maybe']:
    if flag == 1:
        return 'yes'
    elif flag == 2:
        return 'no'
    else:
        return 'maybe'

def get_number_info() -> NumbersDict:
    return {'number': 42, 'description': 'The ultimate answer'}

Best Practices for Typing

While the typing module offers great flexibility, it’s crucial to use type hints appropriately to maintain code readability and avoid confusion. Here are some best practices to follow:

  • Use specific type hints whenever possible to enhance code clarity and facilitate type checking.
  • Leverage Union, Optional, and Any strategically, avoiding overuse that can lead to ambiguity.
  • Stay updated with the latest Python versions for more advanced typing features that can further improve your code.

Conclusion

This tutorial introduced you to typing functions with multiple return types in Python, using the typing module. Through examples featuring Union, Any, Optional, Literal, and TypedDict, we demonstrated how to add static type hints to your Python code. By embracing these techniques, you can improve your code’s readability, robustness, and ease of maintenance.