Acceso a datos utilizando cursores
Un cursor es un objeto de acceso a datos que se puede utilizar para recorrer el conjunto de filas de una tabla o insertar nuevas filas en una tabla. Los cursores tienen tres formas: búsqueda, inserción o actualización. Los cursores se utilizan normalmente para leer geometrías existentes y escribir geometrías nuevas.
Cada tipo de cursor es creado por una función de ArcPy correspondiente (SearchCursor, InsertCursor o UpdateCursor) en una tabla, vista de tabla, clase de entidad o capa de entidades. Un cursor de búsqueda se puede utilizar para recuperar filas. Un cursor de actualización se puede utilizar para actualizar y eliminar filas en función de la posición, mientras que un cursor de inserción se utiliza para insertar filas en una tabla o una clase de entidad.
Cursor |
Explicación |
---|---|
InsertCursor(dataset, {referencia_espacial}) |
Inserta filas |
SearchCursor(dataset, {cláusula_where}, {referencia_espacial}, {campos}, {campos_de_orden}) |
Acceso de solo lectura |
UpdateCursor(dataset, {cláusula_where}, {referencia_espacial}, {campos}, {campos_de_orden}) |
Actualiza o elimina filas |
Los cursores respetan las selecciones y consultas de definición de vista de capa/tabla. El objeto de cursor solo contiene las filas que utilizaría cualquier herramienta de geoprocesamiento durante una operación.
Las tres funciones de cursor crean un objeto de cursor que se puede utilizar para tener acceso a objetos de fila. Los métodos admitidos por el objeto de fila dependen del tipo de cursor creado.
Los cursores solo se pueden recorrer en la dirección de avance; no permiten retroceder y recuperar filas que ya hayan sido recuperadas, ni pasar varias veces sobre los datos. Si una secuencia de comandos necesita pasar varias veces sobre los datos, la aplicación debe ejecutar de nuevo la función de cursor. Si ambas ejecuciones se realizan dentro de la misma sesión de edición (o transacción de base de datos con el nivel adecuado de aislamiento), se garantiza que la aplicación no verá ningún cambio realizado en los datos por otras aplicaciones que se ejecuten simultáneamente.
Los cursores de búsqueda o actualización pueden recorrerse con un bucle For o un bucle While utilizando el método next del cursor para devolver la fila siguiente. Cuando se utiliza el método next en un cursor para recuperar todas las filas de una tabla que contiene N filas, la secuencia de comandos debe realizar N llamadas a next. Una llamada a next una vez recuperada la última fila del conjunto de resultados devuelve None, que es un tipo de datos de Python que actúa aquí como un marcador de posición.
En este ejemplo se muestra una operación de cursor simple. Imprime el nombre y el valor de cada campo de cadena para cada fila de una clase de entidad.
import arcpy fc = "D:/st_johns/roads.shp" # Create a search cursor # rows = arcpy.SearchCursor(fc) # Create a list of string fields fields = arcpy.ListFields(fc, "", "String") for row in rows: for field in fields: if field.type != "Geometry": print "%s: Value = %s" % (field.name, row.getValue(field.name))
Todos los objetos de fila recuperados de una tabla contienen lógicamente el mismo conjunto ordenado de campos. En particular, el orden de los campos en una fila de una tabla es igual que el orden de campos devuelto por la función ListFields. La fila contendrá solo los campos visibles de la tabla utilizada para crear el cursor, siendo cada nombre de campo una propiedad del objeto.
El objeto de cursor
Las funciones SearchCursor, UpdateCursor e InsertCursor crean un objeto de cursor (conocido a veces como objeto de enumeración) que se puede utilizar para recorrer los registros. Los métodos del objeto de cursor creados por las diversas funciones de cursor varían, dependiendo del tipo de cursor creado.
La tabla siguiente muestra los métodos admitidos para cada tipo de cursor:
Tipo de cursor |
Método |
Efecto sobre la posición |
---|---|---|
Buscar |
next |
Recupera el siguiente objeto de fila |
Insertar |
newRow |
Crea un objeto de fila vacío |
insertRow |
Inserta un objeto de fila en la tabla |
|
next |
Recupera el siguiente objeto de fila |
|
Actualizar |
updateRow |
Actualiza la fila actual con un objeto de fila modificado |
deleteRow |
Quita la fila de la tabla |
|
next |
Recupera el siguiente objeto de fila |
insertRow
El cursor de inserción se utiliza para crear nuevas filas e insertarlas. El flujo básico del procesamiento consiste en utilizar el método newRow en el objeto de cursor en el que se va a insertar las filas. Este nuevo objeto de fila contiene todos los campos que se encuentran en la tabla; puede asignar valores a los campos de la fila antes de insertar la fila mediante insertRow.
import arcpy # Create insert cursor for table # rows = arcpy.InsertCursor("D:/St_Johns/data.gdb/roads_lut") x = 1 # Create 25 new rows. Set the initial row ID and distance values # while x <= 25: row = rows.newRow() row.rowid = x row.distance = 100 rows.insertRow(row) x = x + 1
updateRow
El método updateRow se utiliza para actualizar la fila en la posición actual de un cursor de actualización. Después de obtener un objeto de fila del objeto de cursor, modifique la fila como sea necesario y llame a updateRow, pasando la fila modificada.
import arcpy # Create update cursor for feature class # rows = arcpy.UpdateCursor("D:/St_Johns/data.gdb/roads") for row in rows: # Update the field used in buffer so the distance is based on the road # type. Road type is either 1, 2, 3, or 4. Distance is in meters. # row.buffer_distance = row.road_type * 100 rows.updateRow(row)
deleteRow
El método deleteRow se utiliza para eliminar la fila de la posición actual de un cursor de actualización. Después de obtener el objeto de fila, llame a deleteRow en el cursor para eliminar la fila.
import arcpy # Create update cursor for feature class # rows = arcpy.UpdateCursor("D:/St_Johns/data.gdb/roads") for row in rows: # Delete all rows that have a roads type of 4 if row.road_type == 4: rows.deleteRow(row)
getValue y setValue
Hay dos maneras básicas de obtener y establecer valores de campo en una fila:
- Utilizar el nombre de campo, como en value = row.road_type
- Utilizar getValue y setValue, como en value = row.getValue("road_type")
Método |
Explicación |
---|---|
getValue(nombre_de_campo) |
Obtiene el valor del campo |
isNull(nombre_de_campo) |
Indica si el valor del campo es nulo |
setNull(nombre_de_campo) |
Establece el valor del campo en nulo |
setValue(nombre_de_campo, objeto) |
Establece el valor del campo |
En el siguiente ejemplo se crea una tabla de búsqueda para todas las clases de entidad de polígono de un espacio de trabajo. La tabla de búsqueda contiene un ID que corresponde al ID de objeto para cada entidad de la clase de entidad y un valor de distancia que se podría utilizar para la herramienta Buffer.
import arcpy from arcpy import env import os env.overwriteOutput = 1 env.workspace = arcpy.GetParameterAsText(0) # List the polygon feature classes # fcs = arcpy.ListFeatureClasses("*","polygon") # Loop through the results. # for fc in fcs: # Get the ObjectID field # desc = arcpy.Describe(env.workspace + os.sep + fc) OIDFieldName = desc.OIDFieldName # Create lookup table name # if fc.endswith(".shp"): tab = fc.replace(".", "_lut.") else: tab = fc + "_lut" # Create lookup table and add fields # arcpy.CreateTable_management(env.workspace, tab) arcpy.AddField_management(tab,"id","long") arcpy.AddField_management(tab,"dist","long") # Open insert cursor on new lookup table # tabcur = arcpy.InsertCursor(env.workspace + os.sep + tab) # Open search cursor on feature class # featcur = arcpy.SearchCursor(env.workspace + os.sep + fc) # Loop through the rows in the feature class # for f_row in featcur: # Create a new row for the lookup table and set its ID to be the # same as the feature OID and set the distance to 100. # t_row = tabcur.newRow() t_row.id = f_row.getValue(OIDFieldName) t_row.dist = 100 # Insert the row into the lookup table and get the next row # from the feature class # tabcur.insertRow(t_row)
Cursores y bloqueo
Los cursores de inserción y actualización respetan los bloqueos de tabla establecidos por aplicaciones ArcGIS. Los bloqueos evitan que varios procesos cambien la misma tabla al mismo tiempo. Hay dos tipos de bloqueos: compartidos y exclusivos.
- Un bloqueo compartido se aplica cada vez que se tiene acceso a una tabla o a un dataset. Pueden existir varios bloqueos compartidos para una tabla, pero no se permite ningún bloqueo exclusivo si existe un bloqueo compartido. La visualización de una clase de entidad en ArcMap y la vista previa de una tabla en ArcCatalog son ejemplos de cuándo se aplicaría un bloqueo compartido.
- Los bloqueos exclusivos se aplican cuando se hace algún cambio en una tabla o clase de entidad. Editar y guardar una clase de entidad en ArcMap; cambiar el esquema de una tabla en ArcCatalog; o utilizar un cursor de inserción en una clase de entidad en un IDE de Python, tal como PythonWin, son ejemplos de cuándo aplica ArcGIS un bloqueo exclusivo.
No se puede crear cursores de actualización e inserción para una tabla o clase de entidad si existe un bloqueo exclusivo para ese dataset. Las funciones UpdateCursor o InsertCursor producen un error debido a un bloqueo exclusivo sobre el dataset. Si estas funciones crean un cursor correctamente, aplican un bloqueo exclusivo sobre el dataset para que dos secuencias de comandos no puedan crear un cursor de actualización o inserción en el mismo dataset.
Los bloqueos persisten hasta la aplicación o la secuencia de comandos libera el dataset, cerrando o liberando el objeto de cursor explícitamente. En una secuencia de comandos, el objeto de cursor se debe eliminar para que se libere el bloqueo exclusivo que impuso sobre el dataset. De lo contrario, se podría impedir a todas las demás aplicaciones o secuencias de comandos el acceso a un dataset. En el siguiente ejemplo se muestra cómo abrir un cursor de actualización y cómo liberarlo. Se utiliza un controlador de errores para comprobar si la función UpdateCursor produce un error debido a otro bloqueo exclusivo sobre la tabla.
import arcpy # Create update cursor for feature class # try: # The variables row and rows are initially set to None, so that they # can be deleted in the finally block regardless of where (or if) # script fails. # row, rows = None, None rows = arcpy.UpdateCursor("D:/St_Johns/data.mdb/roads") # Update the field used in buffer so the distance is based on the # road type. Road type is either 1, 2, 3 or 4. Distance is in meters. # for row in rows: row.buffer_distance = row.road_type * 100 rows.updateRow(row) except: if not arcpy.GetMessages() == "": arcpy.AddMessage(arcpy.GetMessages(2)) finally: # Regardless of whether the script succeeds or not, delete # the row and cursor # if row: del row if rows: del rows
Una sesión de edición en ArcMap aplica un bloqueo compartido a los datos durante la sesión de edición. Cuando se guardan las ediciones se aplica un bloqueo exclusivo. Un dataset no es editable si ya existe un bloqueo exclusivo.
Al trabajar en un editor de Python, tal como PythonWin, puede que necesite limpiar las referencias de objeto para quitar bloqueos de dataset establecidos por cursores. Utilice el módulo gc (recolección de elementos no utilizados) para controlar cuándo se quitan los objetos no utilizados o para eliminar referencias explícitamente dentro de la secuencia de comandos.