10  Functions 02

10.1 Arguments

10.1.1 Postion & Keywords

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

/ and * are optional. If used, these symbols indicate the kind of parameter by how the arguments may be passed to the function: positional-only, positional-or-keyword, and keyword-only. Keyword parameters are also referred to as named parameters.

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])
cheeseshop("Limburger", 
            # Postional as tuple
            "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
            # Keywords as dict
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch

10.1.2 Unpacking Args

List

list(range(3, 6))            # normal call with separate arguments
[3, 4, 5]
args = [3, 6]
list(range(*args))            # call with arguments unpacked from a list
[3, 4, 5]

Dict

def parrot(voltage, state='a stiff', action='voom'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.", end=' ')
    print("E's", state, "!")

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

10.2 Function Factory

def make_incrementor(n):
    return lambda x: x + n

f = make_incrementor(42)
f(0)
42
f(1)
43

10.3 Function Annotation

def f(ham: str, eggs: str = 'eggs') -> str:
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return ham + ' and ' + eggs

f('spam')
Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
Arguments: spam eggs
'spam and eggs'

NB: Type hinting doesn’t throw error when input the wrong type.

10.4 Function Documentation

def say_hello(name):
    """A simple function that says hello... Richie style"""
    print(f"Hello {name}, is it me you're looking for?")

Obtain docstring by:

  • help()
help(say_hello)
Help on function say_hello in module __main__:

say_hello(name)
    A simple function that says hello... Richie style
  • __doc__
say_hello.__doc__
'A simple function that says hello... Richie style'

print() will invoke str()

print(say_hello)
<function say_hello at 0x12fa79090>
str(say_hello)
'<function say_hello at 0x12fa79090>'
repr(say_hello)
'<function say_hello at 0x12fa79090>'

10.5 Wrapper function

def external_fn(a, b, c=10, d=20, *args, **kwargs):
    print(f"a: {a}, b: {b}, c: {c}, d: {d}")
    print(f"Additional positional arguments: {args}")
    print(f"Additional keyword arguments: {kwargs}")
def my_wrapper(a, b, *args, **kwargs):
    # You can add any pre-processing logic here
    print("Inside my_wrapper:")
    
    # Call the external function with the provided arguments
    external_fn(a, b, *args, **kwargs)

    # You can add any post-processing logic here
    print("Exiting my_wrapper")
my_wrapper(1, 2, 3, 4, 5, e=50, f=60)
Inside my_wrapper:
a: 1, b: 2, c: 3, d: 4
Additional positional arguments: (5,)
Additional keyword arguments: {'e': 50, 'f': 60}
Exiting my_wrapper

10.6 Wrapper Class

class External_Class:
    def __init__(self, a, b, c=10, d=20, *args, **kwargs):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.additional_args = args
        self.additional_kwargs = kwargs
        print(f"External_Class initialized with: a={a}, b={b}, c={c}, d={d}")
        print(f"Additional positional arguments: {args}")
        print(f"Additional keyword arguments: {kwargs}")

    def display(self):
        print(f"a: {self.a}, b: {self.b}, c: {self.c}, d: {self.d}")
        print(f"Additional positional arguments: {self.additional_args}")
        print(f"Additional keyword arguments: {self.additional_kwargs}")
class My_Wrapper(External_Class):
    def __init__(self, a, b, *args, **kwargs):
        # You can add any pre-initialization logic here
        print("Inside My_Wrapper __init__")

        # Initialize the base class (External_Class) with the provided arguments
        super().__init__(a, b, *args, **kwargs)

        # You can add any post-initialization logic here
        print("Exiting My_Wrapper __init__")
# Example usage of My_Wrapper
wrapper_instance = My_Wrapper(1, 2, 3, 4, 5, e=50, f=60)

# Call a method from the base class to see the stored values
wrapper_instance.display()
Inside My_Wrapper __init__
External_Class initialized with: a=1, b=2, c=3, d=4
Additional positional arguments: (5,)
Additional keyword arguments: {'e': 50, 'f': 60}
Exiting My_Wrapper __init__
a: 1, b: 2, c: 3, d: 4
Additional positional arguments: (5,)
Additional keyword arguments: {'e': 50, 'f': 60}