Claude
Skills
Sign in
Back

python-type-system

Included with Lifetime
$97 forever

Use when Python's type system including type hints, mypy, Protocol, TypedDict, and Generics. Use when working with Python type annotations.

General

What this skill does


# Python Type System

Master Python's type system to write type-safe, maintainable code. This
skill covers type hints, static type checking with mypy, and advanced
typing features.

## Type Checking Tools

```bash
# Install mypy for static type checking
pip install mypy

# Run mypy on a file or directory
mypy my_module.py
mypy src/

# Run with specific configuration
mypy --config-file mypy.ini src/

# Run with strict mode
mypy --strict src/

# Show type coverage report
mypy --html-report mypy-report src/
```

## mypy Configuration

**mypy.ini configuration file:**

```ini
[mypy]
# Global options
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_any_unimported = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_no_return = True
check_untyped_defs = True
strict_equality = True

# Per-module options
[mypy-tests.*]
disallow_untyped_defs = False

[mypy-third_party.*]
ignore_missing_imports = True
```

**pyproject.toml configuration:**

```toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_any_unimported = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
check_untyped_defs = true
strict_equality = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
```

## Basic Type Hints

**Primitive types and collections:**

```python
from typing import List, Dict, Set, Tuple, Optional, Union, Any

# Basic types
def greet(name: str) -> str:
    return f"Hello, {name}"

# Collections
def process_items(items: List[str]) -> Dict[str, int]:
    return {item: len(item) for item in items}

# Optional (can be None)
def find_user(user_id: int) -> Optional[str]:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

# Union types (multiple possible types)
def process_value(value: Union[int, str]) -> str:
    return str(value)

# Tuple with fixed types
def get_coordinates() -> Tuple[float, float]:
    return (37.7749, -122.4194)

# Any type (avoid when possible)
def process_data(data: Any) -> None:
    print(data)
```

## Modern Type Syntax (Python 3.10+)

**Using PEP 604 union syntax:**

```python
# Python 3.10+ union syntax with |
def process_value(value: int | str) -> str:
    return str(value)

# Optional with | None
def find_user(user_id: int) -> str | None:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

# Multiple unions
def handle_response(
    response: dict | list | str | None
) -> str:
    if response is None:
        return "No response"
    return str(response)
```

**Built-in generic types (Python 3.9+):**

```python
# Use built-in types instead of typing module
def process_items(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

def get_mapping() -> dict[str, list[int]]:
    return {"numbers": [1, 2, 3]}

def get_unique(items: list[str]) -> set[str]:
    return set(items)

# Nested generics
def group_items(
    items: list[tuple[str, int]]
) -> dict[str, list[int]]:
    result: dict[str, list[int]] = {}
    for key, value in items:
        result.setdefault(key, []).append(value)
    return result
```

## Generic Types

**Creating generic functions and classes:**

```python
from typing import TypeVar, Generic, Sequence

# Type variable
T = TypeVar("T")

def first(items: Sequence[T]) -> T | None:
    return items[0] if items else None

def last(items: list[T]) -> T | None:
    return items[-1] if items else None

# Constrained type variable
Number = TypeVar("Number", int, float)

def add(a: Number, b: Number) -> Number:
    return a + b  # type: ignore

# Generic class
class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

    def peek(self) -> T | None:
        return self._items[-1] if self._items else None

# Usage
stack: Stack[int] = Stack()
stack.push(1)
stack.push(2)
value: int = stack.pop()
```

**Bound type variables:**

```python
from typing import TypeVar
from collections.abc import Sized

# Type variable with upper bound
TSized = TypeVar("TSized", bound=Sized)

def get_length(obj: TSized) -> int:
    return len(obj)

# Works with any Sized type
get_length("hello")
get_length([1, 2, 3])
get_length({"a": 1})
```

## Protocol (Structural Subtyping)

**Define interfaces using Protocol:**

```python
from typing import Protocol

# Define a protocol
class Drawable(Protocol):
    def draw(self) -> str:
        ...

# Classes that match the protocol don't need inheritance
class Circle:
    def draw(self) -> str:
        return "Drawing circle"

class Square:
    def draw(self) -> str:
        return "Drawing square"

# Function accepts any type matching the protocol
def render(shape: Drawable) -> None:
    print(shape.draw())

# Works with any matching class
render(Circle())
render(Square())
```

**Protocol with properties and methods:**

```python
from typing import Protocol

class Comparable(Protocol):
    def __lt__(self, other: "Comparable") -> bool:
        ...

    def __gt__(self, other: "Comparable") -> bool:
        ...

def find_max(items: list[Comparable]) -> Comparable:
    return max(items)

class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

    def __lt__(self, other: "Person") -> bool:
        return self.age < other.age

    def __gt__(self, other: "Person") -> bool:
        return self.age > other.age

# Works because Person implements Comparable protocol
people = [Person("Alice", 30), Person("Bob", 25)]
oldest = find_max(people)
```

**Runtime checkable protocols:**

```python
from typing import Protocol, runtime_checkable

@runtime_checkable
class Serializable(Protocol):
    def to_dict(self) -> dict[str, Any]:
        ...

class User:
    def __init__(self, name: str) -> None:
        self.name = name

    def to_dict(self) -> dict[str, Any]:
        return {"name": self.name}

user = User("Alice")
assert isinstance(user, Serializable)
```

## TypedDict

**Define dictionary shapes with TypedDict:**

```python
from typing import TypedDict, NotRequired

# Basic TypedDict
class UserDict(TypedDict):
    id: int
    name: str
    email: str

def create_user(user: UserDict) -> UserDict:
    return user

user: UserDict = {
    "id": 1,
    "name": "Alice",
    "email": "[email protected]"
}

# Optional fields (Python 3.11+)
class PersonDict(TypedDict):
    name: str
    age: int
    email: NotRequired[str]  # Optional field

person: PersonDict = {"name": "Bob", "age": 30}

# Total=False makes all fields optional
class ConfigDict(TypedDict, total=False):
    host: str
    port: int
    debug: bool

config: ConfigDict = {"host": "localhost"}
```

**Inheritance with TypedDict:**

```python
from typing import TypedDict

class BaseUserDict(TypedDict):
    id: int
    name: str

class ExtendedUserDict(BaseUserDict):
    email: str
    is_active: bool

user: ExtendedUserDict = {
    "id": 1,
    "name": "Alice",
    "email": "[email protected]",
    "is_active": True
}
```

## Literal Types

**Restrict values to specific literals:**

```python
from typing import Literal

def set_mode(mode: Literal["read", "write", "append"]) -> None:
    print(f"Mode set to {mode}")

# Valid
set_mode("read")

# Type error: not a valid literal
# set_mode("invalid")

# Literal unions
Status = Literal["pending", "active", "completed"]

def update_status(status: Status) -> None:
    print(f"Status: {status}")

# Literal with multiple types
MixedLiteral = Literal[True, 1, "one"]
```

## Type Aliases

**Create type aliases for complex types:**

```python
from typing import TypeAlias

# Type alias
UserId: TypeAlias = int
UserName: TypeAlias = str

def get_user(user_id: UserId) -> UserName:
    return f"User {user_id}"

# Compl

Related in General