Parameter passing

Parameter passing is somehow standard in Python. For instance:

def fact(n):
    if n == 0:
        return 1
    else:
        return n * fact(n-1)

fact(50)
30414093201713378043612608166064768844377641568960512000000000000

Default values

Parameters with default values should end the signature:

def f(a, b=0):
    ...
def f(a, b=1):
    return a+b

f(5)
6
Parameters with default values should end the signature.
def f(a, b=0, c=1):
    return a+b+c

Warning when using mutable default values

Mutable default values can lead to unexpected behavior:

def f(b=[]):
    b.append(1)
    return b

f()
f()
[1, 1]

The solution is to replace the mutable default argument by an immutable one as follows.

def f(b=None):
    if b == None:
        b = []
    b.append(1)
    return b

f()
f()
[1]

Positional VS keyword parameters

A positional parameter is characterized by its position. A keyword parameter is characterized by its name.

f(1, b=4)
f(b=4)
etc.
def f(a, b):
    return a-b

f(b=2, a=5)
3

Unpacking arguments

On the calling side, it is possible to unpack tuples/lists and dictionnaries.

Unpacking a tuple/list

For instance,

f(*[1, 2, 3, 4])

is the same as:

f(1, 2, 3, 4)

def f(a, b,c, d):
    return a+b+c+d

f(*[1, 2, 3, 4])
10

Warning: it is impossible to use * elsewhere than in a call.

*[1, 2]
  Cell In[8], line 1
    *[1, 2]
    ^
SyntaxError: can't use starred expression here

Unpacking a dictionnary

With **, you unpack a dictionnary.

f(**{'y': 5, 'z': 6}) is the same as f(y = 5, z= 6).

def f(z, y):
    print(f"z = {z} and y = {y}")

f(**{'y': 5, 'z': 6})
z = 6 and y = 5

You can use both at the same time:

def f(a, b,c, d, y, z):
    return a+b+c+d+y+z

f(*[1, 2, 3, 4], **{'y': 5, 'z': 6})
21

But you cannot give several arguments:

def f(a, b,c, d, y, z):
    return a+b+c+d+y+z

f(*[1, 2, 3, 4], **{'a': 42, 'y': 5, 'z': 6})
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[9], line 4
      1 def f(a, b,c, d, y, z):
      2     return a+b+c+d+y+z
----> 4 f(*[1, 2, 3, 4], **{'a': 42, 'y': 5, 'z': 6})


TypeError: f() got multiple values for argument 'a'

Unpacking in Javascript

In Javascript, unpacking is performed with ...: Math.min(...[2, 3, 1]) is the same as Math.min(2, 3, 1).

Packing arguments

It is possible to get all (remaining) arguments at once. This is called packing. It is possible to get:

  • all (remaining) positional parameters *args as the tuple args
  • all (remaining) keyword parameters **kwargs as a dictionary kwargs
def f(a, b=0, *args, **kwargs):
    return a + b + sum(args) + sum(kwargs.values())

f(1, 2, 3, 4, y=5, z=6)
21

Enforcing parameters to be keyword

A stand-alone * means that the parameters that follows * should be given with keywords.

def f(a, b, *, operator):
    if operator == "+":
        return a+b
    else:
        return a-b

f(3, 5, '+')
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[12], line 7
      4     else:
      5         return a-b
----> 7 f(3, 5, '+')


TypeError: f() takes 2 positional arguments but 3 were given
def f(a, b, *, operator):
    if operator == "+":
        return a+b
    else:
        return a-b

f(3, 5, operator='+')
8

Enforcing parameters to be positional

The / enforces a and b not to be specified by keywords.

def f(a, b, /):
    return a+b

f(3, 5)
8
def f(a, b, /):
    return a+b

f(a=3, b=5)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[11], line 4
      1 def f(a, b, /):
      2     return a+b
----> 4 f(a=3, b=5)


TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'