Problem with Update Cursor

3774
17
09-19-2011 05:33 PM
DeidreEllis
New Contributor II
Hi,
Im new to Python and have been trying to teach myself.
I am trying to calculate fields based on the results of two fields and read that using an update cursor is the best way to do this. I have attemped to create one but it still doesn't work, its not returning any errors but its doens't return any results.

Help!!

#--------------------------------
import os
import sys
import arcpy
import arcgisscripting
import functools
import arcpy.geoprocessing

def updateCursor(str):
    """Update fields based on analyis"""
    try:
        arcpy.gp._base._updateCursor = True
        arcpy.env.overwriteOutput = True
        arcpy.env.workspace = "C:\Temp\BAL_Mapping.gdb"

        # Create Update Cursor for feature Class
        rows = arcpy.gp._base._updateCursor ("C:\Temp\BAL_Mapping.gdb\INTERFACE")
        rows = row.next ()
        while row:
            if row.Slope == "0-5" and row.Veg == "Woodland":
                row.BALFZ = "15"
            elif row.Slope == "0-5" and row.Veg == "Forest":
                row.BALFZ = "24"
            else:
                row.BALFZ = "0"
            rows.row (row)
            row = row.next ()

        pass
    except arcpy.ExecuteError:
        print arcpy.GetMessages(2)
    except Exception as e:
        print e.args[0]

# End updateCursor function

if __name__ == '__main__':
    argv = tuple(arcpy.GetParameterAsText(i)
            for i in range(arcpy.GetArgumentCount()))
    updateCursor(str)
Tags (2)
17 Replies
StacyRendall1
Occasional Contributor III
Hey,

good work having a crack at this; unfortunately your script seems a little more complicated than necessary (I guess you have been reading some Python books, or are used to programming in something like C++). A good place to start for these kind of things is the ESRI documentation for Arcpy; i.e. UpdateCursor (whenever I get stuck I just google "arcpy thing" for whatever thing I am having trouble with).

Just a few notes on your code

  1. don't import anything you don't need - this will just slow things down unnecessarily

  2. an operation like this is simple enough that you can just make it a script (see below); you don't need to start defining functions


Here is a script to do what you want, assuming you specify just one parameter, which is the feature class you want to do the operation on:
import arcpy

featureClass = arcpy.GetParameterAsText(0) # if running from Arc as a script tool, get first (0th) parameter
if len(featureClass) == 0: # if not, it comes out as '' - has length 0; use this path
 featureClass = "C:\\Temp\\BAL_Mapping.gdb\\INTERFACE" # paths in python need either double slashes or forward slashed

arcpy.AddMessage('Starting operation on: %s' % featureClass) # tell user operation is starting

rows = arcpy.UpdateCursor(featureClass) # create the updatecursor rows object
for row in rows: # iterate over the rows
 if row.Slope == "0-5" and row.Veg == "Woodland":
  row.BALFZ = "15"
 elif row.Slope == "0-5" and row.Veg == "Forest":
  row.BALFZ = "24"
 else:
  row.BALFZ = "0"
  
 rows.updateRow(row) # write the new value to the object

del row, rows

arcpy.AddMessage('Completed operation on: %s' % featureClass) # inform user of completion status


Because this is so simple, and not really scalable in any meaningful way, I have changed it to be a script, but in my next post I will show you an example with a function.

One other thing, which I didn't include above to keep it simple, is that row.GetValue() and row.SetValue() are more flexible operators to use for getting and setting attributes (see next post).
0 Kudos
StacyRendall1
Occasional Contributor III
Here is an updated version that uses a function (so you could call it from another script, or use it twice within the same script, etc.). Field names are hard coded here, but you could make them parameters from Arc.

To make it truly reusable you could make your conditional statements inputs to the function (as long as they followed the same format), but that is a bit more work.

import arcpy

def customUpdateCursor(FeatureClass, inField1, inField2, outField):
 '''customUpdateCursor(FeatureClass, inField1, inField2, outField)
 
 takes a feature class and three field names; two for input fields to the calculation and one for output
 
 performs a conditional assesment on the inputs and returns a value to the output 
 '''
 
 rows = arcpy.UpdateCursor(FeatureClass)
 for row in rows: # iterate over the rows
  if (row.getValue(inField1) == "0-5") and (row.getValue(inField2) == "Woodland"):
   row.setValue(outField, "15")
  elif (row.getValue(inField1) == "0-5") and (row.getValue(inField2) == "Forest"):
   row.setValue(outField, "24")
  else:
   row.setValue(outField, "0")

  rows.updateRow(row)

 del row, rows
 
 # End of customUpdateCursor function
 

