Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python overload single argument

Let's assume I have a function with many arguments, e.g.

import pandas as pd

def f(df: pd.DataFrame, a: int, b: int, c: int, d: int, inplace: bool = True) -> Optional[pd.DataFrame]:
    raise NotImplementedError

The function will modify the dataframe if inplace=True and return a modified copy if inplace=False.

I know I can do

@overload
def f(df: pd.DataFrame, a: int, b: int, c: int, d: int, inplace: Literal[True] = True) -> None:
    ...

@overload
def f(df: pd.DataFrame, a: int, b: int, c: int, d: int, inplace: Literal[False] = True) -> pd.DataFrame:
    ...

to inform the typing system about this fact.

However, I'm wondering if there's a way to do this without repeating the entire function definition, which seems cumbersome if there are many arguments. I'm looking for something like

@overload
def f(..., inplace: Literal[True]) -> None:
    ...

@overload
def f(..., inplace: Literal[False]) -> pd.DataFrame:
    ...

EDIT: Several persons have made the point that the API design itself may be flawed. While this may be true, in this particular case I am retrospectively adding type hints to an existing library and changing the interface is not an option.

like image 758
Gregor Sturm Avatar asked Jul 02 '26 11:07

Gregor Sturm


1 Answers

One function with a boolean flag to alter its behavior like this is an anti-pattern. There should be two functions: one that modifies in-place and returns None, the other that returns a modified copy. (Cf. list.sort and sorted.)

That doesn't mean you need to duplicate behavior; it just means one of the functions will be a thin wrapper around the other.

import pandas as pd


def inplace_f(df: pd.DataFrame, a: int, b: int, c: int, d: int) -> None:
    ...


def f(df: pd.DataFrame, a: int, b: int, c: int, d:int) -> pd.DataFrame:
    copy = df.copy()
    inplace_f(copy)
    return copy

Now, you just use the former Boolean argument to decide which function to use. That is, for example,

f(df, ..., inplace)

becomes

(inplace_f if inplace else f)(df, ...)

If your objection is that this is more complicated than the "simple" call, then you are ignoring the fact that the "simple" call isn't that simple, because it clearly doesn't make sense to call f with inplace=False if you aren't doing anything with the return value. Context already means you have some notion of what the value of inplace is, so it's not going to be any harder to pick the right function than it will be to supply a Boolean argument to the same function.

like image 167
chepner Avatar answered Jul 04 '26 00:07

chepner