Dynamically Setting the Output Type of Python Function
Question:
Okay, I have a set of dictionaries that contain a key/ID and a function. Said dictionaries are used as identifiers that point to a specific cache key and function to update said cache key’s associated data.
from typing import TypedDict, List
class CacheIdentifier(TypedDict):
key: str
func: Callable
def function_to_fetch_cache_data() -> List[str]:
return ["a", "b", "c"]
IDENTIFIER: CacheIdentifier = {
"key": "my_cache_key",
"func": function_to_fetch_cache_data
}
Now, the thing is, I have a function called load_or_set_cache
which takes an identifier object and, like the name says, checks if there’s data associated with a cache key and fetches the data associated with the cache key if it exists, otherwise it uses the func
argument of the CacheIdentifier
object provided to fetch new data.
def load_or_set_cache(identifier: CacheIdentifier) -> Any:
# Logic to check if the data exists in the cache or not
if not cached:
cache_data = identifier["func"]()
cache_key = identifier["key"]
cache.set(cache_key, cache_data, TTL = 3600)
else:
cache_key = identifier["key"]
cache_data = cache.get(cache_key)
return cache_data
cache_data = load_or_set_cache(IDENTIFIER)
The thing is, the load_or_set_function
function returns the data that was fetched and stored in the cache, but as you can expect, the type of said data varies depending on the return type of the function of each identifier. In my example above, if the function_to_fetch_cache_data
has a return type of List[str]
then the load_or_set_cache
function will have the same return type, causing cache_data
to have a List[str]
type.
Currently the output type of the load_or_set_cache
function is just set to Any
, is there any way I could dynamically change the output type of the function depending on the output type of the associated func
argument found in each cache identifier?
I’ve tried playing with TypeVars but dont feel like they really suit what I want to do
Answers:
I think Pedro has the right idea. Making your CacheIdentifier
generic in terms of the func
return type works, but not with a TypedDict
:
from collections.abc import Callable
from typing import Generic, TypeVar
T = TypeVar("T")
class CacheIdentifier(Generic[T]):
key: str
func: Callable[..., T]
def __init__(self, key: str, func: Callable[..., T]) -> None:
self.key = key
self.func = func
def load_or_set_cache(identifier: CacheIdentifier[T]) -> T:
return identifier.func()
def main() -> None:
def function_to_fetch_cache_data() -> list[str]:
return ["a", "b", "c"]
ident = CacheIdentifier("my_cache_key", func=function_to_fetch_cache_data)
cache_data = load_or_set_cache(ident)
reveal_type(cache_data)
Mypy output:
note: Revealed type is "builtins.list[builtins.str]"
Python currently does not support defining generic TypedDict
subclasses.
I think your question was confusing some people, not least because you were talking about "dynamically" setting the output type, yet have that be understood by a static type checker. That makes no sense. And nothing about this is dynamic. The types are all known before the program is run.
I recommend you read through this section of PEP 484 (but really the entire thing).
Okay, I have a set of dictionaries that contain a key/ID and a function. Said dictionaries are used as identifiers that point to a specific cache key and function to update said cache key’s associated data.
from typing import TypedDict, List
class CacheIdentifier(TypedDict):
key: str
func: Callable
def function_to_fetch_cache_data() -> List[str]:
return ["a", "b", "c"]
IDENTIFIER: CacheIdentifier = {
"key": "my_cache_key",
"func": function_to_fetch_cache_data
}
Now, the thing is, I have a function called load_or_set_cache
which takes an identifier object and, like the name says, checks if there’s data associated with a cache key and fetches the data associated with the cache key if it exists, otherwise it uses the func
argument of the CacheIdentifier
object provided to fetch new data.
def load_or_set_cache(identifier: CacheIdentifier) -> Any:
# Logic to check if the data exists in the cache or not
if not cached:
cache_data = identifier["func"]()
cache_key = identifier["key"]
cache.set(cache_key, cache_data, TTL = 3600)
else:
cache_key = identifier["key"]
cache_data = cache.get(cache_key)
return cache_data
cache_data = load_or_set_cache(IDENTIFIER)
The thing is, the load_or_set_function
function returns the data that was fetched and stored in the cache, but as you can expect, the type of said data varies depending on the return type of the function of each identifier. In my example above, if the function_to_fetch_cache_data
has a return type of List[str]
then the load_or_set_cache
function will have the same return type, causing cache_data
to have a List[str]
type.
Currently the output type of the load_or_set_cache
function is just set to Any
, is there any way I could dynamically change the output type of the function depending on the output type of the associated func
argument found in each cache identifier?
I’ve tried playing with TypeVars but dont feel like they really suit what I want to do
I think Pedro has the right idea. Making your CacheIdentifier
generic in terms of the func
return type works, but not with a TypedDict
:
from collections.abc import Callable
from typing import Generic, TypeVar
T = TypeVar("T")
class CacheIdentifier(Generic[T]):
key: str
func: Callable[..., T]
def __init__(self, key: str, func: Callable[..., T]) -> None:
self.key = key
self.func = func
def load_or_set_cache(identifier: CacheIdentifier[T]) -> T:
return identifier.func()
def main() -> None:
def function_to_fetch_cache_data() -> list[str]:
return ["a", "b", "c"]
ident = CacheIdentifier("my_cache_key", func=function_to_fetch_cache_data)
cache_data = load_or_set_cache(ident)
reveal_type(cache_data)
Mypy output:
note: Revealed type is "builtins.list[builtins.str]"
Python currently does not support defining generic TypedDict
subclasses.
I think your question was confusing some people, not least because you were talking about "dynamically" setting the output type, yet have that be understood by a static type checker. That makes no sense. And nothing about this is dynamic. The types are all known before the program is run.
I recommend you read through this section of PEP 484 (but really the entire thing).