For typing **kwargs there are TypedDicts
<a href="https://peps.python.org/pep-0692/" rel="nofollow noreferrer">https://peps.python.org/pep-0692/</a><p>If your function just wraps another you can use the same type hints as the other function with functools.wraps
<a href="https://docs.python.org/3/library/functools.html#functools.wraps" rel="nofollow noreferrer">https://docs.python.org/3/library/functools.html#functools.w...</a>
I actually created a library for this!<p>Forge: forge (python signatures) for fun and profit<p><a href="https://python-forge.readthedocs.io/" rel="nofollow noreferrer">https://python-forge.readthedocs.io/</a><p><a href="https://github.com/dfee/forge">https://github.com/dfee/forge</a>
The ability of **kwargs to leave behind no proper documentation and silently swallow any invalid arguments has made us remove them entirely from our codebase. They're almost entirely redundant when you have dataclasses.
This does restrict all of your keyword arguments to the same type. If you have keyword arguments of different types, you're right back to no type safety.
Why do people not just type everything they want passed?<p>def variable(n:str, nn:str, nnn:str, *, a:int, b:int, c:int)<p>Anything after,*, is a kwarg.
now try typing a decorator<p><a href="https://stackoverflow.com/questions/47060133/python-3-type-hinting-for-decorator" rel="nofollow noreferrer">https://stackoverflow.com/questions/47060133/python-3-type-h...</a><p>what a disaster
> In the function body, args will be a tuple, and kwargs a dict with string keys.<p>This always bugs me: why is `args` immutable (tuple) but `kwargs` mutable (dict)? In my experience it’s much more common to have to extend or modify `kwargs` rather than `args`, but I would find more natural having an immutable dict for `kwargs`.
#TIL. Also cool to know is pydantic's @validate decorator: <a href="https://docs.pydantic.dev/latest/usage/validation_decorator/#function-signatures" rel="nofollow noreferrer">https://docs.pydantic.dev/latest/usage/validation_decorator/...</a> and in case you were thinking its not superflous to mypy(<a href="https://docs.pydantic.dev/latest/usage/validation_decorator/#usage-with-mypy" rel="nofollow noreferrer">https://docs.pydantic.dev/latest/usage/validation_decorator/...</a>).
Alternatively, use an `@overload` in a `.pyi` file and specify your types there.<p>This means that you will have 2^N combinations and doubling every time you accept a new argument.<p>If that is not good enough, then simply use a `TypedDict` with everything optional instead of `**kwargs`. Your call will then become `foo(SomeTypedDict(p1=p2,...))`.
That article promulgates a misunderstanding about immutability. For my way of thinking, python is already an interpreted language and I can enforce tropes in code more cleanly and effectively than people taking something five levels up at face value and trying to figure out what sticks when they throw it against the wall: no wonder they end up frustrated, and it's a frustrating situation.<p>Given:<p><pre><code> def foo(*args):
print(args)
return
class Thing(object):
def __init__(self,a,b):
self.a = a
self.b = b
return
def foo_style(self):
return (self.a, self.b)
</code></pre>
<i>args is not required to refer to a tuple:<p><pre><code> >>> foo(*[31,42])
(31, 42)
</code></pre>
I can have objects construct parameters conforming to the specifications for a signature:<p><pre><code> >>> foo(*Thing(3,91).foo_style())
(3, 91)
</code></pre>
Consider that a counterexample.</i>
Although these two comes in handly, people have been using them wrong. Often in scientific open source package, they slap *kwargs in function definition without documentation. How am I suppose to know what to pass in?<p><a href="https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Estimator.set_options.html#qiskit_aer.primitives.Estimator.set_options" rel="nofollow noreferrer">https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives...</a>