# 4.1. Defining function with arbitrary number of arguments

## 4.1.1. Recap information about function parameters

• positional arguments

• keyword (named) arguments

• default values

• keyword arguments must be on the right side

• order of keyword arguments doesn't matter

```def echo(a, b):
print(a)
print(b)

echo(1, 2)       # positional arguments
echo(a=1, b=2)   # keyword arguments
echo(b=2, a=1)   # keyword arguments, order doesn't matter
echo(1, b=2)     # positional and keyword arguments
echo(a=1, 2)     # SyntaxError: positional argument follows keyword argument
```

## 4.1.2. Arbitrary number of positional arguments

• `*` in this context, is not multiplication in mathematical sense

• `*` is used for positional arguments

• `args` is a convention, but you can use any name

• `*args` unpacks to `tuple`

```def echo(*args):
print(args)

echo()                     # ()
echo(1)                    # (1,)
echo(2, 3)                 # (2, 3)
echo('a', 'b')             # ('a', 'b')
echo('a', 2, 3.3)          # ('a', 2, 3.3)
```
```def echo(a, b, c=0, *args):
print(a)       # 1
print(b)       # 2
print(c)       # 0
print(args)    # ()

echo(1, 2)
```
```def echo(a, b, c=0, *args):
print(a)       # 1
print(b)       # 2
print(c)       # 3
print(args)    # (4, 5, 6)

echo(1, 2, 3, 4, 5, 6)
```

## 4.1.3. Arbitrary number of keyword arguments

• `**` in this context, is not power in mathematical sense

• `**` is used for keyword arguments

• `kwargs` is a convention, but you can use any name

• `**kwargs` unpacks to `dict`

```def echo(**kwargs):
print(kwargs)

echo(a=1)                                       # {'a': 1}
echo(color='red')                               # {'color': 'red'}
echo(first_name='Jan', last_name='Twardowski')  # {'first_name': 'Jan', 'last_name': Twardowski}
```
```def echo(a, b, c=0, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 0
print(kwargs)  # {}

echo(1, 2)
```
```def echo(a, b, c=0, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 3
print(kwargs)  # {'d': 7, 'e': 8}

echo(1, 2, 3, d=7, e=8)
```

## 4.1.4. Arbitrary number of positional and named arguments

```def echo(a, b, c=0, *args, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 0
print(args)    # ()
print(kwargs)  # {}

echo(1, 2)
```
```def echo(a, b, c=0, *args, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 3
print(args)    # (4, 5, 6)
print(kwargs)  # {}

echo(1, 2, 3, 4, 5, 6)
```
```def echo(a, b, c=0, *args, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 0
print(args)    # ()
print(kwargs)  # {'d': 7, 'e': 8}

echo(1, 2, d=7, e=8)
```
```def echo(a, b, c=0, *args, **kwargs):
print(a)       # 1
print(b)       # 2
print(c)       # 3
print(args)    # (4, 5, 6)
print(kwargs)  # {'d': 7, 'e': 8}

echo(1, 2, 3, 4, 5, 6, d=7, e=8)
```

## 4.1.5. Keyword only

• All arguments after `*` is keyword only

• Since Python 3.8 there will be `/` to indicate positional only arguments

```def echo(a, *, b):
print(a)
print(b)

echo(1, b=2)
# 1
# 2

echo(1, 2)
# TypeError: echo() takes 1 positional argument but 2 were given

echo(1)
# TypeError: echo() missing 1 required keyword-only argument: 'b'
```
```def echo(a, /, b, *, c):
print(a, b, c)

echo(1, 2, c=3)      # is valid
echo(1, 2, 3)        # TypeError: echo() takes 2 positional arguments but 3 were given
echo(1, b=2, c=3)    # TypeError: echo() takes 2 positional arguments but 1 positional arguments (and 2 keyword-only argument) were given
```

## 4.1.6. Use cases

```def add(*args):
total = 0

for arg in args:
total += arg

add(1, 2, 3, 4)  # 10
```
Listing 238. Converts arguments between different units
```def kelvin_to_celsius(*degrees):
return [x+273.15 for x in degrees]

kelvin_to_celsius(1)
# [274.15]

kelvin_to_celsius(1, 2, 3, 4, 5)
# [274.15, 275.15, 276.15, 277.15, 278.15]
```
Listing 239. Generate HTML list from function arguments
```def html_list(*args):
print('<ul>')

for element in args:
print(f'<li>{element}</li>')

print('</ul>')

html_list('apple', 'banana', 'orange')
# <ul>
# <li>apple</li>
# <li>banana</li>
# <li>orange</li>
# </ul>
```
Listing 240. Intuitive definition of `print` function
```def print(*values, sep=' ', end='\n', ...):
return sep.join(values) + end
```
```def parse(line):
group_name, *members = line.split(',')
return group_name.upper(), *members

parse('astronauts,twardowski,watney,ivanovic')
# ('ASTRONAUTS', 'twardowski', 'watney', 'ivanovic')
```

## 4.1.7. Assignments

### 4.1.7.1. Average

• Complexity level: easy

• Lines of code to write: 4 lines + doctests

• Estimated time of completion: 5 min

English
1. Create function `average()`, which calculates arithmetic mean

2. Function can have arbitrary number of positional arguments

3. Don't use neither `numpy` nor `statistics`

Polish
1. Napisz funkcję `average()`, wyliczającą średnią arytmetyczną

2. Funkcja przyjmuje dowolną ilość pozycyjnych argumentów

3. Nie używaj `numpy` ani `statistics`

### 4.1.7.2. args

English
1. Create function `is_numeric`

2. Function can have arbitrary number of positional arguments

3. Arguments can be of any type

4. Return `True` if all arguments are `int` or `float` only

5. Return `False` if any argument is different type

6. Do not use `all()` and `any()`

7. Compare using `type()` and `isinstance()` passing `True` as an argument

8. Run the function without any arguments

Polish
1. Stwórz funkcję `is_numeric`

2. Funkcja może przyjmować dowolną liczbę argumentów pozycyjnych

3. Podawane argumenty mogą być dowolnego typu

4. Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`

5. Zwróć `False` jeżeli którykolwiek jest innego typu

6. Nie używaj `all()` oraz `any()`

7. Porównaj użycie `type()` i `isinstance()` podając argument do funkcji `True`

8. Uruchom funkcję bez podawania argumentów

The whys and wherefores
• Defining and calling functions

• Arbitrary number of positional arguments

• Corner case checking

• Function arguments checking

• Type casting

### 4.1.7.3. args and kwargs

• Complexity level: medium

• Lines of code to write: 8 lines + doctests

• Estimated time of completion: 10 min

English
1. Create function `is_numeric`

2. Function can have arbitrary number of positional and keyword arguments

3. Arguments can be of any type

4. Return `True` if all arguments are `int` or `float` only

5. Return `False` if any argument is different type

6. Do not use `all()` and `any()`

7. Compare using `type()` and `isinstance()` passing `True` as an argument

8. Run the function without any arguments

Polish
1. Stwórz funkcję `is_numeric`

2. Funkcja może przyjmować dowolną liczbę argumentów pozycyjnych i nazwanych

3. Podawane argumenty mogą być dowolnego typu

4. Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`

5. Zwróć `False` jeżeli którykolwiek jest innego typu

6. Nie używaj `all()` oraz `any()`

7. Porównaj użycie `type()` i `isinstance()` podając argument do funkcji `True`

8. Uruchom funkcję bez podawania argumentów

The whys and wherefores
• Defining and calling functions

• Arbitrary number of positional arguments

• Corner case checking

• Function arguments checking

• Type casting