Sideshow Bob
Sideshow Bob

Reputation: 4716

Pythonic way to efficiently handle variable number of return args

So I have a function that can either work quietly or verbosely. In quiet mode it produces an output. In verbose mode it also saves intermediate calculations to a list, though doing so takes extra computation in itself.

Before you ask, yes, this is an identified bottleneck for optimization, and the verbose output is rarely needed so that's fine.

So the question is, what's the most pythonic way to efficiently handle a function which may or may not return a second value? I suspect a pythonic way would be named tuples or dictionary output, e.g.

def f(x,verbose=False):
    result = 0
    verbosity = []
    for _ in x:
        foo = # something quick to calculate
        result += foo
        if verbose:
            verbosity += # something slow to calculate based on foo
    return {"result":result, "verbosity":verbosity}

But that requires constructing a dict when it's not needed.

Some alternatives are:

# "verbose" changes syntax of return value, yuck!
return result if verbose else (result,verbosity)

or using a mutable argument

def f(x,verbosity=None):
    if verbosity:
        assert verbosity==[[]]
    result = 0
    for _ in x:
        foo = # something quick to calculate
        result += foo
        if verbosity:
            # hard coded value, yuck
            verbosity[0] += # something slow to calculate based on foo
    return result

# for verbose results call as
verbosity = [[]]
f(x,verbosity)

Any better ideas?

Upvotes: 11

Views: 359

Answers (2)

user447688
user447688

Reputation:

Don't return verbosity. Make it an optional function argument, passed in by the caller, mutated in the function if not empty.

The non-pythonic part of some answers is the need to test the structure of the return value. Passing mutable arguments for optional processing avoids this ugliness.

Upvotes: 2

Phylogenesis
Phylogenesis

Reputation: 7880

I like the first option, but instead of passing a verbose parameter in the function call, return a tuple of a quick result and a lazily-evaluated function:

import time

def getResult(x):
    quickResult = x * 2

    def verboseResult():
        time.sleep(5)
        return quickResult * 2

    return (quickResult, verboseResult)

# Returns immediately
(quickResult, verboseResult) = getResult(2)

print(quickResult)     # Prints immediately
print(verboseResult()) # Prints after running the long-running function

Upvotes: 0

Related Questions