How can a function accept any number of positional arguments? Put `*` before a parameter name. In `print(*objects, ...)`, that lets callers print zero or more objects. `*args` is the conventional spelling, but the name is not special: `*objects` and `*items` work the same way. section 6.4 section 6.4.2
Collect and use the arguments
```python def stringify(*items): return [str(item) for item in items] print(stringify(1, "two", None)) # ['1', 'two', 'None'] ``` Inside the function, `items` is a tuple containing the supplied positional arguments. The list comprehension visits each tuple element and converts it to a string. section 6.4.2
If a parameter must receive a positional argument first, place it before the starred parameter: `def stringify_a(item0, *items)`. A call such as `stringify_a(0, 1)` assigns `0` to `item0` and packs `(1,)` into `items`. In `def stringify_b(*items, item0)`, by contrast, `item0` must be supplied by keyword: `stringify_b(0, item0=1)`. section 6.4.2
Variadic arguments also make decorator wrappers usable with functions that have different calling signatures. A wrapper with no parameters fails when the decorated function is called with an argument; an inner function accepting `*args` and `**kwargs` can accommodate positional and keyword arguments. Use this flexibility when the number of arguments cannot be known in advance. Otherwise, explicitly named parameters make a function easier to understand. section 7.3.2 section 6.4.4







