Home > Mobile >  Python - Factory functions with different arguments
Python - Factory functions with different arguments

Time:11-10

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)
  • Related