欢迎光临
我们一直在努力

python学习-Python2.7与3.x差异-arcpy场景

python学习—Python 2.7 与 3.x 差异(arcpy 场景)


发布信息

  • 标题:python学习—Python 2.7 与 3.x 差异(arcpy 场景)
  • 摘要:ArcGIS Desktop 锁死 Python 2.7,而网上教程九成是 3.x。本文从环境归属讲到 print、除法、异常、字典、中文编码五类实际会撞上的差异,给出 arcpy 场景下的正确写法和一张速查表,附迁移自检清单。
  • 标签:Python2.7、Python3、arcpy、ArcGIS、字符编码、版本差异
  • 专栏:python学习

系列文章目录

python学习—阻止谷歌浏览器升级 python学习—合并多个word文档之VBA宏代码方法 python学习—合并多个word文档 python学习—详解word邮件合并 python学习—批量复制并重命名文件夹 python学习—字典(Dictionary) python学习—列表和元组 python学习—合并多个Excel工作簿表格文件 python学习—循环语句-控制流 python学习—年会不能停,游戏抽签抽奖 python学习—查找指定目录下的指定类型文件 python学习—统计嵌套文件夹内的文件数量并建立索引表格 python学习—合并TXT文本文件


功能说明

写 arcpy 的人大概都经历过这件事:

在自己电脑上跑得好好的脚本,拷到同事电脑上,双击运行,第一行就给你一个 SyntaxError。或者照着网上教程抄了一段 Python 代码,贴进 ArcMap 的 Python 窗口,满屏红色报错,一句都看不懂。

不是代码写错了,是你抄的是 Python 3 的代码,而 ArcGIS Desktop 只能跑 Python 2.7。

这个坑绕不开:ArcGIS 桌面版最后一版是 10.8.2,它绑定的就是 Python 2.7,不会再变了。而网上能搜到的 Python 教程、ChatGPT 给的代码、新一点的第三方库文档,默认全是 python 3.x 语法。两边的语法差异虽然不大,但每一处都致命——不报错,就是结果错;不报结果错,就是中文乱码。

本文想解决四件事:

  • 1 说清电脑上这几套 Python 分别从哪来、谁有 arcpy、谁没有
  • 2 列出实际会撞上的语法差异,每条给可直接运行的对比代码
  • 3 讲透 arcpy 场景下最痛的中文编码问题,给出正确写法
  • 4 一张速查表 + 一份迁移自检清单,改代码时照着对

我使用的 arcgis 软件版本为 10.8.2 版本,自带的 Python 是 2.7.18,32 位。文中的 py2 代码均可直接在 ArcMap 的 Python 窗口运行。 (我的电脑上还另外安装了 python 3.13版本,用来其他 非 arcpy代码使用的环境,使用PyCharm作为IDE编辑器)。


1 先搞清楚:三套 Python,谁是谁

很多人的电脑上同时躺着三个 Python,互相打架。以我的电脑为例:

ArcGIS Desktop 10.8.2 自带ArcGIS Pro 自带自己装的通用版
Python 版本 2.7.18 3.6 ~ 3.11(随 Pro 版本) 3.13
典型路径 C:\\Python27\\ArcGIS10.8 C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\arcgispro-py3 C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python313
位数 32 位 64 位 64 位
有没有 arcpy 有 有(另一个 arcpy) 永远没有
地图文档模块 arcpy.mapping arcpy.mp —

关键点一:两个 arcpy 不是同一个东西。

Desktop 的 arcpy 是一堆编译好的 32 位 .pyd 文件,只能被 32 位的 Python 2.7 加载; Pro 的 arcpy 是 64 位、Python 3 的包。 你把 Pro 的 arcpy 目录塞给 Python 3.13,只会得到:

ImportError: DLL load failed: 找不到指定的模块。

或者干脆:

ModuleNotFoundError: No module named 'arcpy'

2

关键点二:自己装的那个 Python 3.13,永远不会有 arcpy。

arcpy 不是 pip 能装的包,它跟着 ArcGIS 安装包一起落盘。所以在 PyCharm 里新建项目,如果 Interpreter 解释器环境 选的是 3.13,import arcpy 必然失败——这不是环境变量没配好,是方向错了。

关键点三:想看当前解释器是谁,一句话。

import sys
print(sys.version)

py2 输出(注意末尾的 32 bit,这是 ArcGIS Desktop 的 Python,别搞混):

2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 20:25:09) [MSC v.1500 32 bit (Intel)]

4

py3 输出长这样:

3.13.0 (main, Oct 7 2024, 12:13:39) [MSC v.1941 64 bit (AMD64)]

