# 2.5. Unpack Parameter Syntax¶

## 2.5.1. Recap¶

>>> def echo(a, b, c=3):
...      print(f'{a=} {b=} {c=}')
>>>
>>>
>>> echo(1, 2)
a=1 b=2 c=3
>>>
>>> echo(1, b=2)
a=1 b=2 c=3
>>>
>>> echo(1, c=3)
Traceback (most recent call last):
TypeError: echo() missing 1 required positional argument: 'b'
>>>
>>> echo(1, 2, 3)
a=1 b=2 c=3
>>>
>>> echo(1, 2, c=3)
a=1 b=2 c=3
>>>
>>> echo(1, b=2, c=3)
a=1 b=2 c=3
>>>
>>> echo(a=1, b=2, c=3)
a=1 b=2 c=3


## 2.5.2. Rationale¶

• Define API for you functions

• Require particular way of passing positional and optional parameters

## 2.5.3. Keyword-only Parameters¶

• All parameters after * must be keyword-only

>>> def echo(a, *, b, c=3):
...     print(f'{a=} {b=} {c=}')
>>>
>>>
>>> echo(1, 2)
Traceback (most recent call last):
TypeError: echo() takes 1 positional argument but 2 were given
>>>
>>> echo(1, b=2)
a=1 b=2 c=3
>>>
>>> echo(1, c=3)
Traceback (most recent call last):
TypeError: echo() missing 1 required keyword-only argument: 'b'
>>>
>>> echo(1, 2, 3)
Traceback (most recent call last):
TypeError: echo() takes 1 positional argument but 3 were given
>>>
>>> echo(1, 2, c=3)
Traceback (most recent call last):
TypeError: echo() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given
>>>
>>> echo(1, b=2, c=3)
a=1 b=2 c=3
>>>
>>> echo(a=1, b=2, c=3)
a=1 b=2 c=3


## 2.5.4. Positional-only Parameters¶

• Since Python 3.8: PEP 570 -- Python Positional-Only Parameters

• All parameters before / must be positional-only

>>> def echo(a, /, b, c=3):
...     print(f'{a=} {b=} {c=}')
>>>
>>>
>>> echo(1, 2)
a=1 b=2 c=3
>>>
>>> echo(1, b=2)
a=1 b=2 c=3
>>>
>>> echo(1, c=3)
Traceback (most recent call last):
TypeError: echo() missing 1 required positional argument: 'b'
>>>
>>> echo(1, 2, 3)
a=1 b=2 c=3
>>>
>>> echo(1, 2, c=3)
a=1 b=2 c=3
>>>
>>> echo(1, b=2, c=3)
a=1 b=2 c=3
>>>
>>> echo(a=1, b=2, c=3)
Traceback (most recent call last):
TypeError: echo() got some positional-only arguments passed as keyword arguments: 'a'


## 2.5.5. Positional and Keyword Parameters¶

>>> def echo(a, /, b, *, c=3):
...     print(f'{a=} {b=} {c=}')
>>>
>>>
>>> echo(1, 2)
a=1 b=2 c=3
>>>
>>> echo(1, b=2)
a=1 b=2 c=3
>>>
>>> echo(1, c=3)
Traceback (most recent call last):
TypeError: echo() missing 1 required positional argument: 'b'
>>>
>>> echo(1, 2, 3)
Traceback (most recent call last):
TypeError: echo() takes 2 positional arguments but 3 were given
>>>
>>> echo(1, 2, c=3)
a=1 b=2 c=3
>>>
>>> echo(1, b=2, c=3)
a=1 b=2 c=3
>>>
>>> echo(a=1, b=2, c=3)
Traceback (most recent call last):
TypeError: echo() got some positional-only arguments passed as keyword arguments: 'a'


## 2.5.6. Use Case - 0x01¶

>>> def add(a, b, /):
...     return a + b


## 2.5.7. Use Case - 0x02¶

• Divmod

>>> def divmod(a, b, /):
...     return a//b, a%b


## 2.5.8. Use Case - 0x03¶

• Sorted

• sorted(iterable, /, *, key=None, reverse=False)

>>> from inspect import signature
>>>
>>>
>>> signature(sorted)
<Signature (iterable, /, *, key=None, reverse=False)>


## 2.5.9. Use Case - 0x04¶

• Sum

• sum(iterable, /, start=0)