if __name__ == '__main__':
 featureClass = arcpy.GetParameterAsText(0) # if running from Arc as a script tool, get first (0th) parameter
 if len(featureClass) == 0: # if not, it comes out as '' - has length 0; use this default path
  featureClass =  "C:\\Temp\\BAL_Mapping.gdb\\INTERFACE" # paths in python need either double slashes or forward slashed
 
 arcpy.AddMessage('Starting operation on: %s' % featureClass)
 
 infield1 = 'Slope' # you could also make these input paramaters
 infield2 = 'Veg'
 outfield = 'BALFZ'
 
 result = customUpdateCursor(featureClass, infield1, infield2, outfield)# pass variables to function

 arcpy.AddMessage('Completed operation on: %s' % featureClass)
0 Kudos
SylviaNiderla
New Contributor
Hi,

I just happened to need the exact same thing as this post. However what is the if__name etc part for? When I use just this part (with my input/output fields set as parameters)

import arcpy

def customUpdateCursor(FeatureClass, inField1, inField2, outField):
'''customUpdateCursor(FeatureClass, inField1, inField2, outField)

takes a feature class and three field names; two for input fields to the calculation and one for output

performs a conditional assesment on the inputs and returns a value to the output
'''

rows = arcpy.UpdateCursor(FeatureClass)
for row in rows: # iterate over the rows
  if (row.getValue(inField1) == "Sandy") and (row.getValue(inField2) == "0-10"):
   row.setValue(outField, "poor")
  elif (row.getValue(inField1) == "Sandy") and (row.getValue(inField2) == ">10"):
   row.setValue(outField, "medium")
  else:
   row.setValue(outField, "none")

  rows.updateRow(row)

del row, rows

# End of customUpdateCursor function

it says completed, but it doesn't update my "Richness" field.

Any idea? Do i need a searchcurser too?
0 Kudos
StacyRendall1
Occasional Contributor III
OK, I'll try to explain fully - sorry if you already know some of this stuff...

Essentially you can use Python to code in two ways. The first is just plain scripting, i.e.:
do this
if that
    then do this
else
    do this
then do this

The code starts at the top of the file, then goes through line by line, does whatever it is told to do then proceeds to the next line. My first post the other day contained a script. It got the input from the user (or defaulted to something), did the calculation, then told the user it was done.

More advanced Python programming makes use of Modules (anything following a def statement is a module). Modules are sometimes also called functions. These allow you to use the same bit of code more than once. For example, if I have a code that selects by attributes, performs a buffer then exports the data, and I want to do it three times while varying a few things, I could do (just making stuff up to explain the example, this code will definitely not run):
inGDB = 'C:\data.gdb'
inFeatureClass = 'input'

# select field = 3, buffer by 5m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 3')
bufferedSelection = arcpy.Buffer(selection,'5m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_3_5m')

# select field = 7, buffer by 7m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 7')
bufferedSelection = arcpy.Buffer(selection,'7m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_7_7m')

# select field = 9, buffer by 1m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 9')
bufferedSelection = arcpy.Buffer(selection,'1m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_9_1m') 


Now, as I am repeating a lot of code, it would be simpler to define a module that takes the things that actually change as inputs and then does the operations. This module must be placed before it is called, so that it is loaded into memory. Adding the module gives:
# inputs
inGDB = 'C:\data.gdb'
inFeatureClass = 'input'

# define module (before trying to use it)
def selectBufferExport(GDB, FC, fieldName fieldVal, buffersize):
    '''selects FC (within GDB) entries for fieldName == fieldVal
    buffers by buffersize
    exports to new featureclass in GDB with name "test_fieldVal_buffersize"
    '''
    selection = arcpy.selectByAttributes(GDB + '\\' + FC, '%s = %s' % (fieldName, fieldVal))
    bufferedSelection = arcpy.Buffer(selection, buffersize)
    arcpy.Export(bufferedSelection, GDB + '\\' + 'test_%s_%s' % (fieldVal, buffersize))