9

在 ArcMap 的 Python 窗口里第一条命令就敲这个,能避免后面所有误会。

温馨提示:ArcGIS 10.8.2 装完之后,开始菜单里 ArcGIS 组下面有个 Python 2.7 文件夹,里面是 IDLE 和 Python (command line)。IDLE 比 ArcMap 自带的 Python 窗口好用得多——有语法高亮、能保存 .py 文件、报错信息带行号。日常写 arcpy 建议在这里写,调通了再贴进 Python 窗口。


2 语法差异:五处必踩

(1) print:从语句到函数

py2 里 print 是语句,不用括号:

print "hello"
print "坐标:", x, y

py3 里它是函数,必须带括号:

print("hello")
print("坐标:", x, y)

这是最容易被"看起来没问题"骗过去的一处。 print("hello") 在 py2 里也能跑,因为括号被当成普通的分组表达式,等价于 print ("hello")。但一旦有多个参数就露馅:

# py2 中执行
print("坐标:", 100, 200)

py2 输出:

('坐标:', 100, 200)

一个元组。括号被理解成了"把这三个东西打包成元组再打印",而不是"三个参数"。

arcpy 场景:调试时想看游标取出来的值,写成 print(row) 在两边都对,因为只有一个参数。但写成 print("值:", row) 就只在 py3 对。稳妥写法是用格式化:

print("值:{}".format(row))

这种写法两边完全一致。

如果一定要在 py2 里用函数式 print(比如要 end= 参数),加一行:

from __future__ import print_function

(2) 除法:整数相除,小数消失

py2 的 / 遇到两个整数,做的是地板除,直接砍掉小数:

# py2
print 5 / 2 # 2 不是 2.5
print 5 / 2.0 # 2.5 有一边是浮点才正常
print 5 // 2 # 2 地板除,两边一致

9

py3 的 / 永远是真除法,// 才是地板除。 8

这一处不报错,但结果错,最阴。

arcpy 里最典型的翻车现场是面积单位换算:

# 字段值是 LONG 类型,单位平方米
area = row[0] # 12345678
mu = area / 10000 # py2 得 1234,小数点后全丢

正确写法,主动制造浮点:

mu = area / 10000.0 # 1234.5678
# 或者
mu = float(area) / 10000

12

要整体改成本意,加一行让 / 变成真除法:

from __future__ import division

arcpy 场景提醒:arcpy.Describe() 拿到的 extent.width、shape.area 都是浮点,不受影响。真正危险的是属性表里 SHORT / LONG 字段的值——读出来是 int,一转除法就掉精度。

(3) 异常捕获:逗号换成 as

# py2 老写法,py3 直接 SyntaxError
try:
arcpy.Project_management(src, dst, sr)
except Exception, e:
print e

在这里插入图片描述 在这里插入图片描述

# py2.6+ 和 py3 都认的写法
try:
arcpy.Project_management(src, dst, sr)
except Exception as e:
print(e)

注意:except … as e 在 Python 2.6 就已经支持,所以 py2.7 完全可以用。写 arcpy 代码时统一用 as,能省一半迁移麻烦。

另外 py2 的 print e(不带括号)在 py3 是语法错误,改成 print(e) 或 arcpy.AddError(str(e)) 两边都对。

(4) range 和 xrange

# py2
range(1000000) # 立刻在内存里建一个 100 万元素的 list,占几十 MB
xrange(1000000) # 惰性生成,几乎不占内存

# py3
range(1000000) # 本身就是惰性对象,没有 xrange 这个名字了

arcpy 场景:批处理几千个要素类时,for i in xrange(len(lst)) 在 py2 是标准写法,搬到 py3 报 NameError: name 'xrange' is not defined。

统一写法:

for i in range(len(lst)):

py2 里如果 len(lst) 不大(几千以内),用 range 完全无所谓;要是上万,才需要考虑 xrange。写 arcpy 脚本时列表通常就几十上百项,直接 range 更省心。

更好的做法是根本不用索引:

for fc in lst:

(5) input 和 raw_input

# py2
name = raw_input("输入名称:") # 拿到字符串,安全
name = input("输入名称:") # 等价于 eval(raw_input(…)),输入 1+1 得 2,输入中文不加引号直接报错

# py3
name = input("输入名称:") # 就是 py2 的 raw_input

arcpy 场景:写工具箱脚本时不要用 input()。ArcGIS 工具箱的参数统一走:

fc = arcpy.GetParameterAsText(0)

这样参数从工具对话框传进来,和 Python 版本无关,也就没有这个坑。

(6) 3.x 独有、2.7 必报错的写法

写法起始版本py2.7 的报错
f"结果:{x}" 3.6 SyntaxError: invalid syntax
def f(a: int) -> str: 类型注解 3.0 / 3.5 SyntaxError
async def / await 3.5 SyntaxError
yield from 3.3 SyntaxError
nonlocal x 3.0 SyntaxError
{**d1, **d2} 字典解包合并 3.5 SyntaxError

f-string 是最高频的一个。 现在问 AI 要代码,十有八九给你 f-string。改法很简单:

# py3
arcpy.AddMessage(f"共处理 {n} 个图层")

# py2 / 通用
arcpy.AddMessage("共处理 {} 个图层".format(n))

.format() 从 Python 2.6 就有,两边通吃,是我写 arcpy 的默认选择。


3 中文编码:arcpy 最痛的一关

前面几处至少会报错,看得见。编码问题不一样,它时好时坏、换台电脑就变,最耗时间。

(1) 第一行必须写编码声明

py3 源码默认就是 UTF-8,写中文注释毫无压力。py2 默认是 ASCII,源码里只要出现一个中文字符——哪怕是注释——就报:

SyntaxError: Non-ASCII character '\\xe4' in file xxx.py on line 3,
but no encoding declared

所以 py2 脚本第一行(或第二行,如果第一行是 #!/usr/bin/env python)必须写:

# -*- coding: utf-8 -*-

用记事本或 Notepad++ 保存时,文件本身的编码也要选 UTF-8。 声明写 UTF-8、文件实际存成 ANSI,照样报错,而且报错信息更莫名其妙。

(2) py2 里 str 和 unicode 是两回事

这是整个编码问题的根。py3 只有一种字符串 str(就是文本),bytes 单独存在。py2 里:

类型是什么举例
str 字节串,存的是编码后的字节 "中文" 实际是 6 个字节
unicode 文本,存的是字符 u"中文" 是 2 个字符

# -*- coding: utf-8 -*-
s = "中文" # str,字节串,内容 = b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'
u = u"中文" # unicode,2 个字符

print type(s) # <type 'str'>
print type(u) # <type 'unicode'>
print len(s) # 6
print len(u) # 2

(3) 三个高频报错,一次说清

① UnicodeDecodeError: ‘ascii’ codec can’t decode byte

来源:把 str 和 unicode 拼在一起,py2 会自动尝试用 ASCII 去解码那个 str。

s = "中文" # str
u = u"abc" # unicode
print s + u # UnicodeDecodeError!

改成同类型再拼:

print s.decode('utf-8') + u # 都变成 unicode 再相加

② UnicodeEncodeError: ‘gbk’ codec can’t encode character

来源:把 unicode 输出到 GBK 环境(Windows 控制台默认 GBK),遇到 GBK 里没有的字符(比如某些少数民族文字、特殊符号)就崩。

print u"℃" # 控制台 GBK 编码,可能报错

③ IOError: [Errno 22] Invalid argument 或路径找不到

来源:arcpy 拿到的是 str 类型的中文路径,内部转码失败。py2 下 arcpy 对 unicode 路径的支持明显更好。

(4) arcpy 里的正确写法

统一原则:脚本内部的所有文本一律用 unicode,只在落盘输出时转成字节。

路径:

# -*- coding: utf-8 -*-
import arcpy, os

# 常量直接加 u 前缀,一步到位
workspace = u"D:\\\\数据\\\\成果"
arcpy.env.workspace = workspace

# 拼接出来的路径也要是 unicode
fc = os.path.join(workspace, u"行政区.shp")

消息输出:

# 不推荐:传入 str,换台电脑可能乱码
arcpy.AddMessage("处理完成:" + name)

# 推荐:全程 unicode
arcpy.AddMessage(u"处理完成:{}".format(name))

注意 .format() 里的变量也必须是 unicode,否则 py2 会尝试把 str 按 ASCII 解码。稳妥写法是准备一个转换函数:

def to_u(s):
"""py2 专用:任意对象转 unicode"""
if isinstance(s, unicode):
return s
if isinstance(s, str):
try:
return s.decode('utf-8')
except UnicodeDecodeError:
return s.decode('gbk')
return unicode(s)

arcpy.AddMessage(u"处理完成:{}".format(to_u(name)))

注意:这个函数只能在 py2 用,unicode 在 py3 里没有这个名字。写双版本兼容时用 sys.version_info 判断。

文件读写:py2 的 open() 读写的是字节,中文内容必须显式指定编码,用 codecs:

import codecs

# 写
with codecs.open(u"D:\\\\数据\\\\清单.txt", 'w', 'utf-8') as f:
f.write(u"图层名称,要素数\\n")
f.write(u"{},{}\\n".format(name, count))

# 读
with codecs.open(u"D:\\\\数据\\\\清单.txt", 'r', 'utf-8') as f:
for line in f:
print(line.strip())

不加 codecs 直接 open(…, 'w') 写中文,在 Windows 上会按 GBK 落盘,别人用 UTF-8 打开就是一堆乱码。

CSV 特别注意:用 csv 模块 + codecs.open 写出来的 UTF-8 CSV,用 Excel 双击打开会乱码(Excel 默认按 GBK 解析)。两个办法:一是文件开头写 BOM,二是改用 GBK。我通常用第一种:

with codecs.open(path, 'w', 'utf-8-sig') as f: # utf-8-sig 会写入 BOM
writer = csv.writer(f)

(5) 批量转换编码

如果手里一堆 txt 编码混乱,用 chardet 探测后统一转 UTF-8。这部分在《python学习—合并TXT文本文件》里有完整代码,这里不重复。

安装 chardet 到 arcpy 环境:

C:\\Python27\\ArcGIS10.8\\python.exe -m pip install chardet


4 字典与迭代

(1) keys / values / items 的返回值

d = {'a': 1, 'b': 2}

# py2:返回 list,可以用索引
d.keys() # ['a', 'b']
d.keys()[0] # 'a' 合法

在这里插入图片描述

# py3:返回视图对象,不支持索引
d.keys() # dict_keys(['a', 'b'])
d.keys()[0] # TypeError: 'dict_keys' object is not subscriptable

py3 想取第一个键,要转成 list:

list(d.keys())[0]

arcpy 场景:属性表字段名和别名的映射字典,按顺序取字段名时容易踩。建议统一写 list(d.keys()),两边都能跑。

(2) iteritems 在 py3 消失

# py2
for k, v in d.iteritems():
print k, v

# py3
for k, v in d.items():
print(k, v)

通用写法用 d.items()。 在 py2 里 items() 会一次性建 list,字段字典最多几十项,完全无所谓。

(3) has_key 被移除

# py2
if d.has_key('a'):
pass

# py3 / 通用
if 'a' in d:
pass

(4) 排序行为

py2 里 sorted(d.items()) 遇到键类型不一致(比如既有 int 又有 str)不会报错,只是按类型名排;py3 直接抛 TypeError。写字典排序时保证键类型统一即可。


5 库与包管理

(1) pip 到底装到哪去了

电脑上装了 3.13 之后,命令行敲 pip 很可能指向 3.13 的 pip:

pip install docx

结果是装进了 3.13 的 site-packages,arcpy 的 Python 2.7 里 import docx 依旧报错。永远用 -m pip 明确解释器:

C:\\Python27\\ArcGIS10.8\\python.exe -m pip install python-docx

写全路径,不给歧义留空间。

(2) 哪些包能装、哪些不能

包能否装到 arcpy 环境说明
python-docx 能 生成 word 报告
openpyxl 能 读写 xlsx,比 xlrd/xlwt 好用
chardet 能 编码探测
pandas 谨慎 新版已不支持 py2,必须锁 pandas==0.24.2
numpy 不建议动 arcpy 自己依赖特定版本,升级会连带崩掉
gdal/geopandas 不建议 与 arcpy 的几何对象不通用,两套并存的坑很深

铁律:不要升级 arcpy 环境里已存在的包。 只装新的、arcpy 用不到的小包。装出问题只能重装 ArcGIS,代价太大。

(3) Python 2.7 的包生态已经停更

2020 年 1 月 1 日 py2 停止维护。现在 pip install 报 Could not find a version that satisfies the requirement 是常态。解决办法:

  • 优先找老版本:pip install 包名==1.2.3
  • 有些包要指定源:-i https://pypi.tuna.tsinghua.edu.cn/simple
  • 实在装不上,就回到标准库——os、csv、shutil、codecs、zipfile 这几个模块能覆盖 90% 的办公自动化需求

6 arcpy 本身的变化:mapping 变 mp

语法差异之外,两代 arcpy 的 API 也不一样。最明显的是地图文档模块:

功能Desktop 10.x(py2)Pro(py3)
地图文档对象 arcpy.mapping.MapDocument arcpy.mp.ArcGISProject
取当前文档 MapDocument("CURRENT") ArcGISProject("CURRENT")
列出数据框 arcpy.mapping.ListDataFrames(mxd) project.listMaps()
列出图层 arcpy.mapping.ListLayers(mxd, "", df) map.listLayers()
导出 PDF arcpy.mapping.ExportToPDF layout.exportToPDF()
修改图层符号 arcpy.mapping.UpdateLayer layer.symbology
文本元素 arcpy.mapping.ListLayoutElements layout.listElements("TEXT_ELEMENT")

# py2 + Desktop
import arcpy
mxd = arcpy.mapping.MapDocument(u"D:\\\\工程\\\\成果.mxd")
df = arcpy.mapping.ListDataFrames(mxd)[0]
for lyr in arcpy.mapping.ListLayers(mxd, "", df):
print lyr.name
mxd.save()
del mxd

# py3 + Pro
import arcpy
aprx = arcpy.mp.ArcGISProject(r"D:\\工程\\成果.aprx")
m = aprx.listMaps()[0]
for lyr in m.listLayers():
print(lyr.name)
aprx.save()

这部分不能只改语法,逻辑要重写。 好在基础工具层完全一致——arcpy.Project_management、arcpy.Clip_analysis、arcpy.da.SearchCursor、arcpy.GetParameterAsText 这些在两边写法一模一样,只是 py3 里 print 要加括号。

所以现有的 arcpy 代码迁移到 Pro,工作量主要集中在 mapping 相关的部分。


7 速查表

写代码时照着对,左边是 py2(Desktop),右边是 py3(Pro / 通用)。

场景Python 2.7Python 3.x通用写法
输出 print x print(x) print(x)
多参数输出 print "a", x print("a", x) print("a", x)
格式化 "%s" % x f"{x}" "{}".format(x)
真除法 float(a)/b a/b float(a)/b
地板除 a/b a//b a//b
异常 except E, e: except E as e: except E as e:
循环 xrange(n) range(n) range(n)
输入 raw_input() input() arcpy.GetParameterAsText()
字典遍历 d.iteritems() d.items() d.items()
键判断 d.has_key(k) k in d k in d
键列表 d.keys() list(d.keys()) list(d.keys())
字符串类型 str = 字节,unicode = 文本 str = 文本,bytes = 字节 全部加 u"" 前缀
源文件声明 # -*- coding: utf-8 -*- 必须 可省略 写上
文本文件读写 codecs.open(p,'w','utf-8') open(p,'w',encoding='utf-8') 两边写各自的,别硬求统一
整数类型 int / long 并存 只有 int 避免 123L 字面量
地图文档 arcpy.mapping.MapDocument arcpy.mp.ArcGISProject 无法通用,需重写

8 迁移自检清单

拿到一段来路不明的代码,或者要把旧脚本换个环境跑,按顺序过一遍:

  • 1 先确认解释器版本:import sys; print(sys.version)
  • 2 有没有 print "xxx" 这种不带括号的语句?
  • 3 有没有 except X, e 这种逗号捕获?
  • 4 有没有 xrange、has_key、iteritems?
  • 5 有没有 f-string(f"…")或类型注解?py2 要全部改掉
  • 6 所有除法:两个整数相除的地方,是不是漏了 .0 或 float()?
  • 7 源码第一行有没有 # -*- coding: utf-8 -*-?文件本身是不是真存成了 UTF-8?
  • 8 所有路径字符串加 u 前缀了没有?
  • 9 有没有 "中文" + u"中文" 这种 str 与 unicode 混拼?
  • 10 用到的第三方库,在 py2 下有对应版本吗?python -m pip list 看一下
  • 11 涉及 arcpy.mapping 的,目标环境是 Desktop 还是 Pro?

十一条全过一遍,py2 / py3 之间的坑基本就清完了。


后记

Python 2 和 3 的差异,说到底是"历史包袱"和"生态现状"在打架。ArcGIS 桌面版锁死 2.7 是既定事实,短期内不会变;而外部的代码示例、教程、AI 生成的内容,默认都是 3.x。

我的建议是:写 arcpy 时代码直接按 2.7 写,但心里装着 3.x 的写法。凡是能用通用写法的地方就用通用写法(.format()、except as、in 判断键),这样将来真要迁到 Pro,改动量能少一大半。

真正无法通用、必须重写的,只有 arcpy.mapping 那一块。所以如果你的脚本里大量用到了 arcpy.mapping,从一开始就把地图文档操作和数据操作拆成两个模块,迁移时只动前半截。

如果觉得文章不错,请多多点赞添加关注,您的支持就是我的动力。

2323

赞(0)
未经允许不得转载:171主机测评 » python学习-Python2.7与3.x差异-arcpy场景
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址