pythonpython-3.xyield

Converting list function to generator using yield


I am trying to convert a for loop into an iterator using yield, but I have failed in my attempts. I don't understand exactly why the yield isn't giving me the expected output. Does anyone know what the problem is?

Attempt at using yield:

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                order[idx] += sgn
                yield order
print(list(iteration_order(2))
>>> [[0, 0, 0], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]

The code as it should work (when not using yield):

def iteration_order(dimensions):
    full_order = []
    for dim in range(dimensions):
        order = [[0, dim, 0]]
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                nxt = list(order[-1])
                nxt[idx] += sgn
                order.append(nxt)

        full_order.extend(order)
    return full_order
print(iteration_order(2))
>>> [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]

Solution

  • The problem you see is because you are using that same list for everything. You might yield it with different value, but the generator still has reference to that list and it modifies it, giving you weird output. If you add .copy() for each yield, they will be unique lists and will behave as expected:

    def iteration_order(dimensions):
        for dim in range(dimensions):
            order = [0, dim, 0]
            yield order.copy()
            for j in range(6):
                sgn = 1 if j % 2 == 0 else -1
                idx = j % 3
                for _ in range(dim if j < 5 else dim-1):
                    order[idx] += sgn
                    yield order.copy()
    
    print(list(iteration_order(2))) # [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]