python type annotation

Python is well known as a dynamic typing language. This means that every value is checked at runtime without the need to explicitly specify the type of a variable. However, as large projects become more complex and multiple developers collaborate, understanding and maintaining the code can become difficult. To address this, Type Annotation was introduced in Python 3.5. Using type annotations helps improve code readability, prevent bugs, and enhance autocompletion features.

1. Basics of Type Annotation

Type annotation is the syntax that allows explicitly specifying the types of variables or functions. Here are the basic ways to annotate the types of variables and functions:

Variable Annotation:
x: int = 10
y: float = 10.5
name: str = "Alice"

Function Annotation:
def greeting(name: str) -> str:
    return "Hello " + name

1.1 Type Annotation of Variables

By specifying the type of a variable, the code writer can clearly indicate which type is expected. This allows for early error detection through static analysis in tooling and IDEs.

1.2 Annotations for Function Parameters and Return Values

Function annotations can explicitly specify the input and output types of a function, helping to anticipate what type of data the function will receive. This is very helpful during code reviews.

2. Built-in Data Types

Python supports various built-in data types, and these types can be used in annotations.

  • Basic types like int, float, str, bool, None, etc.
  • Container types like List, Dict, Set, Tuple can be further refined using the typing module.
from typing import List, Dict, Tuple

names: List[str] = ["Alice", "Bob", "Charlie"]
scores: Dict[str, int] = {"Alice": 95, "Bob": 85}
position: Tuple[int, int] = (10, 20)

3. Union and Optional

When multiple types are allowed, it is common to use Union, and when None is allowed, Optional is used.

from typing import Union, Optional

value: Union[int, float] = 5.5

def get_user(id: int) -> Optional[Dict[str, str]]:
    if id == 1:
        return {"name": "Alice", "role": "admin"}
    return None

4. User-defined Types

When you need to define complex types, using Type or NewType allows you to write clearer code.

from typing import NewType

UserID = NewType('UserID', int)
admin_user_id: UserID = UserID(524313)

4.1 Type Alias

Using type aliases allows you to express complex type structures with concise names.

Vector = List[float]

def normalize(vec: Vector) -> Vector:
    magnitude = sum(x**2 for x in vec) ** 0.5
    return [x / magnitude for x in vec]

5. Generic Types

Using generic types allows a single function or class to work with multiple types. You can define generic types using the typing.Generic class.

from typing import TypeVar, Generic

T = TypeVar('T')

class Box(Generic[T]):
    def __init__(self, content: T) -> None:
        self.content = content

int_box = Box(123)
str_box = Box("hello")

6. Advanced Example

Here is a slightly more complex example utilizing type annotations.

from typing import List, Dict, Union, Callable

def process_data(data: List[Union[int, float, str]]) -> Dict[str, Union[int, float]]:
    result: Dict[str, Union[int, float]] = {'total': 0, 'numeric_count': 0}

    def is_number(val: Union[int, float, str]) -> bool:
        return isinstance(val, (int, float))

    for item in data:
        if is_number(item):
            result['total'] += item  # Prevents type warnings.
            result['numeric_count'] += 1

    return result

mixed_data: List[Union[int, float, str]] = [10, '20', 30.5, 'forty', '60', 70.2]
output = process_data(mixed_data)
print(output)
# {'total': 110.7, 'numeric_count': 3}

7. Static Type Checking Tools

Type annotations are most useful when used with static type checking tools. In Python, tools like mypy, Pyright, and Pylance are widely used.

For example, mypy is used as follows:

mypy script.py

These tools are very effective in checking the type consistency of the code and preventing unexpected type errors.

8. Conclusion

Type annotation is a powerful feature of Python that greatly helps improve code readability, ease maintenance, and prevent errors early. Additionally, when combined with static analysis tools, it provides greater stability for large projects. Through this tutorial, I hope you will be able to effectively utilize type annotations and write more robust Python code.