Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing more than two arguments in reduce function

I am aware that Python reduce only accepts a function with two arguments. However, is there a way to make more than two arguments available to the function? I don't want to make it a global variable because that would be visible for all other imports. The following snippet might help describing the problem (Please read the comments in the snippets):

# The reduce function
def apply_something(something, config):
    # Consrtuct a class object based on the truth value of some other variable.
    # some_var can be changed and is being accessed in different threads so its 
    # not safe to make it global. The reduce function is being called from 
    # inside some other function so It would be better to make 
    # some_var only accessible in the function context.

    if some_var:
        obj = Klass(some_var)
    else:
        obj = Klass()

def callee():
    # This is how I apply the reduce operation.
    reduce(apply_something, [1, 2, 3], something_initializer)

    # I want something like this:
    some_var = True  # So this can be accessed in apply_something

Please provide some insight into this kind of problem.

like image 819
Rafay Avatar asked Dec 05 '22 06:12

Rafay


2 Answers

I think what you're looking for is partial function application, which you can do using functools.

def apply_something(something, config, some_var):
    pass  # ...

import functools

functools.reduce(functools.partial(apply_something, some_var=True), 
       [1, 2, 3], something_initializer)

Example:

>>> def foo(a, b, c):
...     return a + b if c else a * b

>>> functools.reduce(functools.partial(foo, c=True), [1,2,3,4,5], 0)
15

>>> functools.reduce(functools.partial(foo, c=False), [1,2,3,4,5], 1)
120
like image 178
Chris Martin Avatar answered Dec 08 '22 04:12

Chris Martin


Strictly speaking, the function passed to reduce will always be called with two arguments. However, these types of functions will often access variables in an outer scope. Since I'm unclear on your exact problem, let's implement join in terms of reduce:

def join(joining_string, strings_to_join):
    # i.e., join('-', ['a', 'b', 'c']) -> 'a-b-c'
    # Here, we need an extra piece of data in our reduce function:
    #   joining_string.

    def do_reduce(a, b):
        # Joining string comes from the outer scope:
        return a + joining_string + b

    return reduce(do_reduce, strings_to_join)

Also, Chris Martin's solution involving partial is perfectly correct as well. Applying it to the above would look like this:

# This didn't *need* to move out of join, but it can. Before, it needed
# to be in join to be able to "capture" the local variable joining_string.
def do_reduce(a, b, joiner):
    # Joining string comes from the outer scope:
    return a + joiner + b


def join(joining_string, strings_to_join):
    # i.e., join('-', ['a', 'b', 'c']) -> 'a-b-c'
    # Here, we need an extra piece of data in our reduce function:
    #   joining_string.

    return reduce(
        functools.partial(do_reduce, joiner=joining_string),
        strings_to_join)
like image 42
Thanatos Avatar answered Dec 08 '22 06:12

Thanatos