Positional and Keyword Arguments

Traditional Function Call

  • An ordinary function, taking two parameters a and b

    def f(a, b):
        print(a, b)
    
  • Called with positional arguments

    f(1, 2)
    
    1 2
    
  • Called with keyword arguments

    f(a=3, b=4)
    
    3 4
    

Supplying Function Arguments Dynamically

  • Positional arguments: sequence

    args = [1, 2]
    f(*args)
    
    1 2
    

    Any sequence is sufficient - e.g. range()

    args = range(1, 3)
    f(*args)
    
    1 2
    
  • Keyword arguments: mapping (usually dict)

    kwargs = {'a':1, 'b':2}
    f(**kwargs)
    
    1 2
    

Functions That Takes Arbitrary Number Of Positional Arguments

  • The other way around: not passing parameters dynamically, but accepting them dynamically

  • “Starargs”: *args function parameter

  • args is just a common name, can be arbitrary

def f(*args):                     # <--- starargs
    print(args, type(args))       # <--- will print *tuple*
  • Called traditionally, with positional arguments

  • Any number of arguments possible

f(1, 2)
f(1, 2, 3)
(1, 2) <class 'tuple'>
(1, 2, 3) <class 'tuple'>

Functions That Take Arbitrary Keyword Arguments

  • Much like positional args, but keyword args and dict

  • “Starstarargs”: **kwargs function parameter

  • Again, kwargs is just a common name, can be arbitrary

def f(**kwargs):                  # <--- starstarargs
    print(kwargs, type(kwargs))   # <--- will print *dict*
  • Called traditionally, with keyword arguments

  • Any number of arguments possible

f(a=1, b=2)
f(a=1, b=2, c=3)
{'a': 1, 'b': 2} <class 'dict'>
{'a': 1, 'b': 2, 'c': 3} <class 'dict'>

And Arbitrary Positional And Keyword Arguments?

  • Rule: positional Arguments must come before keyword arguments

def f(*args, **kwargs):
    print(args)
    print(kwargs)

f(1, 2, a=3, b=4)
(1, 2)
{'a': 3, 'b': 4}

Ultimate Dynamicity: Arbitrary Positional And Keyword Arguments

  • Pass-through: accept any number and of arguments

  • Pass them on as they were passed

  • Typical usecase: decorators

def f(*args, **kwargs):
    print(*args, **kwargs)

f(1, 2, end='\n(done)\n')            # <--- actually, this *is* print
1 2
(done)

Use Case: A Better print()

  • Works like print()

  • Has a number of bells and whistles of its own

  • indent

import sys

def my_print(*args, **kwargs):
    indent = kwargs.pop('indent')    # <--- remove from kwargs
    if indent is not None:
        args = (' '*indent,) + args
    print(*args, **kwargs)           # <--- pass rest to print

my_print(1, 2,
         end='\n(done)\n',           # <--- consumed by print
         indent=4,                   # <--- consumed by my_print
         )
     1 2
(done)