One function to print the return value of many functions with different sets of parameters in Python

Viewed 56

I have a number of functions func_1, func_2, ... , func_n, each has its own number of parameters, but all of them return either a numeric value or None.

I want to write a universal printing function that will have as the first parameter the name of one of func_1, func_2, ... , func_n. It should print the return value if it is not None.

Ideally, it should look something like this:

    def universal_print(func, maybe_something_else_I_don't_know):
        if func is not None:
            print(func())    

I don't understand how to pass parameters of the function to be printed to universal_print given that each function has its own number and type of parameters.

Is my problem solvable in general? If yes, how?

3 Answers

Seems like the perfect job for a decorator :

from typing import Callable, Union
def universal_print(function_to_decorate: Callable) -> Callable:
    def wrapper(*args, **kw):
        # Calling your function
        output = function_to_decorate(*args, **kw)
        # Below this line you can do post processing
        if output is not None:
            print(f"your output : {output}")
        return output
    return wrapper

@universal_print
def return_if_odd(n:int) -> Union[int, None]:
    if n%2:
        return n
    return None

Using it:

>>> return_if_odd(1)
your output : 1
1
>>> return_if_odd(2)
2

You might not want the decorator behaviour, which is to be executed every time you call the function. In that case, skip the decoration, and use the function directly:

def return_if_odd(n:int) -> Union[int, None]:
    if n%2:
        return n
    return None
>>> universal_print(return_if_odd)(57)
your output: 57
57

You could get arguments separately using *a, **kw:

def universal_print(func=None, *a, **kw):
    if func is not None:
        rv = func(*a, **kw)
        if rv is not None:
            print(rv)

universal_print(func, func_arg1, func_arg2, func_kwarg1=1)

or you could pass the functions in lambda:

def universal_print(func=None):
    if func is not None:
        rv = func()
        if rv is not None:
            print(rv)

universal_print(lambda: func(func_arg1, func_arg2, func_kwarg1=1))

Lescurel's answer with decorator is great, but it is worth noting that the decorator will replace your original function with the decorated one. If you want to keep the original function intact, you can do:

def universal_print(func: callable, /, *args, **kwargs):
    result = func(*args, **kwargs)
    if result is not None:
        print(result)
    return result

Notes:

  • I am using *args and **kwars to pass arbitrary positional and keyword arguments.
  • I am using the "positional-only arguments" syntax introduced in Python 3.8 (see the slash in the parameter list). It prevents conflicts if a function you pass to universal_print requires an argument called func.
Related