NirG
NirG

Reputation: 71

In Python, when/why should you return a function?

I am coming from c

The concept of first class function is interesting and exiting. However, I am struggling to find a practical use-case to returning a function.

I have seen the examples of building a function that returns print a grating...

Hello = grating('Hello')

Hi = grating('Hi')

But why is this better then just using grating('Hello') and grating('Hi')?

Consider:

def func_a():
    def func_b():
        do sonthing
        return somthing
     return func_b

When this is better then:

def func_b():
    do sonthing
    return somthing

def func_a():
     res = func_b()
     return (res)

can someone point me to a real world useful example? Thanks!

Upvotes: 1

Views: 76

Answers (5)

Greg
Greg

Reputation: 1989

On example would be creating a timer function. Let's say we want a function that accepts another function, and times how long it takes to complete:

import time
def timeit(func, args):
    st = time.time()
    func(args)
    return str('Took ' + str(time.time() - st) + ' seconds to complete.'

timeit(lambda x: x*10, 10)
# Took 1.9073486328125e-06 seconds to complete

Edit: Using a function that returns a function

import time
def timeit(func):
    st = time.time()
    func(10)
    return str('Took ' + str(time.time() - st) + ' seconds to complete.')

def make_func(x):
    return lambda x: x

timeit(make_func(10))
# Took 1.90734863281e-06 seconds to complete.

Upvotes: 0

ShadowRanger
ShadowRanger

Reputation: 155506

Your examples aren't helpful as written. But there are other cases where it's useful, e.g. decorators (which are functions that are called on functions and return functions, to modify the behavior of the function they're called on for future callers) and closures. The closure case can be a convenience (e.g. some other part of your code doesn't want to have to pass 10 arguments on every call when eight of them are always the same value; this is usually covered by functools.partial, but closures also work), or it can be a caching time saver. For an example of the latter, imagine a stupid function that tests which of a set of numbers less than some bound are prime by computing all primes up to that bound, then filtering the inputs to those in the set of primes.

If you write it as:

def get_primes(*args):
     maxtotest = max(args)
     primes = sieve_of_eratosthenes(maxtotest)  # (expensive) Produce set of primes up to maxtotest
     return primes.intersection(args)

then you're redoing the Sieve of Eratosthenes every call, which swamps the cost of a set intersection. If you implement it as a closure, you might do:

def make_get_primes(initialmax=1000):
    primes = sieve_of_eratosthenes(initialmax)  # (expensive) Produce set of primes up to maxtotest
    currentmax = initialmax
    def get_primes(*args):
        nonlocal currentmax
        maxtotest = max(args)
        if maxtotest > currentmax:
            primes.update(partial_sieve_of_eratosthenes(currentmax, maxtotest))  # (less expensive) Fill in additional primes not sieved
            currentmax = maxtotest 
        return primes.intersection(args)
    return get_primes

Now, if you need a tester for a while, you can do:

get_primes = make_get_primes()

and each call to get_primes is cheap (essentially free if the cached primes already cover you, and cheaper if it has to compute more).

Upvotes: 2

Handerson Contreras
Handerson Contreras

Reputation: 21

The idea of a function that returns a function in Python is for decorators, which is a design pattern that allows to add new functionality to an existing object.

I recommend you to read about decorators

Upvotes: 0

felipe
felipe

Reputation: 8035

A perfect example of a function returning a function is a Python decorator:

def foo(func):
    def bar():
        return func().upper()
    return bar

@foo
def hello_world():
    return "Hello World!"

print(hello_world())

The decorator is the @foo symbol above the hello_world() function declaration. In essence, we tell the interpreter that whenever it sees the mention of hello_world(), to actually call foo(hello_world()), therefore, the output of the code above is:

HELLO WORLD!

Upvotes: 1

Michael Bianconi
Michael Bianconi

Reputation: 5242

Imagine you wanted to pass a function that would choose a function based on parameters:

def compare(a, b):
    ...

def anticompare(a, b):  # Compare but backwards
    ...

def get_comparator(reverse):
    if reverse: return anticompare
    else: return compare

def sort(arr, reverse=false):
    comparator = get_comparator(reverse)
    ...

Obviously this is mildly contrived, but it separates the logic of choosing a comparator from the comparator functions themselves.

Upvotes: 1

Related Questions