Modularization of Python code with function specific parameters

127 Views Asked by At

My code contains multiple lines of a nested for loop . I wanted to try and reduce the amount of nested for loops being called by putting the code into its own function similar to the do_for_each function below. Within the nested for loop I want to call another function. Below is a working example:

def do_for_each(self, func, h, w, init_data):
        for x in range(1, h):
            for y in range(1, w):
                init_data[x,y] = func(x, y)
        return init_data

def calculate_land_neighbours(self, x, y):
        return self.lscape[x-1,y] + self.lscape[x+1,y] + self.lscape[x,y-1] + self.lscape[x,y+1]

ouput = self.do_for_each(self.calculate_land_neighbours, self.h+1, self.w+1, data)

However, my problem arises when the function called within do_for_each does not take parameters x or y but another variable. For instance a new function would look like:

def add_to_density_array(self, seed):
        if seed == 0:
            return 0
        else:
            return random.uniform(0, 5.0)

output2 = self.do_for_each(self.add_to_density_array, self.h+1, self.w+1, data, seed)

For this to run properly I would need to amend my do_for_each function to:

def do_for_each(self, func, h, w, init_data, seed):
        for x in range(1, h):
            for y in range(1, w):
                init_data[x,y] = func(seed)
        return init_data

Would anyone have any recommendations where I could keep the modularised code of the do_for_each function but call functions within do_for_each that do not have the same input parameters?

3

There are 3 best solutions below

0
On

You can use default parameter of seed as None and check if it exits and operate accordingly:

def do_for_each(self, func, h, w, init_data, seed=None):
    for x in range(1, h):
        for y in range(1, w):
            # If no seed provided
            if seed is None:
                init_data[x, y] = func(x, y)
            # Else, seed provided - use it
            else:
                init_data[x, y] = func(seed)
    return init_data

This has the advantage of backward compatibility with the "old" do_for_each(self, func, h, w, init_data).

0
On

Fundamentally, your two do_for_each functions are just very different. They don’t really perform the same transformation at all — one changes matrix entries as a function of the x, y indices, and another doesn’t.

I would consider using a different abstraction here.

That said, you can make this work without changing the original do_for_each: pass a lambda that wraps add_to_density_array, instead of passing the latter directly:

add_to_density = lambda x, y: self.add_to_density_array(seed)
output2 = self.do_for_each(add_to_density, self.h+1, self.w+1, data)
0
On

You can try out Overloading Functions, here is a possible solution. You can read more here - https://www.geeksforgeeks.org/python-method-overloading/

def do_for_each(self, func, h, w, init_data, seed=None):
    if seed==None:
        for x in range(1, h):
            for y in range(1, w):
                init_data[x,y] = func(x, y)
        return init_data
    else:
        for x in range(1, h):
            for y in range(1, w):
                init_data[x,y] = func(seed)
        return init_data