I have logic in my application that requires me to create different variations of one specific class. These variations differ only by class properties.
If variation == "A", then some class properties need to be set to zero and others filled with the provided input, else if variation == "B", some other properties are zero etc.
It looks to me that the factory pattern is a perfect solution for this problem, but there is a complication I cannot find a solution for - every variation is expected to receive a different number of arguments. Basically, business logic knows that, let's say, property X of variation A is always 0, so function create_variation_a() should not expect X as argument.
I could just set argument X to a default value, but than other devs would not know if X needs to be provided or not without checking the code.
class Something:
def __init__(self, one: int, two: int, three: int):
self.one = one
self.two = two
self.three = three
def create_one(one: int) -> Something:
return Something(one=one, two=0, three=0)
def create_two(one: int, two: int) -> Something:
return Something(one=one, two=two, three=0)
def create_three(one: int, two: int, three: int) -> Something:
return Something(one=one, two=two, three=three)
factory = {
"one": create_one,
"two": create_two,
"three": create_three
}
myfunc1 = factory["one"]
# I want type checker to know that I only need to supply one
variation_one = myfunc1()
myfunc3 = factory["three"]
# I want type checker to know that I need to supply one, two and three
variation_three = myfunc3()
CodePudding user response:
You can use typing.TypedDict
to provide a more specifically typed dict
that knows what type of value each specific key maps to.
from typing import TypedDict, Callable
class FactoryType(TypedDict):
one: Callable[[str], str]
two: Callable[[str, str], str]
three: Callable[[str, str, str], str]
factory : FactoryType = {'one': create_one, 'two': create_two, 'three': create_three}
This works because TypedDict
is essentially one giant special case for handling argument-specific type narrowing for dict.get
.
CodePudding user response:
You can use typing.overload
here:
import typing
from collections.abc import Callable
def create_one(one: str) -> str:
return one
def create_two(one: str, two: str) -> str:
return one two
def create_three(one: str, two: str, three: str) -> str:
return one two three
@typing.overload
def get_func(term: typing.Literal["one"]) -> Callable[[str], str]:
...
@typing.overload
def get_func(term: typing.Literal["two"]) -> Callable[[str, str], str]:
...
@typing.overload
def get_func(term: typing.Literal["three"]) -> Callable[[str, str, str], str]:
...
def get_func(term):
if term == "one":
return create_one
elif term == "two":
return create_two
elif term == "three":
return create_three
else:
raise ValueError(f"Did not recognize f{repr(term)}")
f = get_func("one")
f('foo')
f('foo', 'bar')
g = get_func("three")
g('foo', 'bar')
g('foo', 'bar', 'baz')
Then, using mypy
:
(py311) juanarrivillaga-mbp16-2019:~ juanarrivillaga$ mypy test_typing.py
test_typing.py:37: error: Too many arguments [call-arg]
test_typing.py:40: error: Too few arguments [call-arg]
Found 2 errors in 1 file (checked 1 source file)