6

I need to use CalculateField_management in a Python script, and the calc expression needs to concatenate a runtime variable, a string, and a field (string) value. I have searched all over for examples from which to learn. I have tried many things I have found in my research and some of my own ideas, too, but I just can't get the calc expression to evaluate. I am now completely confused and need some help to dig out of this hole. So, here's the scoop.

The field to be calculated, DESCRIP, is a string field in a fGDB feature class with one feature. The runtime variable, enddesg, will be a string. Essentially, what I want to do is concatenate the runtime value with a (longish) string and prepend that to the existing value in the DESCRIP field, storing the result back in the DESCRIP field. I have tried many forms of an "all-in-one" string for the expression, but nothing worked. I came to the conclusion that a pre-logic approach would be better, but I haven't been able to get that to work either. Below is what I have right now.

expr = "getDescription(str(enddesg))"
codeblock = """def getDescription(endstr):
    exprstr = endstr + ' Touchdown Zone Elevation;duplicated from existing control point;' + !DESCRIP!
    return exprstr"""
arcpy.CalculateField_management(perpptsSX_src, "DESCRIP", expr, 'PYTHON_9.3', codeblock)  #Should be only one feature in the source

(BTW, the enddesg variable is either a string or an integer but is converted to a string as input.) I get the following error:

<class 'arcgisscripting.ExecuteError'>: Failed to execute. Parameters are not valid.
ERROR 000989: Python syntax error: Parsing error <type 'exceptions.SyntaxError'>: invalid syntax (line 2)

Failed to execute (CalculateField).

The following code variation produces no errors, but I also get nothing entered in the DESCRIP field.

expr = "getDescription(str(enddesg), !DESCRIP!)"
codeblock = """def getDescription(endstr, desc):
    exprstr = endstr + ' Touchdown Zone Elevation;duplicated from existing control point;' + desc
    return exprstr"""

I am now thoroughly confused.

3 Answers 3

10

I never understand why people even try to mess with CalculateField_management within a Python script instead of just doing their updates within an UpdateCursor.

Not only do you not have to worry about escaping characters and writing Python functions within Python strings (shudder), you can update multiple fields at once.

It's very easy with the newer arcpy cursor iteration syntax:

rows = arcpy.UpdateCursor(fc)
for row in rows:
    row.DESCRIP = "%s Touchdown Zone Elevation;duplicated from existing control point;%s" % (str(enddesg), row.DESCRIP)
    rows.updateRow(row)
del rows

The magic on the row.DESCRIP = line is called string formatting in Python -- in this case it wasn't that necessary but when you're concatenating more than a couple of strings or need some specific numeric formatting it's really handy.

The other thing to note is that instead of hardcoding the field name (row.DESCRIP) you can also get and set field values using a variable to specify the field using row.getValue and row.setValue instead.

For the most part, I would suggest using getValue and setValue as it makes your code much easier to maintain if the field name changes or you want to write a generic function or script tool with the field name(s) as parameters.

9
  • Thank you for this. I usually avoid Update Cursors, because performance can be so abysmal on large datasets. I am normally working with very large datasets, and it just didn't click with me that, in this case, speed just isn't an issue. The update cursor method worked fine, of course, and I shall move on. Just for my own edification, though, I really would like to find out the solution to using CalculateField. I do work with complex legacy scripts that need updating now and again. The knowledge would be helpful. Thanks, again, all. Commented Jul 27, 2011 at 13:38
  • Cursors are slow and smelly. :( And besides, you can use string substitution just as easily in a codeblock, can you not?
    – Nathanus
    Commented Jul 27, 2011 at 16:07
  • 1
    @Nathanus, I've never noticed any performance issues with UpdateCursors but in 10.1 they are supposed to be faster. I would be interested in seeing if anyone has timed the two with varying size tables. And sure you can do string substitution in a function written in the code block of CalculateValue, but I find doing so is less maintainable and readable and just all around a PITA compared to doing it with a cursor. You would need to do CalculateValue once for each field which is surely going to be slower than an cursor that updates multiple fields at once.
    – blah238
    Commented Jul 27, 2011 at 16:43
  • correction CalculateField where I said CalculateValue -- darn 5 minute edit limit!
    – blah238
    Commented Jul 27, 2011 at 16:49
  • 1
    This looks much cleaner than using calculateField. Due to the claims of the abysmal performance on large datasets I did some fast tests. I did a calculateField and updateCursor on a dataset with about 90000 values. The function reads from one field, does something with it and writes to an other field. calculateField took about 26s, updateCursor did it 10 times slower, 260s! So if you care about performance, choose calculateField. Remark: calculateField will skip null value fields, updateCursor iterators over all the rows, so the semantics of both functions are not exactly the same! 5min edit.
    – tmske
    Commented Nov 10, 2011 at 9:46
3

I might be wrong, but I believe you are not supposed to put the field name !DESCRIP! in the code block. Use a placeholder variable and it should function properly.

1
  • 1
    Yup. !field! only works in the expression. Commented Jul 26, 2011 at 20:54
2

In my testing, calling str() on an argument in your expression causes the tool to fail. Maybe try:

expr = "getDescription(enddesg, !DESCRIP!)"
codeblock = """def getDescription(endstr, desc):
    exprstr = str(endstr) + ' Touchdown Zone Elevation;duplicated from existing control point;' + desc
    return exprstr"""
4
  • Why not convert it to a string inside the codeblock so that it can concatenate happily?
    – Nathanus
    Commented Jul 26, 2011 at 21:18
  • @Nathanus that should work :-), I'm going to edit my answer. Commented Jul 26, 2011 at 21:23
  • @Derek Swingley - I thought it would work, too ... until it didn't. Same result. No errors, just nothing entered into the DESCRIP field. I did try converting the enddesg variable to a string before the expression statement and just passing in the string: no converting in either the expression statement or the codeblock. Still the same result. Hmmm. I shall continue to pound on this, but I would appreciate any further thoughts any of you may have. Commented Jul 27, 2011 at 2:10
  • UPDATE - I tried the above code in the Field Calculator (with the variation of getDescription('10', !DESCRIP!) for testing on a single feature), and it worked just fine. So, the logic is fine, but there is something funky about how the code is passed to and interpreted in the CalculateField_management tool. I think it must have something to do with how things are quoted. Any thoughts? Commented Jul 27, 2011 at 2:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.