0

Does something like the following class exist in the Python stdlib (or maybe in some other very common lib)?

class Args:
  def __init__(self, *args, **kwargs):
    self.args = args
    self.kwargs = kwargs

Background

(Although this is just one example, I can imagine that there are more potential use cases.)

I frequently have the pattern that some function forwards the kwargs to another, or even some of the args.

Examples:

class Super: ...

class Foo(Super):
  def __init__(self, foo, **kwargs):
    super().__init__(**kwargs)
def basic_func(x, opt1, opt2): ...

def higher_level_func(opt3, opt4, **kwargs):
  ...
  y = basic_func(x, **kwargs)
  ...

functools.partial

Sometimes when it gets more complex, you see patterns like this:

def basic_foo(x, foo1, foo2): ...
def basic_bar(x, bar1, bar2): ...

def higher_level_func(baz1, baz2, foo_opts: Dict[str,Any], bar_opts: Dict[str,Any]):
  ...
  x = basic_foo(x, **foo_opts)
  ...
  x = basic_bar(x, **bar_opts)
  ...

I want to generalize foo_opts: Dict[str,Any] and bar_opts: Dict[str,Any] in this last example to also allow for args and not just `kwargs.

You could simply do this:

def higher_level_func(baz1, baz2, foo_args: Tuple[Any], foo_kwargs: Dict[str,Any], bar_args: Tuple[Any], bar_kwargs: Dict[str,Any]):
  ...
  x = basic_foo(x, *foo_args, **foo_kwargs)
  ...
  x = basic_bar(x, *bar_args, **bar_kwargs)
  ...

But that looks ugly. So I would want to do this instead:

def higher_level_func(baz1, baz2, foo_args: Args, bar_args: Args):
  ...
  x = basic_foo(x, *foo_args.args, **foo_args.kwargs)
  ...
  x = basic_bar(x, *bar_args.args, **bar_args.kwargs)
  ...

This looks like it would be a useful common pattern, so I wonder whether something like this already exists (maybe even in the stdlib).

Note that functools.partial is also already close to what I want. It binds also the function itself (basic_foo or basic_bar in the example) which is fine. However, the order of the args is different:

foo = partial(basic_foo, *args, **kwargs)
...
foo(x) == basic_foo(*args, x, **kwargs)

And this is a problem, because I need the x arg to be first here, and I would want that the partial only binds the following args. (Related, related, related.)

Bonus question

Somehow tell type checkers like MyPy or PyCharm to what function Args matches, e.g. like Args[basic_foo]. Not sure if this is feasible...

Albert
  • 65,406
  • 61
  • 242
  • 386
  • I highly doubt this exists in the standard library, because almost all of the time you want to forward both `*args` and `**kwargs`, you want to forward them within the same lexical scope where they're defined, not store them somewhere to be forwarded later from a different scope. – kaya3 Nov 11 '21 at 09:50
  • Perhaps implementing wrapper function is a better solution? Ex: https://stackoverflow.com/a/49376460/10074443 – kklocker Nov 11 '21 at 11:28
  • @kaya3 But in my examples, this is not at all about the scope. It would still forward them within the same lexical scope. It's just some slightly nicer syntax. – Albert Nov 11 '21 at 13:44
  • @kklocker: `functools.partial` would be the right candidate for what I want. It would fit almost perfectly, except that the order of the args is wrong. `partial` in this example would do `basic_foo(*foo_args, x, **foo_kwargs)` but not what I have (and need) in the example. Your linked wrapper would not work because it needs to add additional args inside (`x` in the example). – Albert Nov 11 '21 at 13:47

0 Answers0