Supprimer Rendre public Rendre privé Add tags Delete tags
  Ajouter un tag   Annuler
  Supprimer le tag   Annuler
  • • DevOps notes •
  •  
  • Tags
  • Connexion

Generics typing/shaare/8cGpBA

  • python
  • python

Introduction to Generics

  • Generic types let you write reusable, type-safe functions and classes that work uniformly across different data types.
  • They preserve the relationship between input and output types, enabling MyPy to infer precise types instead of falling back to Any.
  • The typing module’s TypeVar and Generic primitives unlock this capability.

The Need for Generics

  • Annotating with Any sacrifices type information, so tools cannot guarantee correct usage of returned values.
  • A generic abstraction retains knowledge of the specific type in each context, improving IDE support and static checks.
  • For example, a "first-item" function should return str for a list[str] and int for a list[int], not just Any.

Defining Type Variables

  • T = TypeVar('T') declares a placeholder type variable T that can stand for any type.
  • A function annotated def get_first_item_generic(data: List[T]) -> Optional[T]: returns an element of the same type as the list elements.
  • MyPy infers T from each call site, preserving specific return types like Optional[str] or Optional[int].

Constrained Type Variables

  • When a generic should only accept certain types, constrain it: NumberType = TypeVar('NumberType', int, float).
  • Functions like def add_generic_numbers(x: NumberType, y: NumberType) -> NumberType: then only accept int or float and return that same type.
  • Constrained type variables combine flexibility with necessary restrictions for safe operations.

Bounded Type Variables

  • When a generic should only accept subclasses of a specific superclass, we can use a type bound, constrain it: NumberType = TypeVar('NumberType', bound=Superclass).
  • Functions like def add_generic_numbers(x: NumberType, y: NumberType) -> NumberType: then accept any subclass of Superclass, and they can be different subclasses for each argument.
  • Like constrained type variables, bounded type variables provide useful functionalities combining flexibility and type safety.

Generic Classes

  • Inherit from Generic[T] to define a class parameterized by a type variable T.
  • A class like SimpleStack[T] can push, pop, and peek items of type T, and MyPy will enforce that only T instances are used.
  • This pattern creates custom container types that maintain strong type guarantees for their contents.

Common Pitfalls & How to Avoid Them

  • A class that uses T in its methods but does not inherit from Generic[T] is not recognized as generic by MyPy.
  • Unconstrained TypeVar('T') can degrade type safety when operations require certain capabilities—use bounds or explicit type lists when appropriate.
from typing import Optional, TypeVar, Generic

# Section: Defining a generic function to get the first item of a list

T = TypeVar("T")

def get_first_item(
    input_list: list[T],
) -> Optional[T]:
    if input_list:
        return input_list[0]

    return None

first_number = get_first_item([1, 2, 3])
first_str = get_first_item(["abc", "def"])
first_mixed_list = get_first_item(["abc", "def", 1, 2, 3])

# Section: Constrained TypeVar for numeric addition

NumberType = TypeVar("NumberType", int, float)

def add_generic_numbers(
    x: NumberType, y: NumberType
) -> NumberType:
    return x + y

sum_int = add_generic_numbers(3, 5.0)

# Section: Bounded TypeVar with deployed filter for DevOps resources

class CloudResource:
    def __init__(self, name: str, cpu_usage: float) -> None:
        self.name = name
        self.cpu_usage = cpu_usage
        self.deployed: bool = False

    def deploy(self) -> None:
        print(f"Deploying {self.name}")
        self.deployed = True

class VirtualMachine(CloudResource):
    def reboot(self) -> None:
        print(f"Rebooting VM {self.name}")

class DockerContainer(CloudResource):
    def restart(self) -> None:
        print(f"Restarting container {self.name}")

ResourceType = TypeVar("ResourceType", bound=CloudResource)

def filter_deployed(
    resources: list[ResourceType],
) -> list[ResourceType]:
    return [
        resource for resource in resources if resource.deployed
    ]

vm1 = VirtualMachine("vm-01", cpu_usage=65.0)
vm2 = VirtualMachine("vm-02", cpu_usage=45.0)
container1 = DockerContainer("api-service", cpu_usage=85.0)
container2 = DockerContainer("worker", cpu_usage=55.0)

vm1.deploy()
container1.deploy()

all_resources = [vm1, vm2, container1, container2]
deployed_resources = filter_deployed(all_resources)

# Section: Generic class SimpleStack

G = TypeVar("G")

class SimpleStack(Generic[G]):
    def __init__(self) -> None:
        self._items: list[G] = []

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

    def pop(self) -> G:
        if self.is_empty():
            raise IndexError("Stack is empty!")
        return self._items.pop()

    def peek(self) -> Optional[G]:
        if self.is_empty():
            return None

        return self._items[-1]

    def is_empty(self) -> bool:
        return not self._items

str_stack = SimpleStack[str](http://)
str_stack.push("str")

int_stack = SimpleStack[int](http://)
int_stack.push(12)
3 months ago Permalien
cluster icon
  • Range, zip : Efficient Looping: range Creating large lists for loops is memory-intensive (e.g., list(range(1_000_000))). range() stores only start, stop, and step...
  • Functions: return vs yield : Functions: return vs yield Regular functions execute immediately, run to completion, and return a single value (or None). Generator functions retur...
  • Python package and subpackage : Introduction to Packages (__init__.py) What is a Package? A Python package provides a way to structure a project's module namespace by using directori...
  • Configuring Pytest : Configuring Pytest As you start using Pytest extensively, typing -v or -m on the command line every time becomes tedious. Centralize your defaults in...
  • Signaling Errors: The raise Statement : Signaling Errors: The raise Statement Functions sometimes encounter states they cannot handle and must signal failure clearly. Using raise triggers...


(110)
Filtrer par liens sans tag
Replier Replier tout Déplier Déplier tout Êtes-vous sûr de vouloir supprimer ce lien ? Êtes-vous sûr de vouloir supprimer ce tag ? Le gestionnaire de marque-pages personnel, minimaliste, et sans base de données par la communauté Shaarli