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

Adding Type Hints to Decorators and Generators/shaare/1guohQ

  • python
  • python

Adding Type Hints to Decorators and Generators

  • Decorators and generators are advanced constructs that require specialized type hints to make their transformations and data flows explicit.
  • Properly typed decorators allow MyPy to understand how they preserve or change function signatures.
  • Typed generators clarify the types of values yielded, values accepted via .send(), and final return values.

Typing Decorators

  • Decorators take a function (Callable) and return a new function; using Callable[..., Any] types them broadly but loses specific signature information.
  • To preserve the original function’s signature, define a TypeVar bound to Callable[..., Any] and use it for both the decorator’s input and output types.
  • Inside the decorator, the wrapper can use *args: Any, **kwargs: Any -> Any, while TypeVar ensures the decorated function’s overall type remains correct.

Typing Generators

  • Use Generator[YieldType, SendType, ReturnType] to specify a generator’s yield type, the type accepted by .send(), and its return type on completion.
  • If a generator does not use send(), set SendType to None; if it has no explicit return, set ReturnType to None.
  • The count_up generator is typed as Generator[int, None, str], yielding integers and returning a string message.
  • The accumulate_and_send generator is typed as Generator[float, float, None], yielding a running total, accepting floats via send(), and returning nothing.

Iterable & Iterator

  • For functions that consume sequences of items, use Iterable[T] to accept any iterable of T (lists, tuples, generators).
  • Use Iterator[T] when a function specifically expects an iterator object supporting __next__().
from typing import (
    Callable,
    Any,
    TypeVar,
    ParamSpec,
    Generator,
    Iterable,
)
import functools

# Section: Typing Decorators (simple_logging_decorator)

def simple_logging_decorator(
    func: Callable[..., Any],
) -> Callable[..., Any]:
    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        print(f"LOG: Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"LOG: {func.__name__} returned {result}")

        return result

    return wrapper

@simple_logging_decorator
def add(x: int, y: int) -> int:
    return x + y

result_add = add(3, 5)

# Section: Typing Decorators (better_logging_decorator with TypeVar)

P = ParamSpec("P")
R = TypeVar("R")

def better_logging_decorator(
    func: Callable[P, R],
) -> Callable[P, R]:
    @functools.wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
        print(f"LOG: Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"LOG: {func.__name__} returned {result}")

        return result

    return wrapper

@better_logging_decorator
def subtract(x: int, y: int) -> int:
    return x - y

result_subtract = subtract(3, 5)

# Section: Typing Generators

def count_up_to(limit: int) -> Generator[int, None, str]:
    for i in range(limit):
        yield i

    return "Counting complete!"

def accumulate_and_send() -> (
    Generator[float, float | None, None]
):
    total = 0.0

    try:
        while True:
            sent = yield total

            if sent:
                total += sent
    except GeneratorExit:
        pass

test_accumulate = accumulate_and_send()
next(test_accumulate)
print(test_accumulate.send(1.0))
print(next(test_accumulate))
print(test_accumulate.send(2.0))
print(test_accumulate.send(3.0))
print(next(test_accumulate))

# Section: Iterable & Iterator

def process_items(items: Iterable[str]) -> list[str]:
    return [item.upper() for item in items]

print(process_items(["a", "b"]))
print(process_items(("a", "b")))
print(process_items({"a", "b"}))
print(process_items({"a": "b", "hello": "world"}))
3 months ago Permalien
cluster icon
  • Working with YAML files : Working with YAML files YAML (“YAML Ain’t Markup Language”) focuses on human readability. Indentation replaces braces and brackets, comments are allo...
  • Running External Commands with subprocess.run : Running External Commands with subprocess.run DevOps automation often requires invoking existing CLI tools or scripts to leverage their functionality...
  • Exceptions : Common Built‑in Exceptions Python ships with a rich hierarchy of exception classes; most automation errors fall into a small, predictable subset. A...
  • Fixtures in Pytest : Fixtures in Pytest As tests grow more complex, repeating setup and cleanup steps makes tests harder to read and maintain. Pytest fixtures allow centr...
  • Concise Iteration: List Comprehensions : Concise Iteration: List Comprehensions Simple for loops to create lists can be verbose. We can leverage list comprehensions to define the list content...


(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