0

I have a code that reads as:

def xgauss(self):

    m, n = self.m, self.n

    M = self.copy()

    for k in range(n):
        for i in range(k + 1, m):
            yield M
            if not likezero(M[i][k]):
                lam = M[i][k] / M[k][k]
                M[i] = M[i] - lam * M[k]

    yield M

For now is enough to say that likezero returns true if the value is lesser than precision (for numerical comparisons) ;-)

My objective here is to return each iteration of Gauss Elimination algorithm for study sake (classroom).

I have some test inputs as:

In [77]: A
Out[77]: 
Matrix([
        [     1,      4,      1],
        [     1,      6,     -1],
        [     2,     -1,      2]
      ])

In [78]: Ab
Out[78]: 
Matrix([
        [      1,       4,       1,       7],
        [      1,       6,      -1,      13],
        [      2,      -1,       2,       5]
      ])

Weird or not, when I call the generator over the Matrix Ab (over A I also get the same behavior), I got:

In [76]: list(Ab.xgauss())
Out[76]: 
[Matrix([
         [      1,       4,       1,       7],
         [      0,       2,      -2,       6],
         [      0,       0,      -9,      18]
       ]), Matrix([
         [      1,       4,       1,       7],
         [      0,       2,      -2,       6],
         [      0,       0,      -9,      18]
       ]), Matrix([
         [      1,       4,       1,       7],
         [      0,       2,      -2,       6],
         [      0,       0,      -9,      18]
       ]), Matrix([
         [      1,       4,       1,       7],
         [      0,       2,      -2,       6],
         [      0,       0,      -9,      18]
       ])]

That is the correct answer (should be only the last iteration), but I cannot see each step, the generator returns the matrix result in all iterations. I have no idea what can be happening.

Lin
  • 1,145
  • 11
  • 28

1 Answers1

6

In your generator, you have a value M, which is an instance of your class. You yield the same object each time. At each iteration, you are modifying the object. The generator doesn't yield a copy of M, it yields a reference to M, the same as how functions don't return copies, they return references.

When you make a list of the generator's results, you are making a list which has many references to the same object. Printing the list shows you the same object many times, showing its last state.

BTW, this behavior of Python (no implicit copies, lots of references) is covered in more detail in my PyCon talk: Python Names and Values.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • Very cool slideshow, even as someone who understood those concepts going in. Might want to add a disclaimer that you made it, though. – gyre Apr 23 '17 at 18:41