Python > Advanced Python Concepts > Type Hinting > Syntax of Type Hints

Advanced Type Hinting: Generics and Union Types

This snippet demonstrates more advanced type hinting features, including using generics to specify the types of elements within collections and using Union to indicate that a variable can hold values of different types.

Code Example

This code shows the use of `List`, `Dict`, `Union`, and `Optional` from the `typing` module. * `List[Dict[str, Union[int, str]]]` indicates a list of dictionaries where keys are strings and values can be either integers or strings. * `Union[int, str]` allows a variable to hold either an integer or a string value. * `Optional[str]` means that the variable can either be a string or `None`, simplifying the usage of `Union[str, None]`.

from typing import List, Dict, Union, Optional

def process_data(data: List[Dict[str, Union[int, str]]]) -> List[str]:
    """Processes a list of dictionaries and returns a list of strings."""
    results: List[str] = []
    for item in data:
        name = item.get("name")
        value = item.get("value")
        if isinstance(name, str) and isinstance(value, (int, str)):
            results.append(f"{name}: {value}")
    return results


my_data: List[Dict[str, Union[int, str]]] = [
    {"name": "age", "value": 30},
    {"name": "city", "value": "New York"},
    {"name": "zip", "value": 10001}
]

print(process_data(my_data))


def greet_optional(name: Optional[str] = None) -> str:
    if name:
        return f"Hello, {name}!"
    else:
        return "Hello, guest!"

print(greet_optional())
print(greet_optional("Bob"))

Concepts Behind the Snippet

Generics and Union types enhance the expressiveness of type hints. Generics allow you to specify the types of elements within collections (e.g., the type of items in a list). Union types allow you to specify that a variable can hold a value of one of several different types. `Optional[T]` is shorthand for `Union[T, None]`.

Real-Life Use Case

Consider a function that can accept data from different sources, where each source provides data in a slightly different format. Union types can be used to specify the possible types of input data. Generics are useful when dealing with collections of data that are consistently typed, e.g., a list of user objects from a database.

Best Practices

  • Use generics to provide precise type information for collections.
  • Use Union types when a variable can genuinely hold values of different types. Avoid using Union simply to avoid being specific.
  • Use Optional for function parameters which can be None.

Interview Tip

Be prepared to explain the difference between generics and Union types, and when each should be used. Understand the meaning of `Optional` and how it relates to `Union`.

When to Use Them

Use generics and Union types when you need to specify more complex type constraints than basic types can provide. These are important for creating robust and maintainable code.

Alternatives

Without generics and Union types, you would rely on `Any` type (which disables type checking) or create more complex type hierarchies. `Any` should be avoided where possible as it bypasses type checking.

Pros

  • More expressive and precise type hints.
  • Improved static analysis capabilities.
  • Reduced likelihood of runtime type errors.

Cons

  • Slightly more complex syntax to learn.
  • Can make code slightly more verbose.

FAQ

  • What is the difference between `Union` and `Any`?

    `Union` specifies that a variable can hold one of a specific set of types, while `Any` means that the variable can hold a value of any type. `Any` effectively disables type checking for that variable, while `Union` still provides some type safety.
  • When should I use `Optional` instead of `Union[T, None]`?

    `Optional[T]` is a shorthand notation for `Union[T, None]`. Using `Optional[T]` is generally preferred for readability and conciseness when specifying that a variable can be either of type `T` or `None`.