Short introduction to Type Hints in Python

Problem

def resolve_all(items):
    for item in items:
        item.case.resolve()


def cancel_all(items):
    resolve_all(items)
    notify_all(items)
    ...
  • What are the items? List, dict, something else? What is the particular item - an object? What does it represent?
  • Does it always have `case` attribute?
  • Are we sure that items are passed correctly? Maybe they're `None`?

Potential solutions

  1. External documentation
  2. Internal documentation (comments, explicit naming)
  3. Being very careful (defensive programming)
  4. Type hints

Type hints

  1. PEP 484 -- Type Hints
  2. PEP 526 -- Syntax for Variable Annotations

Examples

def notify(who: User, message: str = 'Default message') -> None:
    ...


def count_chars(text: str) -> Dict[str, int]:
    ...
    

class ExampleClass:
    text: str = 'Some text'
    number: int

    def __init__(self, number: int, text: str = ''):
        self.number = number
        if text:
            self.text = text

Benefits

  1. Static type checking - less bugs in production
  2. Better discoverability & readability
  3. We can treat them as additional documentation
  4. Improved IDE experience
  5. Dataclasses - PEP 557

 

Dataclass example

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Tooling

from typing import Dict, List, Optional


class ExampleClass:
    text: str = 'Some text'
    number: int

    def __init__(self, number: int, text: str = ''):
        self.number = number
        if text:
            self.text = text

    def iterate(self, vals: Optional[List[int]]) -> None:
        for val in vals:
            print(val)


def notify(who: Optional[ExampleClass], message: str = 'Default message') -> None:
    return who.notify()


ex = ExampleClass(111)

ex.nonexistent

notify(ex)

another = ExampleClass('123')

> mypy ex1.py
ex1.py:14: error: Item "None" of "Optional[List[int]]" has no attribute "__iter__" (not iterable)
ex1.py:19: error: Item "ExampleClass" of "Optional[ExampleClass]" has no attribute "notify"
ex1.py:19: error: Item "None" of "Optional[ExampleClass]" has no attribute "notify"
ex1.py:24: error: "ExampleClass" has no attribute "nonexistent"
ex1.py:28: error: Argument 1 to "ExampleClass" has incompatible type "str"; expected "int"

Short introduction to Type Hints in Python

By progressive

Short introduction to Type Hints in Python

  • 560