# 4. Passing many arguments

## 4.1. 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 from `tuple`, `list` or `set`

Listing 100. Positional arguments passed directly
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

echo(1, 2)
```
Listing 101. Positional arguments passed from sequence
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

args = (1, 2)
echo(*args)
```

## 4.2. 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 from `dict`

Listing 102. Keyword arguments passed directly
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

echo(a=1, b=2)
```
Listing 103. Keyword arguments passed from `dict`
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

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

## 4.3. Arbitrary number of positional and keyword arguments

Listing 104. Positional and keyword arguments passed directly
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

echo(1, b=2)
```
Listing 105. Positional and keyword arguments passed from sequence and `dict`
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

args = (1,)
kwargs = {'b': 2}

echo(*args, **kwargs)
```

## 4.4. Examples

### 4.4.1. Creating complex numbers

Listing 106. Defining complex number by passing keyword arguments directly
```complex(real=3, imag=5)
# (3+5j)
```
Listing 107. Defining complex number by passing keyword arguments in `dict`
```kwargs = {'real': 3, 'imag': 5}

complex(**kwargs)
# (3+5j)
```

### 4.4.2. Vectors

Listing 108. Passing vector to the function
```def cartesian_coordinates(x, y, z):
print(x)    # 1
print(y)    # 0
print(z)    # 1

vector = (1, 0, 1)

cartesian_coordinates(*vector)
```

### 4.4.4. Common configuration

Listing 110. Calling a function which has similar parameters
```def draw_line(x, y, color, type, width, markers):
...

draw_line(x=1, y=2, color='red', type='dashed', width='2px', markers='disc')
draw_line(x=3, y=4, color='red', type='dashed', width='2px', markers='disc')
draw_line(x=5, y=6, color='red', type='dashed', width='2px', markers='disc')
```
Listing 111. Passing configuration to the function, which sets parameters from the config
```def draw_line(x, y, color, type, width, markers):
...

style = {
'color': 'red',
'type': 'dashed',
'width': '2px',
'markers': 'disc',
}

draw_line(x=1, y=2, **style)
draw_line(x=3, y=4, **style)
draw_line(x=5, y=6, **style)
```
Listing 112. Database connection configuration read from config file
```config = {
'host': 'localhost',
'port': 5432,
'database': 'my_database',
}

return ...

connection = database_connect(**config)
```

### 4.4.5. Calling function with all variables from higher order function

Listing 113. Passing arguments to lower order function. `locals()` will return a `dict` with all the variables in local scope of the function.
```def lower(a, b, c, d, e):
print(a, b, c, d, e)

def higher(a, b, c=0):
d = 4
e = 5
lower(**locals())
# lower(a=1, b=2, c=0, d=4, e=5)

higher(1, 2)
# 1 2 0 4 5
```

### 4.4.6. Proxy functions

Listing 114. One of the most common use of `*args`, `**kwargs` is for proxy methods.
```def read_csv(filepath_or_buffer, sep=', ', delimiter=None, header='infer',
names=None, index_col=None, usecols=None, squeeze=False, prefix=None,
mangle_dupe_cols=True, dtype=None, engine=None, converters=None,
true_values=None, false_values=None, skipinitialspace=False,
skiprows=None, nrows=None, na_values=None, keep_default_na=True,
na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False,
infer_datetime_format=False, keep_date_col=False, date_parser=None,
dayfirst=False, iterator=False, chunksize=None, compression='infer',
thousands=None, decimal=b'.', lineterminator=None, quotechar='"',
quoting=0, escapechar=None, comment=None, encoding=None, dialect=None,
skipfooter=0, doublequote=True, delim_whitespace=False, low_memory=True,
memory_map=False, float_precision=None):
"""
"""

def my_csv(file, encoding='utf-8', *args, **kwargs):

my_csv('iris1.csv')
my_csv('iris2.csv', encoding='iso-8859-2')
my_csv('iris3.csv', encoding='cp1250', verbose=True)
my_csv('iris4.csv', verbose=True, usecols=['Sepal Length', 'Species'])
```

### 4.4.7. Decorators

Listing 115. Decorators are functions, which get pointer to the decorated function as it's argument, and has closure which gets original function arguments as positional and keyword arguments.
```def login_required(original_function):

def wrapper(*args, **kwargs):
user = kwargs['request'].user

if user.is_authenticated():
return original_function(*args, **kwargs)
else:
print('Permission denied')

return wrapper

def edit_profile(request):
...
```

## 4.5. Assignments

### 4.5.1. Iris

1. Otwórz link w przeglądarce i skopiuj zawartość do pliku na dysku o nazwie `iris.csv`

2. Z pliku `iris.csv` odseparuj nagłówek i dane

3. Z nagłówka odrzuć rekord `species`

4. Stwórz funkcję `print_iris(species, **pomiary)`, która wyświetli zawartość wszystkich argumentów za pomocą `locals()`

5. Dla każdego rekordu w danych:

1. Usuń białe spacje

2. Podziel po przecinku `,`

3. Wyniki podziału zapisz do dwóch zmiennych:

• `pomiary: Dict[str, float]` - pomiary

• `gatunek: str` - nazwa gatunku

4. Odpalaj funkcję `print_iris()`, podając wartości `pomiary` i `gatunek`

5. `gatunek` ma być podany pozycyjnie

6. `pomiary` mają być podane nazwanie