How to change order of lists for nested loops

Question:

I am trying to write a code to change the order of called lists in nested loops.

The code I’m currently using is below. This is a simplified example, so I have removed all the extra functions and documentation. The real code is for creating plots based on the attribute set by variable loop_by.
This code works, but is very long and has a lot of copy/paste code. Is there a better/pythonic way of doing this?

It is important to note, names must be of the format {color}_{shape}_{size}_{number} due to the names of the data files.
I have tried using the product from itertools in the logic block, but the name format is incorrect.

Code:

# Initial lists
colors = ['Red', 'Orange', 'Yellow', 'Green', 'Blue']
shapes = ['Circle', 'Triangle', 'Square', 'Rectangle']
sizes = ['small', 'medium', 'large']
numbers = [i for i in range(7)] #could be any number


# Which loop I am trying to use
loop_by = 'color'
    #loop_by = 'shape'
    #loop_by = 'size'
    #loop_by = 'number'


# Loop functions
def by_color():
    for shape in shapes:
        for size in sizes:
            for number in numbers:
                print('---')
                #call to other functions for more calculations
                for color in colors:
                    name = f'{color}_{shape}_{size}_{number}'
                    print(name)
                    #call to other functions for more calculations


def by_shape():
    for color in colors:
        for size in sizes:
            for number in numbers:
                print('---')
                #call to other functions for more calculations
                for shape in shapes:
                    name = f'{color}_{shape}_{size}_{number}'
                    print(name)
                    #call to other functions for more calculations


def by_size():
    for color in colors:
        for shape in shapes:
            for number in numbers:
                print('---')
                #call to other functions for more calculations
                for size in sizes:
                    name = f'{color}_{shape}_{size}_{number}'
                    print(name)
                    #call to other functions for more calculations


def by_number():
    for color in colors:
        for shape in shapes:
            for size in sizes:
                print('---')
                #call to other functions for more calculations
                for number in numbers:
                    name = f'{color}_{shape}_{size}_{number}'
                    print(name)
                    #call to other functions for more calculations


# Logic block to choose loop
if loop_by == 'color':
    by_color()
elif loop_by == 'shape':
    by_shape()
elif loop_by == 'size':
    by_size()
elif loop_by == 'number':
    by_number()
else:
    print('This loop is not possible')
Asked By: iceboy1029

||

Answers:

I would clean it up by extracting out some methods, and using comprehensions, like this

def create_output(loop_by, colors, shapes, sizes, numbers):
    def parse(color, shape, size, number):
        match loop_by:
            case 'color':
                return f'{color}_{shape}_{size}_{number}'
            case 'shape':
                return f'{shape}_{color}_{size}_{number}'
            case 'size':
                return f'{size}_{color}_{shape}_{number}'
            case 'number':
                return f'{number}_{color}_{shape}_{size}'
            case _:
                raise ValueError()

    return [
        parse(c, s, z, n)
        for c in colors
        for s in shapes
        for z in sizes
        for n in numbers
    ]


if __name__ == '__main__':
    output = create_output(
        loop_by="color",
        colors=['Red', 'Orange', 'Yellow', 'Green', 'Blue'],
        shapes=['Circle', 'Triangle', 'Square', 'Rectangle'],
        sizes=['small', 'medium', 'large'],
        numbers=(range(7))
    )
    
    [print(x) for x in output]
Answered By: Blake
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.