ArcPy游标怎么用?UpdateCursor咋写?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

引言:很多刚接触 ArcPy 的同学都会问:ArcPy游标怎么用?UpdateCursor咋写? 这其实是 ArcGIS Pro 自动化处理中非常常见的问题。比如批量给要素类字段赋值、按条件修改属性、根据几何面积更新字段、清洗空值数据,都会用到 ArcPy 游标,尤其是 arcpy.da.UpdateCursor

本文不讲空泛概念,直接围绕一个具体任务来说明:如何使用 ArcPy 游标读取字段、判断条件、修改属性并保存结果。读完后,你应该能看懂 SearchCursorUpdateCursorInsertCursor 的区别,并能独立写出安全可运行的 UpdateCursor 代码。

ArcPy游标怎么用 UpdateCursor更新字段流程示意图
ArcPy UpdateCursor 的基本工作流:打开数据、逐行读取、修改 row、调用 updateRow 写回。

背景:为什么 ArcPy 游标这么常用

背景:在 ArcGIS Pro 里,我们可以通过字段计算器、选择工具、编辑器手动修改属性表。但当数据量较大、规则较复杂,或者需要重复执行同一套处理逻辑时,手动操作就很容易出错。

ArcPy 游标就是 Python 访问 GIS 表和要素类记录的接口。它可以像遍历 Excel 行一样,逐行读取属性表,并在需要时更新或新增记录。

常见使用场景包括:

  • 根据某个字段值批量更新分类字段。
  • 把空值、异常值替换成统一编码。
  • 根据面积、长度、坐标等几何信息写入新字段。
  • 按条件删除不合格记录。
  • 从一个表读取数据后写入另一个要素类。

在 ArcPy 中,推荐使用 arcpy.da 模块下的游标。这里的 da 是 Data Access 的缩写,相比旧版游标速度更快、写法更清晰,也是现在更常用的方式。

原理:ArcPy 游标到底在做什么

原理:ArcPy 游标的核心逻辑可以理解为“打开一张表,然后逐行处理”。每一行在代码里通常表现为一个列表或元组,列表中的每个位置对应你传入的字段列表。

例如字段列表是:

fields = ["NAME", "TYPE", "STATUS"]

那么游标读到的一行可能是:

row = ["人民公园", "绿地", None]

其中:

  • row[0] 对应 NAME
  • row[1] 对应 TYPE
  • row[2] 对应 STATUS

如果使用 UpdateCursor,你可以修改 row 中的值,然后调用 cursor.updateRow(row) 把修改写回数据源。

ArcPy 常见游标主要有三类:

游标类型 用途 是否修改数据
arcpy.da.SearchCursor 读取记录
arcpy.da.UpdateCursor 读取并更新记录,也可删除记录
arcpy.da.InsertCursor 插入新记录

如果只是查看或统计数据,用 SearchCursor。如果要改字段值,用 UpdateCursor。如果要新增要素或新增表记录,用 InsertCursor

步骤:UpdateCursor 最小可运行写法

步骤:先看一个最小示例。假设有一个面要素类 parks,字段 TYPE 表示公园类型,字段 STATUS 需要根据类型更新。如果 TYPE 是“绿地”,就把 STATUS 更新为“已核查”。

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["TYPE", "STATUS"]

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        if row[0] == "绿地":
            row[1] = "已核查"
            cursor.updateRow(row)

print("更新完成")

这段代码包含 ArcPy UpdateCursor 的几个关键点:

  • fc 是输入要素类或表路径。
  • fields 是需要读取和修改的字段列表。
  • with 语句用于自动释放游标锁。
  • for row in cursor 表示逐行遍历。
  • 修改 row[1] 后,必须调用 cursor.updateRow(row)

很多人写 UpdateCursor 时只改了 row,但忘记写 updateRow,结果属性表没有任何变化。这是最常见的问题之一。

步骤:用 where_clause 只更新符合条件的记录

如果数据量较大,不建议在 Python 里遍历全部记录再判断。更好的做法是使用 where_clause 先筛选记录。

例如只更新 TYPE = '绿地' 的记录:

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["TYPE", "STATUS"]
where_clause = "TYPE = '绿地'"

with arcpy.da.UpdateCursor(fc, fields, where_clause) as cursor:
    for row in cursor:
        row[1] = "已核查"
        cursor.updateRow(row)

print("符合条件的记录已更新")

使用 where_clause 有两个好处:

  • 减少遍历行数,提高处理效率。
  • 逻辑更清楚,避免误改不相关记录。

不过,SQL 条件写法会受数据源影响。File Geodatabase、Enterprise Geodatabase、Shapefile 对字段名、日期、字符串的 SQL 语法可能略有差异。为了更稳妥,可以使用 arcpy.AddFieldDelimiters 处理字段分隔符。

