5. Passing many arguments

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

echo(1, 2)
Listing 299. 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)

5.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 300. 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 301. 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)

5.3. Arbitrary number of positional and keyword arguments

Listing 302. 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 303. 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)

5.4. Examples

5.4.1. Creating complex numbers

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

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

5.4.2. Vectors

Listing 306. 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)

5.4.4. Common configuration

Listing 308. 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 309. 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 310. Database connection configuration read from config file
config = {
    'host': 'localhost',
    'port': 5432,
    'username': 'my_username',
    'password': 'my_password',
    'database': 'my_database',
}


def database_connect(host, port, username, password, database):
    return ...


connection = database_connect(**config)

5.4.5. Calling function with all variables from higher order function

Listing 311. 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

5.4.6. Proxy functions

Listing 312. 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,
             tupleize_cols=None, error_bad_lines=True, warn_bad_lines=True,
             skipfooter=0, doublequote=True, delim_whitespace=False, low_memory=True,
             memory_map=False, float_precision=None):
    """
    Definition of pandas.read_csv() function
    https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html
    """


def my_csv(file, encoding='utf-8', *args, **kwargs):
    return read_csv(file, encoding=encoding, *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'])

5.4.7. Decorators

Listing 313. 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


@login_required
def edit_profile(request):
    ...

5.5. Assignments

5.5.1. Iris

  • Complexity level: medium

  • Lines of code to write: 15 lines

  • Estimated time of completion: 20 min

  • Filename: solution/calling_kwargs.py

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

  2. Separate header from measurements

  3. Remove species column from both header and measurements

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

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

    • key: column name from the header

    • value: measurement at the position

  6. Create function average(**kwargs), function

  7. Iterate over data and calculate mean for each row by passing arguments as keywords

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

  2. Odseparuj nagłówek od pomiarów

  3. Usuń kolumnę species zarówno z nagłówka jak i danych

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

  5. Stwórz data: List[dict] poprzez scalenie nagłówka i pomiarów

    • klucz: nazwa kolumny z nagłówka

    • wartość: pomiar z odpowiedniej kolumny

  6. Stwórz funkcję average(**kwargs)

  7. Iterując po data wylicz średnią dla każdego wiersza przez podawanie argumentów nazwanie

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']

data: 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},
    ...
]