[ale] 3-D arrays in python...

David Jackson deepbsd.ale at gmail.com
Sat Jun 27 14:33:54 EDT 2015


Hi!

So I'm having a little trouble in Python4.  One of the assignments is to
create a 3-d array from a 2-d array, using the array module.  The array as
introduced in the lesson is a list, but could be another data structure.
For this assignment, it asks for a list implementation.  Here's the 2-d
array as introduced in the text.  First, the test, since we're learning
agile and test-driven development:

*** snip of testarray.py ***

#!/usr/bin/env python3

"""
testarray.py: Test list-of-list based array implementations using tuple
subscripting.
"""

import unittest
import arr_single_list as arr

class TestArray(unittest.TestCase):

    def test_zeroes(self):
        for N in range(4):
            a = arr.array(N, N)
            for i in range(N):
                for j in range(N):
                    #self.assertEqual(a[i][j], 0)
                    self.assertEqual(a[i, j], 0)

    def test_identity(self):
        for N in range(4):
            a = arr.array(N, N)
            for i in range(N):
                #a[i][i] = 1
                a[i, i] = 1
            for i in range(N):
                for j in range(N):
                    #self.assertEqual(a[i][j], i==j)
                    self.assertEqual(a[i, j], i==j)

    def test_one_cell(self):
        N = 10
        a = arr.array(N,N)
        a[2,3] = 1
        for i in range(N):
            for j in range(N):
                if i==2 and j==3:
                    self.assertEqual(a[i,j], 1)
                else:
                    self.assertEqual(a[i,j], 0)


    def _index(self, a, r, c):
        return a[r, c]

    def test_key_validity(self):
        a = arr.array(10, 10)
        self.assertRaises(KeyError, self._index, a, -1, 1)
        self.assertRaises(KeyError, self._index, a, 10, 1)
        self.assertRaises(KeyError, self._index, a, 1, -1)
        self.assertRaises(KeyError, self._index, a, 1, 10)


if __name__ == "__main__":
    unittest.main()

*** end snip of test_array.py ***

Here's the 2-D array called called arr_single_list.py:

*** start snip of arr_single_list.py  ***
#!/usr/bin/env python3

"""
arr_single_list.py:  Class-based single-list allowing tuple subscripting.
"""

class array:

    def __init__(self, M, N):
        "Create a list long enough to hold M*N elements."
        self._data = [0] * M * N
        self._rows = M
        self._cols = N


    def __getitem__(self, key):
        "Returns the appropriate element for a two-element subscript tuple."
        row, col = self._validate_key(key)
        return self._data[row*self._cols+col]

    def __setitem__(self, key, value):
        "Sets the appropriate element for a two-element subscript tuple."
        row, col = self._validate_key(key)
        self._data[row*self._cols+col] = value

    def _validate_key(self, key):
        '''
        Validates a key against the array's shape, returning good tuples.
        Raises KeyError on problems.
        '''
        row, col = key
        if (0 <= row < self._rows and 0 <= col < self._cols):
            return key
        raise KeyError("subscript out of range")

***  end snip of arr_single_list.py ***

Okay, this all seems pretty simple, and it ran just fine through the
tests.  The __{get|set}item__ methods
seem to be row oriented and simply take successive rows and stick them each
onto the end of the list.
Fine.  So a third dimension, would, I should think, be fairly simple, but I
seem to have missed something.

Here's my file, called naivearr2.py:

*** start snip of naivearr2.py  ***
#!/usr/bin/env python3

"""
naievearr2.py:  Naive implementation of 3-D array using lists and tuple
subscripts
"""

import array as sys_array

class array:

    def __init__(self, M, N, O):
        "Create 3-D array of lists"
        self._data = sys_array.array("i", [0] * M * N * O)
        self._rows = M
        self._cols = N
        self._depth = O

    def __getitem__(self, key):
        "returns the appropriate element for a three-element subscript
tuple."
        row, col, depth = self._validate_key(key)
        return self._data[row*self._cols+col*self._depth+depth]

    def __setitem__(self, key, value):
        "sets the appropriate element for a three-element subscript tuble."
        row, col, depth = self._validate_key(key)
        self._data[row*self._cols+col*self._depth+depth] = value

    def _validate_key(self, key):
        """Validates a key against the array's shape, returning good tuples.
        Raises KeyError on problems."""
        row, col, depth = key
        if (0 <= row < self._rows and 0 <= col < self._cols and 0 <= depth
< self._depth):
            return key
        raise KeyError("subscript out of range")

*** end snip of naivearr2.py ***


I thought for sure this would work, but there's a gotcha in there
somewhere.  Sure enough, the
__setitem__ sets the correct cells to '1' when in the code below, but I get
extras set to one as well:


*** start snip of test_naivearr2.py ***
import unittest
import naivearr2 as arr


class TestArray(unittest.TestCase):
    def test_zeroes(self):
        for N in range(6):
            a = arr.array(N, N, N)
            for i in range(N):
                for j in range(N):
                    for k in range(N):
                        #print("N={}, i={}, j={}, k={}".format(N, i, j, k))
                        self.assertEqual(a[i, j, k], 0)

    def test_identity(self):
        for N in range(6):
            a = arr.array(N, N, N)
            for i in range(N):
                a[i, i, i] = 1
                print("a[{},{},{}] ".format(i, i, i))
            for i in range(N):
                for j in range(N):
                    for k in range(N):
                        if i==j==k:
                            #print("***A[{},{},{}]".format(i, j, k))
                            self.assertEqual(a[i, j, k], 1)
                        if not i==j==k and a[i,j,k] == 1:
                            print("Dammit!  A[{},{},{}] == 1".format(i, j,
k))
                            #self.assertEqual(a[i,j,k], 0)

    def _index(self, a, r, c, d):
        return a[r, c, d]

    def test_key_validity(self):
        a = arr.array(10, 10, 10)
        self.assertRaises(KeyError, self._index, a, -1, 1, 1)
        self.assertRaises(KeyError, self._index, a, 10, 1, 10)
        self.assertRaises(KeyError, self._index, a, 1, -1, -10)
        self.assertRaises(KeyError, self._index, a, 1, 10, -1)



if __name__ == "__main__":
    unittest.main()
import unittest
import naivearr2 as arr


class TestArray(unittest.TestCase):
    def test_zeroes(self):
        for N in range(6):
            a = arr.array(N, N, N)
            for i in range(N):
                for j in range(N):
                    for k in range(N):
                        #print("N={}, i={}, j={}, k={}".format(N, i, j, k))
                        self.assertEqual(a[i, j, k], 0)

    def test_identity(self):
        for N in range(6):
            a = arr.array(N, N, N)
            for i in range(N):
                a[i, i, i] = 1
                print("a[{},{},{}] ".format(i, i, i))
            for i in range(N):
                for j in range(N):
                    for k in range(N):
                        if i==j==k:
                            #print("***A[{},{},{}]".format(i, j, k))
                            self.assertEqual(a[i, j, k], 1)
                        if not i==j==k and a[i,j,k] == 1:
                            print("Dammit!  A[{},{},{}] == 1".format(i, j,
k))
                            #self.assertEqual(a[i,j,k], 0)

    def _index(self, a, r, c, d):
        return a[r, c, d]

    def test_key_validity(self):
        a = arr.array(10, 10, 10)
        self.assertRaises(KeyError, self._index, a, -1, 1, 1)
        self.assertRaises(KeyError, self._index, a, 10, 1, 10)
        self.assertRaises(KeyError, self._index, a, 1, -1, -10)
        self.assertRaises(KeyError, self._index, a, 1, 10, -1)



if __name__ == "__main__":
    unittest.main()

*** end snip of test_naivearr2.py ***

Everything passes except test_identity.   Sure enough, a[0,0,0] = 1,
a[1,1,1]=1, etc,
but I also get
A[0,2,1] == 1
A[0,4,2] == 1
A[1,3,2] == 1
A[2,0,4] == 1
A[2,4,3] == 1
A[3,1,2] == 1
A[4,0,2] == 1
A[4,2,3] == 1


So I guess my __setitem__ is goofy.  Or I guess I could be calling a value
with
__getitem__ that is the wrong value.

Can anyone see what I'm doing wrong?

Any help is appreciated, and thanks in advance!

Dave
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.ale.org/pipermail/ale/attachments/20150627/6eb7c47c/attachment.html>


More information about the Ale mailing list