import arcpy

fc = r"D:gis_projectdata.gdbparks"
field_name = "TYPE"
field_delimited = arcpy.AddFieldDelimiters(fc, field_name)
where_clause = f"{field_delimited} = '绿地'"

with arcpy.da.UpdateCursor(fc, ["TYPE", "STATUS"], where_clause) as cursor:
    for row in cursor:
        row[1] = "已核查"
        cursor.updateRow(row)

步骤:根据面积更新字段

ArcPy 游标不只能处理普通属性字段,也可以读取几何字段。常用的几何令牌包括 SHAPE@SHAPE@AREASHAPE@LENGTHSHAPE@XY 等。

假设需要根据面要素面积更新 AREA_LEVEL 字段:

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["SHAPE@AREA", "AREA_LEVEL"]

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        area = row[0]

        if area >= 100000:
            row[1] = "大型"
        elif area >= 10000:
            row[1] = "中型"
        else:
            row[1] = "小型"

        cursor.updateRow(row)

print("面积等级更新完成")

这里要注意:SHAPE@AREA 的单位取决于数据坐标系。如果数据是地理坐标系,经纬度单位不是米,面积结果通常不适合直接用于平方米判断。更稳妥的做法是先投影到合适的投影坐标系,再计算面积。

步骤:处理空值和异常值

实际项目中,字段空值非常常见。Python 中通常用 None 表示空值。下面示例把 STATUS 为空的记录统一更新为“未核查”。

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["STATUS"]

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        if row[0] is None or row[0] == "":
            row[0] = "未核查"
            cursor.updateRow(row)

print("空值处理完成")

如果字段来自 Shapefile,要特别注意:Shapefile 对字段名长度、字段类型、中文编码都有更多限制。对于需要长期维护的数据,建议优先使用 File Geodatabase。

步骤:用 UpdateCursor 删除记录

UpdateCursor 还可以删除当前行。比如删除 STATUS 为“废弃”的记录:

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["STATUS"]

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        if row[0] == "废弃":
            cursor.deleteRow()

print("废弃记录已删除")

删除操作不可轻易执行。建议先用 SearchCursor 或 ArcGIS Pro 选择工具检查待删除记录数量,再运行删除代码。必要时先复制一份数据作为备份。

常见坑:UpdateCursor 写了但数据没变化

常见坑:如果 ArcPy UpdateCursor 没有报错,但属性表没有变化,通常先检查以下几项。

  • 是否调用了 cursor.updateRow(row)
  • 字段顺序是否和 row[index] 对应正确。
  • 是否修改了只读字段,例如 OBJECTID、某些系统字段。
  • where 条件是否筛选不到任何记录。
  • 数据是否被 ArcGIS Pro 编辑会话、属性表或其他程序锁定。
  • 是否在企业级地理数据库中缺少编辑权限。
  • 字段类型是否不匹配,例如把中文字符串写入数值字段。

一个简单的排查方法是在循环里打印更新前后的值:

import arcpy

fc = r"D:gis_projectdata.gdbparks"
fields = ["TYPE", "STATUS"]

count = 0

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        if row[0] == "绿地":
            print("更新前:", row)
            row[1] = "已核查"
            cursor.updateRow(row)
            print("更新后:", row)
            count += 1

print(f"共更新 {count} 条记录")

如果 count 是 0,说明条件没有匹配到记录。如果打印结果正确但数据仍没变,要重点检查数据路径、权限和锁定状态。

常见坑:字段名、字段类型和编码问题

ArcPy 游标对字段名和字段类型比较敏感。以下问题在初学者代码中很常见:

  • 字段名拼错,例如 statusSTATUS 混用。
  • 字段不存在,但复制了旧脚本里的字段列表。
  • 把字符串写进整型字段。
  • 把超过字段长度的文本写入文本字段。
  • Shapefile 字段名超过 10 个字符后被截断。
  • 中文字段名或中文路径在旧环境中导致编码问题。

可以先用 arcpy.ListFields 查看字段名称和类型:

import arcpy

fc = r"D:gis_projectdata.gdbparks"

for field in arcpy.ListFields(fc):
    print(field.name, field.type, field.length)

如果你不确定字段类型,先打印字段结构,再决定 UpdateCursor 中要写入什么值。

方法比较:SearchCursor、UpdateCursor、InsertCursor 怎么选

方法比较:很多同学会把三种 ArcPy 游标混在一起。下面用一张表说明它们的使用边界。

需求 推荐游标 示例
只读取字段值 SearchCursor 统计每种用地类型数量
修改已有字段值 UpdateCursor 把空值状态改成“未核查”
删除已有记录 UpdateCursor 删除质量检查不合格记录
新增表记录或新增要素 InsertCursor 批量生成点要素
复杂字段计算 UpdateCursor 或字段计算工具 按多个字段组合生成分类编码

