Need a function to accept a variable number of keyword arguments? Put `**kwargs` after its other parameters. Python collects the supplied keyword arguments into a dictionary. `kwargs` is the conventional name, not a required one: `**grades` works the same way. section 6.4.3
Build a report from supplied grades
```python def create_report(name, **grades): for subject, grade in grades.items(): print(f"{name}: {subject} = {grade}") create_report("John", math=100, phys=98) ``` Output: ```text John: math = 100 John: phys = 98 ``` Here, `grades` is a dictionary, so `items()` supplies each subject–grade pair. Calling the function without grades is also valid. section 6.4.3 section 6.4.5
For optional settings, use `kwargs.get("setting", default)` to supply a value when the caller omits a keyword. Use `**kwargs` selectively, though: explicitly named parameters make accepted options clearer when those options are known. section 3.4.4 section 6.4.4
Decorator wrappers are another useful case. An inner function that accepts `*args` and `**kwargs` can handle decorated functions with differing positional and keyword arguments; a zero-argument wrapper fails when the decorated call supplies an argument. section 7.3.2


