# 4.2. Passing many arguments

## 4.2.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 241. Positional arguments passed directly
```def echo(a, b, c=0):
print(a)    # 1
print(b)    # 2
print(c)    # 0

echo(1, 2)
```
Listing 242. 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.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 243. 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 244. 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.2.3. Arbitrary number of positional and keyword arguments

Listing 245. 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 246. 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.2.4. Examples

### 4.2.4.1. Creating complex numbers

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

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

### 4.2.4.2. Vectors

Listing 249. 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.2.4.4. Common configuration

Listing 251. 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 252. 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 253. Database connection configuration read from config file
```config = {
'host': 'localhost',
'port': 5432,
'database': 'my_database',
}

return ...

connection = database_connect(**config)
```

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

Listing 254. 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.2.4.6. Proxy functions

Listing 255. 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.2.4.7. Decorators

Listing 256. 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.2.5. Assignments

### 4.2.5.1. Iris

English
1. Download `data/iris.csv` and save as `iris.csv`

2. Remove `species` column

4. For each line extract values by splitting lines by coma `,`

5. Create `OUTPUT: List[dict]` by zipping header and measurements:

• key: column name from the header

• value: measurement at the position

6. Create function `mean(**kwargs)`, function

7. Iterate over `OUTPUT` and call `mean()` by passing arguments as keywords

8. Print mean for each row

Polish
1. Pobierz plik `data/iris.csv` i zapisz jako `iris.csv`

2. Usuń kolumnę `species`

3. Odseparuj nagłówek od pomiarów

4. Wyciągnij wartości z każdej linii przez podział jej po przecinku `,`

5. Stwórz `OUTPUT: List[dict]` poprzez scalenie nagłówka i pomiarów z każdego wiersza

• klucz: nazwa kolumny z nagłówka

• wartość: pomiar z odpowiedniej kolumny

6. Stwórz funkcję `mean(**kwargs)`

7. Iterując po `OUTPUT` wywołuj `mean()` podając argumenty nazwanie

8. Wypisz średnią dla każdego wiersza

Non-functional requirements
• Use only `str.split()` method

• Don't use `pandas`, `numpy` or `csv` etc.

Output
```header: list
# ['sepal_length', 'sepal_width' ,'petal_length', 'petal_width']

OUTPUT: List[Dict[str, float]] = [
{'sepal_length': 5.4, 'sepal_width': 3.9, 'petal_length': 1.3, 'petal_width': 0.4},
{'sepal_length': 5.9, 'sepal_width': 3.0, 'petal_length': 5.1, 'petal_width': 1.8},
{'sepal_length': 6.0, 'sepal_width': 3.4, 'petal_length': 4.5, 'petal_width': 1.6},
...
]
```
Hint
• `map(float, measurements)`