Wednesday, June 15, 2011

Advanced - Extending Search Cursor Object

The 'with' statement is used to wrap the execution of a block with functionality provided by a separate guard object.  The expression is evaluated once, and should yield a context guard, which is used to control execution of the suite. The guard can provide execution-specific data, which is assigned to the target (or target list). 

As a developer, think of a 'with' statement as a try/finally pattern

def opening(filename):
   f = open(filename)
   try:
      yield f
   finally:
      f.close()

This can now be viewed as:

with f = opening(filename):
   #...read data from f...

This makes writing code easier to understand, and you don't have to always remember to delete the object reference using the del().

How does this apply to the Cursor object? Well natively, the Cursor object does not support 'with' statement use. To extend the Cursor, more specifically SearchCursor, first create a class:

class custom_cursor(object):
   """
      Extension class of cursor to enable use of
      with statement
   """
   def __init__(self, cursor):
      self.cursor = cursor
   def __enter__(self):
      return self.cursor
   def __exit__(self, type, value, traceback):
      del self.cursor

Now you have an object called custom_cursor that has the minimum required class properties of __enter__() and __exit__(). Notice that __exit__() performs that annoying del() to remove the schema locks on our data.

How do you use this? It's not hard at all.  In this example, the function searchCursor() takes a feature class or table and a where clause (expression) and returns the results as an array of Row objects. 

def searchCursor(fc,expression=""):
   """
       Returns a collections of rows as an array of Row Objects
      :param fc: Feature Class or Table
      :param expression: Where Clause Statement (Optional)
      :rtype Rows: Array of Rows
  
   """
   rows = []
   with custom_cursor(arcpy.SearchCursor(fc,expression)) as cur:
      for row in cur:
         rows.append(row)
   return rows


The del() is taken care of automatically, and when the process is completed the __exit__() is called launching the del().

I got this idea from Sean Gillies Blog