3

ArcMap 10.7.1 — Oracle 18c SDE.St_Geometry — Polyline FC


I have an ArcPy function that works as expected in a standalone ArcPy script (PyScripter):

def new_shape(geom):
    spatial_reference =  geom.spatialReference
    parts = arcpy.Array()
    for i in range(geom.partCount):
        part = geom.getPart(i)
        points = arcpy.Array()
        for point in part:
            point.M = geom.measureOnLine(point)
            points.append(point)
        parts.append(points)
    return arcpy.Polyline(parts, spatial_reference)

That function works correctly, including the following for loop. It returns a reconstructed shape to the main ArcPy Script.

        for point in part:
            point.M = geom.measureOnLine(point)
            points.append(point)

However, when I use that same function in the Field Calculator, the script runs without errors, but it returns an empty shape.

The problem seems to be the for loop: for point in part: . When I debug the code, I can see that the script doesn't enter the for loop.


I was able to work around the issue by using a different style of for loop and the .getObject() array property. Now, the script enters the for loop and constructs the shape from the points, as expected:

for j in range(part.count):
    point = part.getObject(j)
    point.M = geom.measureOnLine(point)
    points.append(point)

Question:

Why does for point in part: work in a standalone ArcPy script, but not in the Field Calculator?

I would like to understand this better — in case I'm doing something wrong.

1

2 Answers 2

6
+100

The difference in behavior is due to the fact that objects within geoprocessing tools are not the same as objects within ArcPy. The pattern of directly iterating over items in a container, for point in part:, is a Python use pattern that is implemented in most ArcPy classes but not in ArcObjects classes being used by geoprocessing tools.

Looking to Esri's quick tour of ArcPy:

ArcPy provides access to geoprocessing tools as well as additional functions, classes, and modules that allow you to create simple or complex workflows. Broadly speaking, ArcPy is organized in tools, functions, classes, and modules.

Technically speaking, geoprocessing tools are functions available from arcpy—that is, they are accessed like any other Python function. However, to avoid confusion, a distinction is always made between tool and nontool functions (such as utility functions like ListFeatureClasses()).

The key is in the second paragraph, i.e., geoprocessing tools are functions available from ArcPy but are not native ArcPy functions. Field Calculator is an example of a geoprocessing tool available through ArcPy as opposed to an ArcPy function that supports Python use patterns. Typically with geoprocessing tools, users either pass basic Python data types (e.g., string) or ArcPy objects that are then converted to geoprocessing objects behind the scenes. Field Calculator is somewhat unique in that users can pass free-form Python code within a Python string for the tool to execute.

In ArcPy, arcobjects are created from geoprocessing code and then extended to add support for core Python functionality and patterns. For ArcPy Array, that includes implementing the Python __iter__ special method that supports the pattern of directly iterating over items in a container. From 3. Data model - Python 3.10.2 documentation:

object.iter(self)

This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container.

If you look at the ArrayMixin class within the mixins module, you can see the implementation of the iter special method is similar to the Python code that currently works within Field Calculator.

# represenative code since ArcPy modules are copyrighted  
def __iter__(self):
    for index in xrange(len(self)):
        yield conversionObject(self.GetObject(index))

You can verify that a geoprocessing array does not implement the __iter__ special method while ArcPy Array does implement it:

>>> import arcpy
>>> 
>>> # Create geoprocessing array
>>> gp_arr = arcpy.gp.CreateObject("Array")
>>> type(gp_arr)
<type 'geoprocessing array object'>
>>> 
>>> # Create ArcPy Array
>>> arcpy_arr = arcpy.Array()
>>> type(arcpy_arr)
<class 'arcpy.arcobjects.arcobjects.Array'>
>>> 
>>> # Check the geoprocessing array for the __iter__ method
>>> gp_arr.__iter__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Array: Get attribute __iter__ does not exist
>>> 
>>> # Check the ArcPy Array for the __iter__ method
>>> arcpy_arr.__iter__
<bound method Array.__iter__ of <Array []>>
>>> 
0

Luckily, arcpy can be accessed inside field calculator, so it's a simple call to arcpy.Array():

def plineM(shp):
  for part in arcpy.Array(shp.getPart(0)):
    for p in part:
      return None if p is None else p.X          

plineM(!Shape!)

Script (ArcPy) vs field calculator (Python Parser) behaviour of getPart in ArcGIS for Desktop?

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.