>>> from inspect import signature
>>>
>>>
>>> signature(sum)
<Signature (iterable, /, start=0)>


## 2.5.10. Use Case - 0x05¶

>>> from inspect import signature
>>>
>>>
>>> signature(str.strip)
<Signature (self, chars=None, /)>


## 2.5.11. Use Case - 0x06¶

>>> from inspect import signature
>>>
>>>
>>> signature(str.split)
<Signature (self, /, sep=None, maxsplit=-1)>


## 2.5.12. Assignments¶

"""
* Assignment: Unpack ParameterSyntax Kwargs
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min

English:
1. Create function set_position
2. Function takes two arguments x, y and always returns None
3. Arguments must be passed only as keywords
4. Run doctests - all must succeed

Polish:
1. Stwórz funkcję set_position
2. Funkcja przyjmuje dwa argumenty x, y i zawsze zwraca None
3. Argumenty można podawać tylko nazwanie (keyword)
4. Uruchom doctesty - wszystkie muszą się powieść

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

>>> assert callable(set_position)
>>> assert isfunction(set_position)

>>> set_position(x=1, y=2)

>>> set_position()  # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
TypeError: set_position() missing 2 required keyword-only arguments: 'x'
and 'y'

>>> set_position(1)
Traceback (most recent call last):
TypeError: set_position() takes 0 positional arguments but 1 was given

>>> set_position(1, 2)
Traceback (most recent call last):
TypeError: set_position() takes 0 positional arguments but 2 were given

>>> set_position(1, y=1)  # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
TypeError: set_position() takes 0 positional arguments but 1 positional
argument (and 1 keyword-only argument) were given

>>> set_position(x=1, 2)
Traceback (most recent call last):
SyntaxError: positional argument follows keyword argument
"""

# callable: Arguments must be passed only as keywords
def set_position(x, y):
pass


"""
* Assignment: Unpack ParameterSyntax Args
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
* Warning: This assignment will work only in Python 3.8+

English:
1. Refactor function take_damage
2. Function takes one argument dmg and always returns None
3. Argument must be passed only as positional
4. Run doctests - all must succeed

Polish:
1. Zrefaktoruj funkcję take_damage
2. Funkcja przyjmuje jeden argument dmg i zawsze zwraca None
3. Argument można podawać tylko pozycyjnie
4. Uruchom doctesty - wszystkie muszą się powieść

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

>>> assert callable(take_damage)
>>> assert isfunction(take_damage)

>>> take_damage(1)

>>> take_damage(1, 2)
Traceback (most recent call last):
TypeError: take_damage() takes 1 positional argument but 2 were given

>>> take_damage()
Traceback (most recent call last):
TypeError: take_damage() missing 1 required positional argument: 'dmg'

>>> take_damage(dmg=1)  # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
TypeError: take_damage() got some positional-only arguments passed as
keyword arguments: 'dmg'
"""

# callable: Argument must be passed only as positional
def take_damage(dmg):
pass


"""
* Assignment: Unpack ParameterSyntax Mixed
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
* Warning: This assignment will work only in Python 3.8+

English:
1. Create function compute
2. Function takes 4 arguments a, b, c, func and always returns None
3. Arguments a, b, c must be passed only as positional, and func as keyword
4. Run doctests - all must succeed

Polish:
1. Stwórz funkcję compute
2. Funkcja przyjmuje cztery argumenty a, b, c, func i zawsze zwraca None
3. Argumenty a, b, c można podawać pozycyjnie, a func keyword
4. Uruchom doctesty - wszystkie muszą się powieść

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

>>> assert callable(compute)
>>> assert isfunction(compute)

>>> compute(1, 2, 3)
>>> compute(1, 2, 3, func=lambda:None)

>>> compute(1, 2)
Traceback (most recent call last):
TypeError: compute() missing 1 required positional argument: 'c'

>>> compute()  # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
TypeError: compute() missing 3 required positional arguments: 'a', 'b',
and 'c'

>>> compute(1, 2, 3, lambda:None)
Traceback (most recent call last):
TypeError: compute() takes 3 positional arguments but 4 were given

>>> compute(a=1, b=2, c=3)  # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
TypeError: compute() got some positional-only arguments passed as
keyword arguments: 'a, b, c'
"""

# callable: Argument a,b,c must be passed only as positional, func as keyword
def compute(a, b, c, func=lambda:...):
pass