# now call the module, passing it the variables
selectBufferExport(inGDB, inFeatureClass, 3, '5m')
selectBufferExport(inGDB, inFeatureClass, 7, '7m')
selectBufferExport(inGDB, inFeatureClass, 9, '1m')


# or, getting fancy, replacing the last four lines with a for loop:
for (fieldval,buffer) in [(3, '5m'), (7, '7m'), (9, '1m')]:
    selectBufferExport(inGDB, inFeatureClass, fieldVal, buffer)

Not only have we saved a bunch of lines, but if we want to make changes to the method or add a step, we only have to change it in one place.

Now that I have created this module I can use it as above, or I can call it from other scripts by importing the *.py file it lives in; if it was saved as selBuffExp.py I could import it into another script within the same folder and call the module with:
import selBuffExp

selBuffExp.selectBufferExport('test.gdb', 'test', 3, '5m')

Now, when you import a file it actually runs/loads into memory the contents of the file. if __name__ == "__main__": is a statement that only runs if you are running the script by itself (i.e. from the command line, or as an Arc tool). However, when the file is imported its name is not "__main__", and that bit doesn't get run... If you don't have it, and you try to import it, it will load the module into memory and do all the things within the script (which at least makes a mess, but probably crashes if your paths aren't still valid or both are set to the same paths).

Presumably you are making a script tool (not using it in a Field Calculator)? If you are making a script tool, the reason your code doesn't run is that you don't have the contents of the if __name__ == "__main__": part, so the module is never actually called. Arc tells you it is complete, meaning that the tool completed, but all you have given it to do is load something into memory (you haven't called the module or anything)...

I modified the code from my first post to follow this convention as the original poster had used it; chances are you don't need to bother about it. If you aren't going to be importing it, you could just delete the if __name__ == "__main__": line and un-indent what was in there.
0 Kudos
SylviaNiderla
New Contributor
OK, I'll try to explain fully - sorry if you already know some of this stuff...

Essentially you can use Python to code in two ways. The first is just plain scripting, i.e.:
do this
if that
    then do this
else
    do this
then do this

The code starts at the top of the file, then goes through line by line, does whatever it is told to do then proceeds to the next line. My first post the other day contained a script. It got the input from the user (or defaulted to something), did the calculation, then told the user it was done.

More advanced Python programming makes use of Modules (anything following a def statement is a module). Modules are sometimes also called functions. These allow you to use the same bit of code more than once. For example, if I have a code that selects by attributes, performs a buffer then exports the data, and I want to do it three times while varying a few things, I could do (just making stuff up to explain the example, this code will definitely not run):
inGDB = 'C:\data.gdb'
inFeatureClass = 'input'

# select field = 3, buffer by 5m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 3')
bufferedSelection = arcpy.Buffer(selection,'5m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_3_5m')

# select field = 7, buffer by 7m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 7')
bufferedSelection = arcpy.Buffer(selection,'7m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_7_7m')

# select field = 9, buffer by 1m, export to test_[fieldVal]_[buffersize] Featureclass in input GDB
selection = arcpy.selectByAttributes(inGDB + '\\' + inFeatureClass, 'field = 9')
bufferedSelection = arcpy.Buffer(selection,'1m')
arcpy.Export(bufferedSelection, inGDB + '\\' + 'test_9_1m') 


Now, as I am repeating a lot of code, it would be simpler to define a module that takes the things that actually change as inputs and then does the operations. This module must be placed before it is called, so that it is loaded into memory. Adding the module gives:
# inputs
inGDB = 'C:\data.gdb'
inFeatureClass = 'input'

# define module (before trying to use it)
def selectBufferExport(GDB, FC, fieldName fieldVal, buffersize):
    '''selects FC (within GDB) entries for fieldName == fieldVal
    buffers by buffersize
    exports to new featureclass in GDB with name "test_fieldVal_buffersize"
    '''
    selection = arcpy.selectByAttributes(GDB + '\\' + FC, '%s = %s' % (fieldName, fieldVal))
    bufferedSelection = arcpy.Buffer(selection, buffersize)
    arcpy.Export(bufferedSelection, GDB + '\\' + 'test_%s_%s' % (fieldVal, buffersize))

# now call the module, passing it the variables
selectBufferExport(inGDB, inFeatureClass, 3, '5m')
selectBufferExport(inGDB, inFeatureClass, 7, '7m')
selectBufferExport(inGDB, inFeatureClass, 9, '1m')


# or, getting fancy, replacing the last four lines with a for loop:
for (fieldval,buffer) in [(3, '5m'), (7, '7m'), (9, '1m')]:
    selectBufferExport(inGDB, inFeatureClass, fieldVal, buffer)

Not only have we saved a bunch of lines, but if we want to make changes to the method or add a step, we only have to change it in one place.

Now that I have created this module I can use it as above, or I can call it from other scripts by importing the *.py file it lives in; if it was saved as selBuffExp.py I could import it into another script within the same folder and call the module with:
import selBuffExp

selBuffExp.selectBufferExport('test.gdb', 'test', 3, '5m')

Now, when you import a file it actually runs/loads into memory the contents of the file. if __name__ == "__main__": is a statement that only runs if you are running the script by itself (i.e. from the command line, or as an Arc tool). However, when the file is imported its name is not "__main__", and that bit doesn't get run... If you don't have it, and you try to import it, it will load the module into memory and do all the things within the script (which at least makes a mess, but probably crashes if your paths aren't still valid or both are set to the same paths).

Presumably you are making a script tool (not using it in a Field Calculator)? If you are making a script tool, the reason your code doesn't run is that you don't have the contents of the if __name__ == "__main__": part, so the module is never actually called. Arc tells you it is complete, meaning that the tool completed, but all you have given it to do is load something into memory (you haven't called the module or anything)...

I modified the code from my first post to follow this convention as the original poster had used it; chances are you don't need to bother about it. If you aren't going to be importing it, you could just delete the if __name__ == "__main__": line and un-indent what was in there.


I meant to reply I get an error if I include the "main" etc bit.

<type 'exceptions.UnboundLocalError'>: local variable 'row' referenced before assignment
Failed to execute (Script).

I just can't work it out. I've copied everything and checked it in pythonwin using script checker.
0 Kudos
StacyRendall1
Occasional Contributor III
Try replacing the line
del row, rows


With:
try: del row
except: pass
del rows


I think sometimes the row disappears before you get to delete it, something to do with how the cursor works (?), anyway, you are trying to delete something that doesn't exist, so it has an error.

The reason you have no the problem when you delete the if __name__ == "__main__": bit is that nothing actually happens - it doesn't run...
0 Kudos
SylviaNiderla
New Contributor
well I just couldn't get it to work, so I tried writing it this way. But this way nothing happens also even though it runs fine. Any ideas?

import sys, arcpy

try:
 infc = arcpy.GetParameterAsText(0)   # Input feature class
 inField1 = arcpy.GetParameterAsText(1)  # Input data field
 inField2 = arcpy.GetParameterAsText(2)
 outfld = arcpy.GetParameterAsText(3)  # Output result field

except: 
 arcpy.AddError("Cannot parse input arguments")
 sys.exit("Error reading arguments!")
 arcpy.AddMessage("Calculating field...")   

try:
        rows = arcpy.UpdateCursor(infc)
        for row in rows: # iterate over the rows
  if (row.getValue(inField1) == "Sandy") and (row.getValue(inField2) == "0-10"):
   row.setValue(outField, "poor")
  elif (row.getValue(inField1) == "Sandy") and (row.getValue(inField2) == ">10"):
   row.setValue(outField, "medium")
  else:
   row.setValue(outField, "none")
  rows.updateRow(row)
  del rows
  arcpy.AddMessage("Completed")
except:
 errMsg = arcpy.GetMessages(2)
 arcpy.AddError("Unexpected error : " + errMsg)
0 Kudos
DanPatterson_Retired
MVP Emeritus
These two rows

  del rows
  arcpy.AddMessage("Completed")

need to be de-dented to line up with
the for statement
try:
        rows = arcpy.UpdateCursor(infc)
        for row in rows: # iterate over the rows
        ....
        ....
        del rows
        arcpy.AddMessage("Completed")
0 Kudos
SylviaNiderla
New Contributor
ah, thanks. But now I just get an "unexpected error" so something in my "try" isn't right I guess.

ok I know its the searchcurser bit

try:
rows = arcpy.SearchCursor(infc)
for row in rows:
        infc = row.getValue(inField1) + row.getValue(inField2)
del rows

how do I get my curser to read over the two fields I want to use in the next bit?
0 Kudos