title: Subscriptable objects in class
body: <p>I have a <code>Python 3.4.3</code> class called <code>Sparse</code> in order to represent sparse matrices and operations among them. </p>
  
  <p>Along with this class I have function definitions including how to overload the addition operation, print function etc. However, these functions are written when the sparse matrices were represented as dictionaries with keys representing the position of non-zero entries and corresponding non-zero entry as the value. </p>
  
  <p>The functions work fine in this prior implementation, yet when the sparse matrices are implemented as a class this approach fails yielding a </p>
  
  <pre><code>TypeError: ` 'Sparse' object is not subscriptable`. 
  </code></pre>
  
  <p>How can I circumvent this issue? I am attaching the relevant code for reference purposes. The <strong>error traceback</strong> is</p>
  
  <pre><code>Traceback (most recent call last):
    File "sparse_driver.py", line 11, in &lt;module&gt;
      print(s)
    File "/mnt/DATA/Dropbox/Courses/Python/HW4/SparseClass.py", line 42, in __str__
      sparse_functions.sparse_print(self)
    File "/mnt/DATA/Dropbox/Courses/Python/HW4/sparse_functions.py", line 56, in sparse_print
      nrow = a['-1'][0]
  TypeError: 'Sparse' object is not subscriptable
  </code></pre>
  
  <p><strong>Function definitions</strong></p>
  
  <pre><code>import random
  # Deepcopy is imported in order not to change the matrix a itself
  from copy import deepcopy
  def sparse_gen(num_row, num_col, spar):
      # Total number of elements
      nelem = num_row * num_col
      # Number of nonzero elements
      n_nonzero = int(nelem * spar)
      # Position of nonzero elements
      poselem = random.sample(range(nelem), n_nonzero)
      # Create an empty dictionary here
      spardict = {};
      # Add entries into the dictionary here  in a for loop
      for i in range(len(poselem)):
          # Create a string from the number in poselem and use it as a key
          key = str(poselem[i])
          # Select -1 or 1  randomly
          spardict[key]  = random.choice([-1,1])
      # Add the size of the matrix with the key value -1
      spardict['-1'] = [num_row, num_col]
      # Return the sparse matrix for reference later on
      return spardict
  # Here is the function for addition of two matrices
  def sparse_add(a, b):
      # First lets check if the matrix sizes are equal or not
      if a['-1'] != b['-1'] :
          print('Matrix sizes should be equal. Exiting!')
          return
      # Copy matrix a into matrix c the sum a + b = c
      c = deepcopy(a)
      # Delete the size info from  b and retrieve the keys
      del b['-1']
      bkeys = b.keys()
      # Write the for loop for summation iterating over keys of b
      for bkey in iter(bkeys):
          if (bkey in a):
              if ((c[bkey] + b[bkey]) == 0):
                  del c[bkey]
                  continue
              else:
                  c[bkey] += b[bkey]
          else:
                  c[bkey] = b[bkey]
      # Since it gives rise to problems later readd b['-1']
      b['-1'] = a['-1']
      return c
  # The function for printing sparse matrices is defined here
  def sparse_print(a):
      # Lets retrieve the size information first (rows, columns)
      # Remember the size information is a list
      nrow = a['-1'][0]
      ncol = a['-1'][1]
      # Lets write a for loop to print out the relevant info
      for i in range(nrow * ncol):
          # if the i is in a print that value otherwise print 0
          return_str = ""
          if str(i) in a:
              return_str += "%3d " %  a[str(i)]
          else:
              return_str += "%3d " % 0
          # Print new line if we hit the end of the row
          if ((i + 1) % nrow) == 0:
              return_str += "\n"
      return_str += "\n\n"# Two new line characters to avoid confusion
      return return_str
  # The function for converting sparse matrices to full is defined here
  def mat2sparse(a):
      spardict = {}
      num_row = len(a)
      num_col = len(a[0])
      nelem = num_row * num_col
      # Input the size information
      spardict['-1'] = [num_row, num_col]
      for i in range(num_row):
          for j in range(num_col):
              if a[i][j] != 0:
                  # Calculate the position of the key
                  key = str(i*num_col + j)
                  spardict[key] = a[i][j]
      return spardict
  </code></pre>
  
  <p><strong>Class Definition</strong></p>
  
  <pre><code>import sparse_functions
  class GetAttr(type):
      def __getitem__(cls, x):
          return getattr(cls, x)
  class Sparse:
      __metaclass__ = GetAttr
      """ Class for sparse matrices"""
      # This is the constructor when dimensions and sparsity are specified
      def __init__(self, *args):
          # The name of the helper function is sparse_gen
          if len(args) == 3:
              num_row = args[0]
              num_col = args[1]
              spar = args[2]
              if ((type(args[0]) is not int) or (type(args[1]) is not int)):
                  raise TypeError('The first two arguments should be integers!')
              elif not ((args[0] &gt; 0) and (args[1] &gt; 0)):
                  raise ValueError('The first two agruments should be positive!')
              elif not ((args[2] &lt; 1) and (args[2] &gt; 0)):
                  raise ValueError('Sparsity must be between 0 and 1!')
              self.sparse_rep = sparse_functions.sparse_gen(num_row, num_col, spar)
          elif len(args) == 1:
              if (type(args[0] is not list)):
                  raise TypeError('The only argument supplied should be a list!')
              # The list of lists matrix is provided convert it to sparse
              self.sparse_rep = sparse_functions.mat2sparse(arg[0])
          else:
              raise AttributeError('Invalid number of arguments. There should be either one argument or three arguments!')
      # Overload the addition operation
      def __add__(a,b):
          # Here we can make use of the already defined functions
          return sparse_functions.sparse_add(a,b)
      # Overload the subtraction operator
      def __sub__(a,b):
          return sparse_functions.sparse_add(a,-b)
      # Overload the print function
      def __str__(self):
          sparse_functions.sparse_print(self)
  </code></pre>
  
  <p><strong>Driver</strong> </p>
  
  <pre><code>import random
  from SparseClass import *
  s=Sparse(4,4,0.2)
  p=Sparse(4,4,0.3)
  # Both of the following lines are problematic
  print(s)
  s + p
  </code></pre>
  
  <p><a href="http://pastebin.com/1JKyghJw" rel="nofollow">Functions</a></p>
  
  <p><a href="http://pastebin.com/y299d3eq" rel="nofollow">Class Definition</a></p>
  
  <p><a href="http://pastebin.com/m5tbZ1gU" rel="nofollow">Driver</a></p>