Skip to content

Instantly share code, notes, and snippets.

@d3dave
Last active October 26, 2022 22:44
Show Gist options
  • Save d3dave/f18d1750a5b8675e371c43dfcdd6470a to your computer and use it in GitHub Desktop.
Save d3dave/f18d1750a5b8675e371c43dfcdd6470a to your computer and use it in GitHub Desktop.
cache_args
# License: MIT
# Copyright (c) David D. Dorfman 2022
"""
Cache function arguments from callables on first invocation
"""
from functools import partial, wraps
from typing import Any, Callable, Optional, ParamSpec, TypeVar
P = ParamSpec('P')
R = TypeVar('R')
def cache_args(*positional_gets: Callable[[], Any], **kwargs_gets: Callable[[], Any]):
"""
Compute values for the specified args on first invocation of the function and cache them for future calls.
Cached positional args cannot be overridden. Caching keyword args is akin to caching default values.
"""
def decorate(func: Callable) -> Callable:
partial_func: Optional[partial[R]] = None
def wrapper(*args, **kwargs) -> R:
nonlocal partial_func
if partial_func is None:
partial_args = [get_arg() for get_arg in positional_gets]
partial_kwargs = {kwarg: get_kwargs() for kwarg, get_kwargs in kwargs_gets.items()}
partial_func = partial(func, *partial_args, **partial_kwargs)
partial_func = wraps(func)(partial_func)
return partial_func(*args, **kwargs)
return wrapper
return decorate
# Example:
import platform
import distro
@cache_args(platform.machine, c=distro.id)
def f(a, b, *, c, d):
print(f'{a=} {b=} {c=} {d=}')
f('xyz', d='abc')
# a='x86_64' b='xyz' c='darwin' d='abc'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment