# Positional and Keyword Arguments¶

• An ordinary function, taking two parameters `a` and `b`

```def f(a, b):
print(a, b)
```
• Called with positional arguments

```f(1, 2)
```
```1 2
```
• Called with keyword arguments

```f(a=3, b=4)
```
```3 4
```

## Supplying Function Arguments Dynamically¶

• Positional arguments: sequence

```args = [1, 2]
f(*args)
```
```1 2
```

Any sequence is sufficient - e.g. `range()`

```args = range(1, 3)
f(*args)
```
```1 2
```
• Keyword arguments: mapping (usually `dict`)

```kwargs = {'a':1, 'b':2}
f(**kwargs)
```
```1 2
```

## Functions That Takes Arbitrary Number Of Positional Arguments¶

• The other way around: not passing parameters dynamically, but accepting them dynamically

• “Starargs”: `*args` function parameter

• `args` is just a common name, can be arbitrary

```def f(*args):                     # <--- starargs
print(args, type(args))       # <--- will print *tuple*
```
• Called traditionally, with positional arguments

• Any number of arguments possible

```f(1, 2)
f(1, 2, 3)
```
```(1, 2) <class 'tuple'>
(1, 2, 3) <class 'tuple'>
```

## Functions That Take Arbitrary Keyword Arguments¶

• Much like positional args, but keyword args and `dict`

• “Starstarargs”: `**kwargs` function parameter

• Again, `kwargs` is just a common name, can be arbitrary

```def f(**kwargs):                  # <--- starstarargs
print(kwargs, type(kwargs))   # <--- will print *dict*
```
• Called traditionally, with keyword arguments

• Any number of arguments possible

```f(a=1, b=2)
f(a=1, b=2, c=3)
```
```{'a': 1, 'b': 2} <class 'dict'>
{'a': 1, 'b': 2, 'c': 3} <class 'dict'>
```

## And Arbitrary Positional And Keyword Arguments?¶

• Rule: positional Arguments must come before keyword arguments

```def f(*args, **kwargs):
print(args)
print(kwargs)

f(1, 2, a=3, b=4)
```
```(1, 2)
{'a': 3, 'b': 4}
```

## Ultimate Dynamicity: Arbitrary Positional And Keyword Arguments¶

• Pass-through: accept any number and of arguments

• Pass them on as they were passed

• Typical usecase: decorators

```def f(*args, **kwargs):
print(*args, **kwargs)

f(1, 2, end='\n(done)\n')            # <--- actually, this *is* print
```
```1 2
(done)
```

## Use Case: A Better `print()`¶

• Works like `print()`

• Has a number of bells and whistles of its own

• `indent`

```import sys

def my_print(*args, **kwargs):
indent = kwargs.pop('indent')    # <--- remove from kwargs
if indent is not None:
args = (' '*indent,) + args
print(*args, **kwargs)           # <--- pass rest to print

my_print(1, 2,
end='\n(done)\n',           # <--- consumed by print
indent=4,                   # <--- consumed by my_print
)
```
```     1 2
(done)
```