如果逻辑很简单,例如直接把一个字段统一赋值,用 ArcGIS Pro 的字段计算器也可以。如果规则包含多条件判断、跨字段处理、几何读取、批量循环多个图层,则更适合写 ArcPy UpdateCursor。

方法比较:UpdateCursor 和 Calculate Field 该用哪个

ArcGIS Pro 的 Calculate Field 工具和 UpdateCursor 都能修改字段值,但适合的场景不同。

对比项 Calculate Field UpdateCursor
上手难度 较低 需要 Python 基础
批量自动化 一般
多字段复杂判断 可以但不够清晰 更灵活
多图层循环处理 不方便 适合
可复用性 较弱 脚本可长期复用

简单理解:一次性的小计算可以用 Calculate Field;需要重复执行、批量处理、写入项目流程的任务,更建议使用 ArcPy 游标。

检查清单:写 UpdateCursor 前先确认这些事

检查清单:在项目中正式运行 ArcPy UpdateCursor 前,建议按下面顺序检查。

  • 确认输入路径正确,最好使用绝对路径。
  • 确认要修改的是测试数据或已经备份的数据。
  • 确认字段名存在,字段类型符合写入值。
  • 确认 where_clause 能选中预期记录。
  • 确认 ArcGIS Pro 没有处于未保存编辑状态。
  • 确认没有其他程序占用同一个地理数据库或 Shapefile。
  • 先打印更新数量,再对全量数据运行。
  • 涉及面积、长度时,先确认坐标系和单位。
  • 涉及企业级地理数据库时,确认版本、权限和编辑规则。

建议初学者先使用小样本数据测试。等逻辑确认无误后,再替换为正式数据路径。

FAQ:ArcPy游标怎么用的常见问题

Q1:UpdateCursor 必须写在 with 里面吗?

不是绝对必须,但强烈建议使用 with。这样游标运行结束后会自动释放数据锁,减少“无法删除数据”“无法编辑数据”的问题。

Q2:为什么我修改了 row,但是属性表没变?

最常见原因是忘记调用 cursor.updateRow(row)。修改 row 只是改了内存中的当前行,只有执行 updateRow 后,结果才会写回数据源。

Q3:UpdateCursor 可以修改 OBJECTID 吗?

通常不可以。OBJECTID 是 ArcGIS 管理的系统字段,不应手动修改。类似的系统字段、几何管理字段、部分只读字段都不适合用 UpdateCursor 直接改。

Q4:UpdateCursor 能不能同时更新多个字段?

可以。把多个字段放进字段列表,然后按索引修改对应位置即可。关键是字段顺序和 row[index] 必须一一对应。

fields = ["TYPE", "STATUS", "CHECKER"]

with arcpy.da.UpdateCursor(fc, fields) as cursor:
    for row in cursor:
        if row[0] == "绿地":
            row[1] = "已核查"
            row[2] = "Dr.GIS"
            cursor.updateRow(row)

Q5:where_clause 怎么写才不容易错?

字符串字段通常需要单引号,例如 TYPE = '绿地'。为了兼容不同数据源,建议用 arcpy.AddFieldDelimiters 处理字段名。复杂 SQL 条件先在 ArcGIS Pro 的按属性选择工具中测试,再写入脚本。

Q6:UpdateCursor 适合处理几十万条数据吗?

可以,但要注意优化。优先使用 where_clause 减少遍历范围,只传入必要字段,避免在循环中频繁执行耗时操作。对于非常大的企业级数据库,还要考虑索引、事务、版本管理和数据库权限。

Q7:ArcGIS Pro 里运行脚本和独立 Python 运行有什么区别?

在 ArcGIS Pro 的 Python 窗口或 Notebook 中运行,环境通常已经配置好。独立 Python 脚本运行时,需要使用 ArcGIS Pro 自带的 Python 环境,确保可以正常导入 arcpy。如果环境不对,通常会报 No module named arcpy

结论:掌握 UpdateCursor 就能处理大多数属性批量更新

结论:ArcPy 游标的核心并不复杂:选择字段,逐行遍历,判断条件,修改 row,然后用 updateRow 写回。真正容易出错的地方,往往是字段顺序、字段类型、SQL 条件、坐标单位和数据锁。

如果你刚开始学习,建议先从 SearchCursor 读取数据,再练习 UpdateCursor 修改一个字段,最后再加入 where_clause、几何令牌和多字段判断。这样学习路径最稳,也最接近真实 GIS 项目中的脚本写法。

简单记住一句话:只读用 SearchCursor,修改用 UpdateCursor,新增用 InsertCursor。当你能熟练写出 UpdateCursor,ArcPy 批量处理属性表的大部分问题就已经解决了一半。