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"}))
4 months ago Permalien
cluster icon
  • 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...
  • Handling Errors and Status Codes : Handling Errors and Status Codes HTTP status codes communicate the outcome of an API request, and handling them correctly is key to robust automation...
  • Python Functions Are First‑Class Citizens : Python Functions Are First‑Class Citizens In Python, functions behave like any other object (strings, ints, lists). Because they are "first‑clas...
  • *args and **kwargs : Flexible Functions: *args and **kwargs We can use the syntax *args and **kwargs to accept a variable number of both positional and keyword arguments....
  • Mocking : Mocking Fundamentals Introduction When unit testing DevOps scripts that interact with external systems, tests can become slow, unreliable, difficult ...


(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