Home > database >  Refactoring nested loop with shared variables
Refactoring nested loop with shared variables

Time:02-15

I have a function that process some quite nested data, using nested loops. Its simplified structure is something like this:

def process_elements(root):
    for a in root.elements:
        if a.some_condition:
            continue
        for b in a.elements:
            if b.some_condition:
                continue
            for c in b.elements:
                if c.some_condition:
                    continue
                for d in c.elements:
                    if d.some_condition:
                        do_something_using_all(a, b, c, d)

This does not look very pythonic to me, so I want to refactor it. My idea was to break it in multiple functions, like:

def process_elements(root):
    for a in root.elements:
        if a.some_condition:
            continue
        process_a_elements(a)
        
def process_a_elements(a):
    for b in a.elements:
        if b.some_condition:
            continue
        process_b_elements(b)
        
def process_b_elements(b):
    for c in b.elements:
        if c.some_condition:
            continue
        process_c_elements(c)
        
def proccess_c_elements(c):
    for d in c.elements:
        if d.some_condition:
            do_something_using_all(a, b, c, d) # Problem: I do not have a nor b!

As you can see, for the more nested level, I need to do something using all its "parent" elements. The functions would have unique scopes, so I couldn't access those elements. Passing all the previous elements to each function (like proccess_c_elements(c, a, b)) does look ugly and not very pythonic to me either...

Any ideas?

CodePudding user response:

I don't know the exact data structures and the complexity of your code but you may try to use a list to pass the object reference to the next daisy chained function something like the following:

def process_elements(root):
    for a in root.elements:
        if a.some_condition:
            continue
        listobjects=[]
        listobjects.append(a)
        process_a_elements(a,listobjects)
        
def process_a_elements(a,listobjects):
    for b in a.elements:
        if b.some_condition:
            continue
        listobjects.append(b)
        process_b_elements(b,listobjects)

def process_b_elements(b,listobjects):
    for c in b.elements:
        if c.some_condition:
            continue
        listobjects.append(c)
        process_c_elements(c,listobjects)
        
def process_c_elements(c,listobjects):
    for d in c.elements:
        if d.some_condition:
            listobjects.append(d)
            do_something_using_all(listobjects)

def do_something_using_all(listobjects):
    print(listobjects)

CodePudding user response:

FWIW, I've found a solution, which is to encapsulate all the proccessing inside a class, and having attributes to track the currently processed elements:

class ElementsProcessor:
    def __init__(self, root):
        self.root = root
        
        self.currently_processed_a = None
        self.currently_processed_b = None
        
    def process_elements(self):
        for a in self.root.elements:
            if a.some_condition:
                continue
            self.currently_processed_a = a
            self.process_a_elements(a)
            
    def process_a_elements(self, a):
        for b in a.elements:
            if b.some_condition:
                continue
            self.currently_processed_b = b
            self.process_b_elements(b)
            
    def process_b_elements(self, b):
        for c in b.elements:
            if c.some_condition:
                continue
            self.process_c_elements(c)
    
    def process_c_elements(self, c):
        for d in c.elements:
            if d.some_condition:
                do_something_using_all(
                    self.currently_processed_a,
                    self.currently_processed_b,
                    c,
                    d
                )
  • Related