Skip to content

Instantly share code, notes, and snippets.

@mikepianka
Last active October 2, 2020 10:03
Show Gist options
  • Save mikepianka/7731dea109d704d5adf491bc47b166c8 to your computer and use it in GitHub Desktop.
Save mikepianka/7731dea109d704d5adf491bc47b166c8 to your computer and use it in GitHub Desktop.
OOP helper class for arcpy cursors

cursor oops

Cursor Object Oriented Programming stuff

This is a helper class to use with arcpy.da cursors, like SearchCursor and UpdateCursor.

Using arcpy cursors usually goes something like this...

feature_class = r'd:\foo\bar.gdb\buildings'
fields = [
	'Name', 
	'Vacancy', 
	'Offices', 
	'OfficesAvailable', 
	'OfficesOccupied', 
	'Banner'
]

# known - Name, Offices, OfficesOccupied
# calculate - OfficesAvailable, Vacancy, Banner

with arcpy.da.UpdateCursor(feature_class, fields) as cursor:
    for row in cursor:
        row[3] = row[2] - row[4]

        if row[3] < row[2]:
            row[1] = True
            row[5] = f'{row[0]}: {row[3]} Offices Available'
        else:
            row[1] = False
            row[5] = f'{row[0]}: Offices Unavailable'
        
        cursor.UpdateRow(row)

That is hard to follow. If the list of fields gets passed in a different order then the logic no longer works either.

Here is an alternative using this Feature helper class...

with arcpy.da.UpdateCursor(feature_class, fields) as cursor:
    for row in cursor:
        blg = Feature(fields, row)

        blg.OfficesAvailable = blg.Offices - blg.OfficesOccupied

        if blg.OfficesAvailable < blg.Offices:
            blg.Vacancy = True
            blg.Banner = f'{blg.Name}: {blg.OfficesAvailable} Offices Available'
        else:
            blg.Vacancy = False
            blg.Banner = f'{blg.Name}: No Office Vacancies'
        
        cursor.UpdateRow(blg.vals_to_tuple())

When arcpy graduates to Python 3.7, dataclasses may be the better alternative.

# when arcpy moves to python 3.7, dataclasses will be the better alternative
class Feature:
"""A feature object created from an attribute table row."""
def __init__(self, field_names, row_values):
self.__schema = dict(zip(field_names, [type(v) for v in row_values]))
d = dict(zip(field_names, row_values))
for key in d:
setattr(self, key, d[key])
def __repr__(self):
return "Feature()"
def pretty_print_attr(self):
"""Pretty print the feature's attributes."""
attr_dict = vars(self)
public_attr_keys = [k for k in attr_dict if not k.startswith("_Feature")]
for k in public_attr_keys:
print(f"{k} = {attr_dict[k]}")
def vals_to_tuple(self):
"""Return the feature's attribute values as a tuple."""
attr_dict = vars(self)
public_attr_keys = [k for k in attr_dict if not k.startswith("_Feature")]
public_attr_vals = [attr_dict[k] for k in public_attr_keys]
public_attr_types = [type(attr_dict[k]) for k in public_attr_keys]
public_attr_schema = dict(zip(public_attr_keys, public_attr_types))
for field in self.__schema:
if self.__schema[field] != public_attr_schema[field]:
raise ValueError(f"Value type mismatch on {field}")
else:
pass
return tuple(public_attr_vals)
if __name__ == "__main__":
# example
fields = ["Name", "ID", "Type", "Area"]
row = ("Wetland A", 123, "Forested", 45.67)
obj = Feature(fields, row)
obj.pretty_print_attr()
print(obj.ID)
obj.ID = "A-789"
print(obj.ID)
print(obj.vals_to_tuple())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment