# 2. Defining function with arbitrary number of arguments

## 2.1. Recap information about function parameters

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

echo(1, 2)       # pozycyjne
echo(a=1, b=2)   # nazwane, kolejność nie ma znaczenia
echo(b=2, a=1)   # nazwane, kolejność nie ma znaczenia
echo(1, b=2)     # pozycyjne i nazwane
echo(a=1, 2)     # SyntaxError: positional argument follows keyword argument
```

## 2.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('red', 2)                # ('red', 2)
echo('red', 'green', 'blue')  # ('red', 'green', 'blue')
```
```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)
```

## 2.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=10)                                      # {'a': 10}
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)       # 0
print(kwargs)  # {'d':7, 'e': 8}

echo(1, 2, d=7, e=8)
```
```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)
```

## 2.4. Arbitrary number of positional and named arguments

```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)
```

## 2.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'
```

## 2.6. Use cases

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

for arg in args:
total += arg

return total

add()            # 0
add(1)           # 1
add(1, 4)        # 5
add(3, 1)        # 4
add(1, 2, 3, 4)  # 10
```
Listing 92. 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 93. 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 94. Intuitive definition of `print` function
```def print(*values, sep=' ', end='\n', ...):
return sep.join(values) + end
```

## 2.7. Assignments

### 2.7.1. Numeric Values, positional only

• Lines of code to write: 5 lines

• Estimated time of completion: 15 min

1. Stwórz funkcję `is_numeric`

2. Funkcja może przyjmować tylko argumenty pozycyjne

3. Podawane argumenty mogą być dowolnego typu

4. Za pomocą `isinstance()` sprawdź:

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

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

5. Przez numeryczną wartością rozumiemy

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

The whys and wherefores
• Definiowanie i uruchamianie funkcji

• Wiele argumentów pozycyjnych

• Sprawdzanie przypadków brzegowych (niekompatybilne argumenty)

• Parsowanie argumentów funkcji

• Rzutowanie i konwersja typów

### 2.7.2. Numeric Values, positional and keyword

• Lines of code to write: 5 lines

• Estimated time of completion: 15 min

1. Stwórz funkcję `is_numeric`

2. Funkcja może przyjmować zarówno argumenty pozycyjne jak i nazwane

3. Podawane argumenty mogą być dowolnego typu

4. Za pomocą `isinstance()` sprawdź:

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

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

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

The whys and wherefores
• Definiowanie i uruchamianie funkcji

• Wiele argumentów pozycyjnych i nazwanych

• Sprawdzanie przypadków brzegowych (niekompatybilne argumenty)

• Parsowanie argumentów funkcji

• Rzutowanie i konwersja typów