Rahul Ranjan
Rahul Ranjan

Reputation: 1058

Why do we pass a function as a parameter in python

I understand the process of passing the function as a parameter to a different function but, coming from the c# background, I don't understand the need of it.

Can someone please make me aware of some scenarios in which this is preferred?

Upvotes: 1

Views: 116

Answers (2)

MisterMiyagi
MisterMiyagi

Reputation: 50116

Passing functions into functions allows to parameterise behaviour. This is not unlike passing values into functions allows to parameterise data.

def is_greater(what: int, base: int):
    if what > base:  # fixed behaviour, parameterised data
       print(f'{what} is greater')

def is_valid(what: int, condition: 'Callable'):
    if condition(what):  # parameterised behaviour
       print(f'{what} is valid')

Some common use-cases include:

  • map, filter and others that apply some behaviour to iterables. The functions itself merely implement the "apply to each element" part, but the behaviour can be swapped out:

    >>> print(*map(float, ['1', '2', '3.0'])
    1.0 2.0 3.0
    

    In such situations, one often uses a lambda to define the behaviour on the fly.

    >>> print(sorted(
    ...     ['Bobby Tables', 'Brian Wayne', 'Charles Chapeau'],
    ...     key=lambda name: name.split()[1]),  # sort by last name
    ... )
    ['Charles Chapeau', 'Bobby Tables', 'Brian Wayne']
    
  • Function decorators that wrap a function with additional behaviour.

    def print_call(func):
        """Decorator that prints the arguments its target is called with"""
        def wrapped_func(*args, **kwargs):
            print(f'call {func} with {args} and {kwargs}')
            return func(*args, **kwargs)
        return wrapped_func
    
    @print_call
    def rolling_sum(*numbers, initial=0):
        totals = [initial]
        for number in numbers:
            totals.append(totals[-1] + number)
        return totals
    
    rolling_sum(1, 10, 27, 42, 5, initial=100)
    # call <function rolling_sum at 0x10ed6fd08> with ([1, 10, 27, 42, 5],) and {'initial': 100}
    

    Every time you see a decorator applied with @ it is a higher order function.

  • Callbacks and payloads that are executed at another time, context, condition, thread or even process.

    def call_after(delay: float, func: 'Callable', *args, **kwargs):
        """Call ``func(*args, **kwargs)`` after ``delay`` seconds"""
        time.sleep(delay)
        func(*args, **kwargs)
    
    thread = threading.Thread(
        target=call_after,  # payload for the thread is a function
        args=(1, print, 'Hello World'))
    thread.start()
    print("Let's see what happens...")
    # Let's see what happens...
    # 
    # Hello World
    
  • Passing functions instead of values allows to emulate lazy evaluation.

    def as_needed(expensive_computation, default):
        if random_condition():
           return expensive_computation()
        return default
    

Upvotes: 2

shuberman
shuberman

Reputation: 1490

One of the reasons why passing a function as a parameter is useful is the concept of lambda functions in python.

method2(lambda: method1('world'))
>>> hello world

The benefit of lambda functions are easily visible when used with python functions map(), filter(), and reduce().

Lambda functions with map()

>map(lambda x: x*2, my_list)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38]

Lambda with reduce()

>reduce(lambda x, y: x+y, my_list)
190

Lambda with filter()

filter(lambda x: x >10, my_list)
[11, 12, 13, 14, 15, 16, 17, 18, 19]

Basically unlike c# your code gets reduced number of lines and becomes more efficient since your function call and execution happens on the same line

Upvotes: 4

Related Questions