Last active
September 28, 2016 07:51
-
-
Save AlexArcPy/47f75f217de827b2c03d17cded653cba to your computer and use it in GitHub Desktop.
Iterating through the properties of arcpy objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import arcpy" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Many of ArcGIS users need to iterate the propeties of `arcpy` objects, such as `Describe` objects or `Layer` objects. The purpose of iterating is to get all the information available either for reporting purposes or during the troubleshooting time. Speaking of `Layer` objects, let's see what Python code is needed for iterating through their properties.\n", | |
"\n", | |
"First, we need to obtain the `Layer` object. This is how we can get hands at the obj which represents the `Layer` object. " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"<map layer u'GridLayer'>" | |
] | |
}, | |
"execution_count": 2, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"import arcpy\n", | |
"\n", | |
"mxd_path = r'C:\\GIS\\introspect.mxd'\n", | |
"mxd = arcpy.mapping.MapDocument(mxd_path)\n", | |
"df = arcpy.mapping.ListDataFrames(mxd, \"Layers\")[0]\n", | |
"obj = arcpy.mapping.ListLayers(mxd, \"GridLayer\", df)[0]\n", | |
"obj" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"True" | |
] | |
}, | |
"execution_count": 3, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"obj.isFeatureLayer" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"A naïve way to list all the properties of this object is to do something like this:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"data source -- C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"dataset name -- GridFeatures\n", | |
"workspace path -- C:\\GIS\\Temp\\test.gdb\n" | |
] | |
} | |
], | |
"source": [ | |
"print \"data source --\", obj.dataSource\n", | |
"print \"dataset name --\", obj.datasetName\n", | |
"print \"workspace path --\", obj.workspacePath" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This could of course be shortened a bit to something like this with the help of `getattr()` method ([help link](https://docs.python.org/2/reference/datamodel.html#object.__getattr__)):" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"dataSource C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"datasetName GridFeatures\n", | |
"workspacePath C:\\GIS\\Temp\\test.gdb\n" | |
] | |
} | |
], | |
"source": [ | |
"props = ['dataSource','datasetName','workspacePath']\n", | |
"for p in props:\n", | |
" print p, getattr(obj,p)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This implies, however, that you still need to create and maintain the list of available properties for each type of object (`Layer` object has different kind of properties than the `Describe` object).\n", | |
"\n", | |
"A more elegant solution to this is to access all the properties of an object using the Python built-in `dir()` command. `dir(obj)` will return all the attributes and methods that an instance of the class has. This includes private methods and attributes which could be ignored as they are not `arcpy` specific." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"['brightness', 'contrast', 'credits', 'dataSource', 'datasetName', 'definitionQuery', 'description', 'findAndReplaceWorkspacePath', 'getExtent', 'getSelectedExtent', 'getSelectionSet', 'isBroken', 'isFeatureLayer', 'isGroupLayer', 'isNetworkAnalystLayer', 'isRasterLayer', 'isRasterizingLayer', 'isServiceLayer', 'labelClasses', 'longName', 'maxScale', 'minScale', 'name', 'replaceDataSource', 'save', 'saveACopy', 'serviceProperties', 'setSelectionSet', 'showLabels', 'supports', 'symbology', 'symbologyType', 'time', 'transparency', 'updateLayerFromJSON', 'visible', 'workspacePath']\n" | |
] | |
} | |
], | |
"source": [ | |
"props = [prop for prop in [prop for prop in dir(obj) if '_' not in prop]]\n", | |
"print props" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This is great! We can probably just use the `getattr()` method now and list all the values of every property this `Layer` object has." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"ename": "ValueError", | |
"evalue": "LayerObject: Layer does not support brightness", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-7-7adcfb79fb31>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mprop\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mprops\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;31mValueError\u001b[0m: LayerObject: Layer does not support brightness" | |
] | |
} | |
], | |
"source": [ | |
"for prop in props:\n", | |
" print getattr(obj,prop)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Not so fast. Not all properties are supported by this particular map layer of `Feature Layer` type. `Raster Layer` type, for instance, would support other properties, so we have to distinguish between those. There is `supports()` method that does just that:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"False\n", | |
"True\n" | |
] | |
} | |
], | |
"source": [ | |
"print obj.supports('brightness')\n", | |
"print obj.supports('transparency')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"OK, let's check then whether a property is supported for this type of map layer:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"credits \n", | |
"dataSource C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"datasetName GridFeatures\n", | |
"definitionQuery \n", | |
"description \n" | |
] | |
}, | |
{ | |
"ename": "ValueError", | |
"evalue": "Invalid value for layer_property: 'FINDANDREPLACEWORKSPACEPATH' (choices are: ['CREDITS', 'DATASETNAME', 'WORKSPACEPATH', 'DESCRIPTION', 'BRIGHTNESS', 'SHOWLABELS', 'LONGNAME', 'MAXSCALE', 'SYMBOLOGY', 'SYMBOLOGYTYPE', 'SERVICEPROPERTIES', 'VISIBLE', 'DEFINITIONQUERY', 'DATASOURCE', 'TRANSPARENCY', 'TIME', 'MINSCALE', 'LABELCLASSES', 'CONTRAST', 'NAME'])", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-9-4ef8a62e6fa7>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mprop\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mprops\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[0mobj\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msupports\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mprop\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mValueError\u001b[0m: Invalid value for layer_property: 'FINDANDREPLACEWORKSPACEPATH' (choices are: ['CREDITS', 'DATASETNAME', 'WORKSPACEPATH', 'DESCRIPTION', 'BRIGHTNESS', 'SHOWLABELS', 'LONGNAME', 'MAXSCALE', 'SYMBOLOGY', 'SYMBOLOGYTYPE', 'SERVICEPROPERTIES', 'VISIBLE', 'DEFINITIONQUERY', 'DATASOURCE', 'TRANSPARENCY', 'TIME', 'MINSCALE', 'LABELCLASSES', 'CONTRAST', 'NAME'])" | |
] | |
} | |
], | |
"source": [ | |
"for prop in props:\n", | |
" if obj.supports(prop):\n", | |
" print prop, getattr(obj,prop)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Hm. Apparently, you cannot throw any string as an input argument for the `supports()` method. In this particular case, we are trying to check whether this layer supports `findAndReplaceWorkspacePath` property. Well, this is actually a method (i.e., a function), not a property. \n", | |
"\n", | |
"As we are not interested in methods, we have to filter them out of our `props` list. This can be done using the `callable()` function ([help link](https://docs.python.org/2/library/functions.html#callable)). " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"True\n", | |
"False\n" | |
] | |
} | |
], | |
"source": [ | |
"print callable(getattr(obj,'getExtent'))\n", | |
"print callable(getattr(obj,'datasetName'))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": false, | |
"scrolled": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"credits \n", | |
"dataSource C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"datasetName GridFeatures\n", | |
"definitionQuery \n", | |
"description \n" | |
] | |
}, | |
{ | |
"ename": "ValueError", | |
"evalue": "Invalid value for layer_property: 'FINDANDREPLACEWORKSPACEPATH' (choices are: ['CREDITS', 'DATASETNAME', 'WORKSPACEPATH', 'DESCRIPTION', 'BRIGHTNESS', 'SHOWLABELS', 'LONGNAME', 'MAXSCALE', 'SYMBOLOGY', 'SYMBOLOGYTYPE', 'SERVICEPROPERTIES', 'VISIBLE', 'DEFINITIONQUERY', 'DATASOURCE', 'TRANSPARENCY', 'TIME', 'MINSCALE', 'LABELCLASSES', 'CONTRAST', 'NAME'])", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-11-b1452789c1be>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mprop\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mprops\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[0mobj\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msupports\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mcallable\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mprop\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mprop\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mValueError\u001b[0m: Invalid value for layer_property: 'FINDANDREPLACEWORKSPACEPATH' (choices are: ['CREDITS', 'DATASETNAME', 'WORKSPACEPATH', 'DESCRIPTION', 'BRIGHTNESS', 'SHOWLABELS', 'LONGNAME', 'MAXSCALE', 'SYMBOLOGY', 'SYMBOLOGYTYPE', 'SERVICEPROPERTIES', 'VISIBLE', 'DEFINITIONQUERY', 'DATASOURCE', 'TRANSPARENCY', 'TIME', 'MINSCALE', 'LABELCLASSES', 'CONTRAST', 'NAME'])" | |
] | |
} | |
], | |
"source": [ | |
"for prop in props: \n", | |
" if obj.supports(prop):\n", | |
" if not callable(getattr(obj,prop)): \n", | |
" print prop, getattr(obj,prop)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"But as you see, the problem remains because we cannot check whether `obj.property` is a callable or not without getting a `ValueError` trying to access `obj.property` first.\n", | |
"\n", | |
"In such a case, one could use a very handy technique of supressing the exceptions using the `try/except` statement. So, if a property is not supported by this layer and if it is a callable, we don't want to keep this property. We will be able to continue running the `for` loop leaving in the `supported_props` list only valid properties." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Unsupported properties: ['brightness', 'contrast', 'findAndReplaceWorkspacePath', 'getExtent', 'getSelectedExtent', 'getSelectionSet', 'replaceDataSource', 'save', 'saveACopy', 'serviceProperties', 'setSelectionSet', 'supports', 'symbology', 'updateLayerFromJSON']\n", | |
"\n", | |
"Supported properties: ['credits', 'dataSource', 'datasetName', 'definitionQuery', 'description', 'isBroken', 'isFeatureLayer', 'isGroupLayer', 'isNetworkAnalystLayer', 'isRasterLayer', 'isRasterizingLayer', 'isServiceLayer', 'labelClasses', 'longName', 'maxScale', 'minScale', 'name', 'showLabels', 'symbologyType', 'time', 'transparency', 'visible', 'workspacePath']\n" | |
] | |
} | |
], | |
"source": [ | |
"unsupported_props = []\n", | |
"supported_props = []\n", | |
"\n", | |
"#get all props for Layer object\n", | |
"for prop in [prop for prop in dir(obj) if '_' not in prop]:\n", | |
" try: \n", | |
" if callable(getattr(obj,prop)): #exclude methods\n", | |
" unsupported_props.append(prop)\n", | |
" continue\n", | |
" else:\n", | |
" getattr(obj,prop) #check whether obj.supports(prop) raises ValueError\n", | |
" supported_props.append(prop)\n", | |
" except:\n", | |
" unsupported_props.append(prop)\n", | |
" \n", | |
"print 'Unsupported properties:', unsupported_props\n", | |
"print\n", | |
"print 'Supported properties:', supported_props" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now we can safely iterate through every property of this particular map layer:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"credits >>> \n", | |
"dataSource >>> C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"datasetName >>> GridFeatures\n", | |
"definitionQuery >>> \n", | |
"description >>> \n", | |
"isBroken >>> False\n", | |
"isFeatureLayer >>> True\n", | |
"isGroupLayer >>> False\n", | |
"isNetworkAnalystLayer >>> False\n", | |
"isRasterLayer >>> False\n", | |
"isRasterizingLayer >>> False\n", | |
"isServiceLayer >>> False\n", | |
"labelClasses >>> [<LabelClass object at 0x5af5950[0x51e0ed8]>]\n", | |
"longName >>> GridLayer\n", | |
"maxScale >>> 0.0\n", | |
"minScale >>> 0.0\n", | |
"name >>> GridLayer\n", | |
"showLabels >>> False\n", | |
"symbologyType >>> OTHER\n", | |
"time >>> <geoprocessing Layer Time object object at 0x0368E900>\n", | |
"transparency >>> 0\n", | |
"visible >>> True\n", | |
"workspacePath >>> C:\\GIS\\Temp\\test.gdb\n" | |
] | |
} | |
], | |
"source": [ | |
"for prop in supported_props:\n", | |
" print prop, \">>>\", getattr(obj,prop)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Another way to list out the values only for valid properties is to use the `hasattr()` method:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"dataSource >>> C:\\GIS\\Temp\\test.gdb\\GridFeatures\n", | |
"datasetName >>> GridFeatures\n", | |
"isFeatureLayer >>> True\n", | |
"labelClasses >>> [<LabelClass object at 0x5dbdb70[0x37dbe90]>]\n", | |
"longName >>> GridLayer\n", | |
"name >>> GridLayer\n", | |
"symbologyType >>> OTHER\n", | |
"time >>> <geoprocessing Layer Time object object at 0x0541F030>\n", | |
"visible >>> True\n", | |
"workspacePath >>> C:\\GIS\\Temp\\test.gdb\n" | |
] | |
} | |
], | |
"source": [ | |
"for prop in [prop for prop in dir(obj) if '_' not in prop]:\n", | |
" if hasattr(obj,prop):\n", | |
" if not callable(getattr(obj,prop)):\n", | |
" if getattr(obj,prop):\n", | |
" print prop, '>>>', getattr(obj,prop)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 2", | |
"language": "python", | |
"name": "python2" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 2 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython2", | |
"version": "2.7.10" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment