使用游标访问数据
游标是一种数据访问对象,可用于在表中迭代一组行或者向表中插入新行。游标有三种形式:搜索、插入或更新。游标通常用于读取现有几何和写入新几何。
每种类型的游标均由对应的 ArcPy 函数(SearchCursor、InsertCursor 或 UpdateCursor)在表、表格视图、要素类或要素图层上创建。搜索游标可用于检索行。更新游标可用于根据位置更新和删除行,而插入游标可用于向表或要素类中插入行。
游标 |
说明 |
---|---|
InsertCursor(dataset, {spatial_reference}) |
插入行 |
SearchCursor(dataset, {where_clause}, {spatial_reference}, {fields}, {sort_fields}) |
只读访问 |
UpdateCursor(dataset, {where_clause}, {spatial_reference}, {fields}, {sort_fields}) |
更新或删除行 |
游标遵循图层/表格视图定义查询和选择。游标对象仅包含任一地理处理工具在操作期间将使用到的行。
全部三个游标函数可创建用于访问行对象的游标对象。行对象支持的方法取决于所创建游标的类型。
游标只能向前导航,它们不支持备份和检索已经检索过的行,或者多轮次通过数据。如果脚本需要多轮次通过数据,应用程序必须重新执行游标函数。如果两次执行都是在同一编辑会话(或具有适当隔离级别的数据库事务)中进行的,则应用程序将确保不会应用由同时执行的另一应用程序对数据所做的更改。
以迭代方式搜索或更新游标的方式有两种:可通过 For 循环,或者通过 While 循环并使用游标的 next 方法返回下一行。如果要在游标上使用 next 方法来检索行数为 N 的表中的所有行,脚本必须反复调用 next N 次。在检索完结果集中的最后一行后调用 next 将返回 None,它是一种 Python 数据类型,此处用作占位符。
本例为一个简单的游标操作。它将打印出要素类中每行各个字符串字段的名称和值。
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))
从表中检索到的所有行对象在逻辑上包含一组顺序相同的字段。具体而言,即表中某行的字段顺序与 ListFields 函数返回的字段顺序相同。行内仅包含表中用于创建游标的可见字段,每个字段名称都是该对象的属性。
游标对象
SearchCursor、UpdateCursor 和 InsertCursor 函数能够创建可用于对记录进行遍历的游标对象(有时称为枚举对象)。由不同游标函数创建的游标对象的方法不同,具体取决于创建的游标类型。
下图显示的是各游标类型支持的方法:
游标类型 |
方法 |
对位置的影响 |
---|---|---|
搜索 |
next |
检索下一个行对象 |
插入 |
newRow |
创建空的行对象 |
insertRow |
向表中插入行对象 | |
next |
检索下一个行对象 | |
更新 |
updateRow |
用经过修改的行对象更新当前行 |
deleteRow |
从表中删除行 | |
next |
检索下一个行对象 |
insertRow
插入游标用于创建并插入新行。基本的处理流程是在要插入行的游标对象上使用 newRow 方法。新的行对象包含在表中找到的所有字段,并且在使用 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
updateRow 方法用于对更新游标当前所在位置的行进行更新。从游标对象提取行对象后,可以根据需要对行进行修改,然后调用 updateRow 传入修改后的行。
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
deleteRow 方法用于删除更新游标当前所在位置的行。提取行对象后,可在游标上调用 deleteRow 删除行。
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 和 setValue
有两种基本方法可以获取和设置行中的字段值:
- 使用字段名,例如 value = row.road_type
- 使用 getValue 和 setValue,例如 value = row.getValue("road_type")
方法 |
说明 |
---|---|
getValue(field_name) |
获取字段值 |
isNull(field_name) |
字段值为空吗 |
setNull(field_name) |
将字段值设置为空 |
setValue(field_name, object) |
设置字段值 |
下例将为工作空间中的所有多边形要素类创建查找表。该查找表包含与要素类中各要素 ObjectID 对应的 ID,以及可供缓冲工具使用的距离值。
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)
游标和锁定
插入和更新游标遵循由 ArcGIS 应用程序设置的表锁。锁能够防止多个进程同时更改同一个表。锁有两种类型 - 共享和排他。
- 只要访问表或数据集就会应用共享锁。同一表中可以存在多个共享锁,但存在共享锁时,将不允许存在排它锁。应用共享锁的示例包括:在 ArcMap 中显示要素类时以及在 ArcCatalog 中预览表时。
- 对表或要素类进行更改时,将应用排它锁。在 ArcGIS 中应用排它锁的示例包括:在 ArcMap 中编辑和保存要素类时;在 ArcCatalog 中更改表的方案时;或者在 Python IDE(例如 PythonWin)中在要素类上使用插入游标时。
如果数据集上存在排它锁,则无法为表或要素类创建更新和插入游标。UpdateCursor 或 InsertCursor 函数会因数据集上存在排它锁而失败。如果这些函数成功地创建了游标,它们将在数据集上应用排它锁,从而使两个脚本无法在同一数据集上创建更新和插入游标。
在应用程序或脚本释放数据集(通过关闭或明确释放游标对象)之前,锁将一直存在。在脚本中,应删除游标对象,以便释放该游标对象设置在数据集上的排它锁。否则,将会阻止所有其他应用程序或脚本访问数据集,而这是毫无必要的。下面的示例显示了如何打开并释放更新游标。其中使用错误处理程序来检测 UpdateCursor 函数是否因表中存在其他排它锁而失败。
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
ArcMap 中的编辑会话将在其会话期间对数据应用共享锁。保存编辑内容时将应用排它锁。已经存在排它锁时,数据集是不可编辑的。
使用 Python 编辑器时(例如 PythonWin),可能需要清除对象引用以删除由游标设置的数据集锁。使用 gc(垃圾收集)模块来控制何时删除未使用对象和/或明确删除脚本中的引用。