# 14.4. Function Arguments

• Argument - value/variable/reference being passed to the function

• Positional argument - value passed to function - order is important

• Keyword argument - value passed to function resolved by name - order is not important

Function definition with parameters:

myfunction(<arguments>)

>>> add(1, 2)

argument

Value/variable/reference being passed to the function

positional argument

Value passed to function - order is important

keyword argument

Value passed to function resolved by name - order is not important

## 14.4.1. Positional Arguments

• Order of positional arguments has significance

Let's define a function:

>>> def echo(a, b):
...     return f'{a=}, {b=}'


Positional arguments are resolved by order. This mean, that the first argument will be assigned to the first parameter, and the second argument to the second parameter and so on:

>>> echo(1, 2)
'a=1, b=2'


The order of positional parameters is important:

>>> echo(2, 1)
'a=2, b=1'


## 14.4.2. Keyword Arguments

• Order of keyword arguments has no significance

Let's define a function:

>>> def echo(a, b):
...     return f'{a=}, {b=}'


Keyword arguments are resolved by name instead of the position. This mean, that the argument with particular name will be assigned to the corresponding parameter with the same name in function signature.

>>> echo(a=1, b=2)
'a=1, b=2'


The order of keyword parameters is not important, because values are assigned by name, not a position:

>>> echo(b=2, a=1)
'a=1, b=2'


## 14.4.3. Positional and Keyword Arguments

• Positional arguments must be at the left side

• Keyword arguments must be at the right side

>>> def echo(a, b):
...     return f'{a=}, {b=}'


All positional arguments must be on the left side, and all the required arguments must be on the right side:

>>> echo(1, b=2)
'a=1, b=2'


Passing positional argument which follows keyword argument will yield a SyntaxError:

>>> echo(a=1, 2)
Traceback (most recent call last):
SyntaxError: positional argument follows keyword argument


Positional argument are resolved first. Defining keyword argument which follows positional argument with the same name will yield a TypeError:

>>> echo(1, a=2)
Traceback (most recent call last):
TypeError: echo() got multiple values for argument 'a'


## 14.4.4. Errors

>>> def echo(a, b, c, d, e):
...     return f'{a=}, {b=}, {c=}, {d=}, {e=}'
>>>
>>>
>>> echo(1, 2, 3)
Traceback (most recent call last):
TypeError: echo() missing 2 required positional arguments: 'd' and 'e'
>>>
>>> echo(1, 2, 3, d=4)
Traceback (most recent call last):
TypeError: echo() missing 1 required positional argument: 'e'
>>>
>>> echo(1, 2, 3, d=4, 5)
Traceback (most recent call last):
SyntaxError: positional argument follows keyword argument
>>>
>>> echo(1, 2, 4, 5, c=3)
Traceback (most recent call last):
TypeError: echo() got multiple values for argument 'c'
>>>
>>> echo(1, 2, 3, d=4, e=5)
'a=1, b=2, c=3, d=4, e=5'


## 14.4.5. Use Case - 0x01

>>> def say_hello(text='say what?'):
...      return text
>>>
>>>
>>> say_hello('hello')
'hello'
>>>
>>> say_hello(text='hello world')
'hello world'
>>>
>>> say_hello()
'say what?'


## 14.4.6. Use Case - 0x02

>>> def connect(*args, **kwargs):
...     pass

>>> connect('myusername', 'mypassword')

>>> connect('myusername', 'mypassword', 'example.com', 443, False, 1, True)

>>> connect(host='example.com', username='myusername', password='mypassword')

>>> connect(
...     host='example.com',
...     port=443,
...     ssl=True,
...     persistent=True)


## 14.4.7. Use Case - 0x03

>>> def read_csv(*args, **kwargs):
...     pass


Calling function with positional only arguments is insane. In Python we don't do that, because we have keyword arguments.

>>> read_csv('myfile.csv', ';', None, 'infer', None, None, None, False, None,
...          True, None, None, None, None, None, False, None, None, None,
...          True, True, False, True, False, False, False, None, False,
...          False, None, 'infer', None, b',', None, '"', 0, None, None,
...          None, None, None, True, True, 0, True, False, True, False, None)


Keyword arguments with sensible defaults are your best friends. The number of function parameters suddenly is not a problem:

>>> read_csv('myfile1.csv', delimiter=';', decimal=b',')

>>> read_csv('iris.csv')

>>> read_csv('iris.csv', encoding='utf-8')

>>> read_csv('iris.csv', encoding='utf-8', parse_dates=['birthdate'])

>>> read_csv('iris.csv', skiprows=3, delimiter=';')

>>> read_csv('iris.csv',
...     encoding='utf-8',
...     skiprows=3,
...     delimiter=';',
...     usecols=['sepal_length', 'species'],
...     parse_dates=['birthdate'])


## 14.4.8. Assignments

# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Sequence
* Type: class assignment
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
1. Define function total:
a. parameter: list of integers (required)
b. return: sum only even numbers
2. Use even() to check if number is even
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję total:
a. parametr: lists liczb całkowitych (wymagany)
b. zwraca: sumę tylko parzystych liczb
2. Użyj even() do sprawdzenia czy liczba jest parzysta
3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert total is not Ellipsis, \
'Write solution inside total function'
>>> assert isfunction(total), \
'Object total must be a function'

>>> data = [1, 2, 3, 4]
>>> total(data)
6

>>> data = [2, -1, 0, 2]
>>> total(data)
4

>>> data = list(range(0,101))
>>> total(data)
2550
"""

def even(x):
return x % 2 == 0

# Define function total():
# - parameter: sequence of integers
# - return: sum only even numbers
# Use even() to check if number is even
# type: Callable[[list[int]], int]
def total():
...


# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Divide
* Type: class assignment
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
1. Define function divide:
a. parameter a: int - first number (required)
b. parameter b: int - second number (required)
c. return result of division of both arguments
2. If division cannot be made raise ValueError
with message "Argument b cannot be zero"
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję divide:
a. parametr a: int - pierwsza liczba (wymagany)
b. parametr b: int - druga liczba (wymagany)
c. zwróć wynik dzielenia obu argumentów
2. Jeżeli dzielenie nie może być wykonane podnieś ValueError
z komunikatem "Argument b cannot be zero"
3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert divide is not Ellipsis, \
'Write solution inside divide function'
>>> assert isfunction(divide), \
'Object divide must be a function'

>>> divide(4, 0)
Traceback (most recent call last):
ValueError: Argument b cannot be zero

>>> divide(4, 2)
2.0
"""

# Define function divide:
# - parameter a: int - first number
# - parameter b: int - second number
# - return result of division of both arguments
# If division cannot be made raise ValueError
# with message "Argument b cannot be zero"
# type: Callable[[int, int], float]
def divide(a, b):
...


# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Power
* Type: class assignment
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
1. Define function power:
a. parameter a: int - first number (required)
b. parameter b: int - second number (optional)
c. returns: power of the first argument to the second
2. If only one argument was passed,
consider second equal to the first one
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję power:
a. parametr a: int - pierwsza liczba (wymagany)
b. parametr b: int - druga liczba (opcjonalny)
c. zwraca: wynik pierwszego argumentu do potęgi drugiego
2. Jeżeli tylko jeden argument był podany,
przyjmij drugi równy pierwszemu
6. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert power is not Ellipsis, \
'Write solution inside power function'
>>> assert isfunction(power), \
'Object power must be a function'

>>> power(4, 3)
64
>>> power(3)
27
"""

# Define function power:
# - parameter a: int - first number (required)
# - parameter b: int - second number (optional)
# - returns: power of the first argument to the second
# If only one argument was passed,
# consider second equal to the first one
# type: Callable[[int, int], int]
def power():
...


# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Translate
* Type: class assignment
* Complexity: easy
* Lines of code: 2 lines
* Time: 5 min

English:
1. Define function translate:
a. parameter text: str (required)
b. return str with translated text
2. If letter is in PL then substitute letter,
otherwise take original letter
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję translate:
a. parametr text: str (wymagany)
b. zwróć str z przetłumaczonym tekstem
2. Jeżeli litera jest w PL to podmień literę,
w przeciwnym przypadku to weź oryginalną literę
3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert translate is not Ellipsis, \
'Write solution inside translate function'
>>> assert isfunction(translate), \
'Object translate must be a function'

>>> translate('zażółć')
'zazolc'
>>> translate('gęślą')
'gesla'
>>> translate('jaźń')
'jazn'
>>> translate('zażółć gęślą jaźń')
'zazolc gesla jazn'
"""

PL = {
'ą': 'a',
'ć': 'c',
'ę': 'e',
'ł': 'l',
'ń': 'n',
'ó': 'o',
'ś': 's',
'ż': 'z',
'ź': 'z',
}

# Define function translate:
# - parameter text: str (required)
# - return str with translated text
# If letter is in PL then substitute letter,
# otherwise take original letter
# type: Callable[[str], str]
def translate():
...


# TODO: to zadanie bardziej pasuje do function_parameters
# TODO: Translate input data to English
# TODO: zamieniać wiele spacji na jedną, a nie na pusty znak
"""
* Assignment: Function Arguments Clean
* Type: homework
* Complexity: medium
* Lines of code: 15 lines
* Time: 13 min

English:
1. Define function clean:
a. parameter text: str (required)
b. returns str with cleaned text
2. To clean text:
a. Remove unwanted whitespaces
b. Remove unwanted special characters
c. Remove unwanted fragments
d. Format text
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję clean:
a. parametr text: str (wymagany)
b. zwraca str z oczyszczonym tekstem
2. Aby oczyścić tekst:
a. Usuń niechciane białe znaki
b. Usuń niechciane znaki specjalne
c. Usuń niechciane fragmenty
d. Sformatuj tekst
3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert clean is not Ellipsis, \
'Write solution inside clean function'
>>> assert isfunction(clean), \
'Object clean must be a function'

>>> clean('ul.Mieszka II')
'Mieszka II'
>>> clean('UL. Zygmunta III WaZY')
'Zygmunta III Wazy'
>>> clean('  bolesława chrobrego ')
'Bolesława Chrobrego'
>>> clean('ul Jana III SobIESkiego')
'Jana III Sobieskiego'
>>> clean('\tul. Jana trzeciego Sobieskiego')
'Jana III Sobieskiego'
>>> clean('ulicaJana III Sobieskiego')
'Jana III Sobieskiego'
>>> clean('UL. JA    NA 3 SOBIES  KIEGO')
'Jana III Sobieskiego'
>>> clean('ULICA JANA III SOBIESKIEGO  ')
'Jana III Sobieskiego'
>>> clean('ULICA. JANA III SOBIeskieGO')
'Jana III Sobieskiego'
>>> clean(' Jana 3 Sobieskiego  ')
'Jana III Sobieskiego'
>>> clean('Jana III Sobi  eskiego ')
'Jana III Sobieskiego'
"""

# Define function clean:
# - parameter text: str (required)
# - returns str with cleaned text
#
# To clean text:
# - Remove unwanted whitespaces
# - Remove unwanted special characters
# - Remove unwanted fragments
# - Format text
# type: Callable[[str], str]
...


# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Num2Str
* Type: homework
* Complexity: medium
* Lines of code: 5 lines
* Time: 8 min

English:
1. Given is pilot's alphabet for numbers
2. Convert DATA: dict[int, str] to data: dict[str,str] (keys as str)
3. Define function pilot_say converting int or float
to text form in Pilot's Speak
4. You cannot change DATA, but you can modify data
5. Run doctests - all must succeed

Polish:
1. Dany jest alfabet pilotów dla numerów
2. Przekonwertuj DATA: dict[int, str] na data: dict[str,str]
(klucze jako str)
3. Zdefiniuj funkcję pilot_say konwertującą int lub float
na formę tekstową w mowie pilotów
4. Nie możesz zmieniać DATA, ale możesz modyfikować data
5. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert pilot_say is not Ellipsis, \
'Write solution inside pilot_say function'
>>> assert isfunction(pilot_say), \
'Object pilot_say must be a function'

>>> pilot_say(1)
'one'
>>> pilot_say(+1)
'one'
>>> pilot_say(-1)
'minus one'
>>> pilot_say(1+1)
'two'
>>> pilot_say(1-1)
'zero'
>>> pilot_say(1969)
'one niner six niner'
>>> pilot_say(31337)
'tree one tree tree seven'
>>> pilot_say(13.37)
'one tree and tree seven'
>>> pilot_say(31.337)
'tree one and tree tree seven'
>>> pilot_say(-1969)
'minus one niner six niner'
>>> pilot_say(-31.337)
'minus tree one and tree tree seven'
>>> pilot_say(-49.35)
'minus fower niner and tree fife'
>>> pilot_say(1.0)
'one and zero'
>>> pilot_say(1.)
'one and zero'
>>> pilot_say(123.)
'one two tree and zero'
>>> pilot_say(123.0)
'one two tree and zero'
>>> pilot_say(.44)
'zero and fower fower'
>>> pilot_say(1-)
Traceback (most recent call last):
SyntaxError: invalid syntax
"""

DATA = {
0: 'zero',
1: 'one',
2: 'two',
3: 'tree',
4: 'fower',
5: 'fife',
6: 'six',
7: 'seven',
8: 'ait',
9: 'niner',
}

# Given is pilot's alphabet for numbers
# Convert DATA: dict[int, str] to data: dict[str,str] (keys as str)
# Define function pilot_say converting int or float
# to text form in Pilot's Speak
# You cannot change DATA, but you can modify data
# type: Callable[[int|float], str]
...


# TODO: to zadanie bardziej pasuje do function_parameters
"""
* Assignment: Function Arguments Range
* Type: homework
* Complexity: medium
* Lines of code: 7 lines
* Time: 13 min

English:
1. Define function myrange with parameters: start, stop, step
2. Write own implementation of a built-in function
myrange(start, stop, step)
3. Do not use built-in range() function
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj funkcję myrange z parametrami: start, stop, step
2. Zaimplementuj własne rozwiązanie wbudowanej funkcji
myrange(start, stop, step)
3. Nie używaj wbudowanej funkcji range()
4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* while

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert myrange is not Ellipsis, \
'Write solution inside myrange function'
>>> assert isfunction(myrange), \
'Object myrange must be a function'

>>> myrange(0, 10, 2)
[0, 2, 4, 6, 8]

>>> myrange(0, 5)
[0, 1, 2, 3, 4]
"""

# Define function myrange with parameters: start, stop, step
# Write own implementation of a built-in function
# myrange(start, stop, step)
# Do not use built-in range() function
# type: Callable[[int, int, int], list[int]]
...