Python2.7调用Python3.x函数的跨版本交互问题求助
我之前刚好碰到过类似的Python2与Python3跨环境调用的痛点,结合你ArcGIS+TensorFlow的场景,给你几个可行的解决方案,完全能跳过磁盘读写的繁琐步骤:
核心思路:跨进程通信(IPC)
因为Python2.7和Python3+TensorFlow的环境完全隔离,没法直接共享内存或导入对方的库,所以核心就是通过进程间通信让两个环境传递numpy数组,实现实时调用。
方案1:本地Socket通信(轻量易实现,无额外依赖)
用原生Socket做本地请求-响应,把numpy数组序列化成字节流传递,不用装第三方库,适合快速验证。
Python3+TensorFlow 服务端(提前启动)
import socket import numpy as np import pickle import tensorflow as tf # 加载你的预训练模型 model = tf.keras.models.load_model("your_tf_model.h5") # 配置本地Socket HOST = "127.0.0.1" PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() print(f"TensorFlow服务端启动,监听 {HOST}:{PORT}") conn, addr = s.accept() with conn: print(f"ArcGIS客户端已连接:{addr}") while True: # 1. 接收数组元数据(形状、 dtype) meta_data = conn.recv(1024) if not meta_data: break shape, dtype = pickle.loads(meta_data) # 2. 计算并接收数组字节数据 data_size = np.prod(shape) * np.dtype(dtype).itemsize data = b"" while len(data) < data_size: chunk = conn.recv(data_size - len(data)) if not chunk: break data += chunk # 3. 还原numpy数组并预测 img_array = np.frombuffer(data, dtype=dtype).reshape(shape) pred_result = model.predict(img_array[np.newaxis, ...]) # 补batch维度 # 4. 返回预测结果 result_data = pickle.dumps(pred_result) conn.sendall(result_data)
Python2.7 ArcGIS 客户端(插件内调用)
import socket import numpy as np import pickle def get_tf_prediction(img_array): HOST = "127.0.0.1" PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) # 发送数组元数据 meta_data = pickle.dumps((img_array.shape, img_array.dtype)) s.sendall(meta_data) # 发送数组字节流 s.sendall(img_array.tobytes()) # 接收预测结果 result_data = b"" while True: chunk = s.recv(4096) if not chunk: break result_data += chunk return pickle.loads(result_data) # 在插件中获取地图图像数组后直接调用 # img_array = 你的ArcGIS地图图像numpy数组 # prediction = get_tf_prediction(img_array) # 直接用prediction做后续分析
方案2:ZeroMQ通信(更稳定,适合复杂场景)
ZeroMQ是专门做高性能IPC的库,封装了Socket的底层细节,支持多种通信模式(REQ-REP刚好适合你的请求-响应场景),比原生Socket更可靠。
先装依赖
- Python3环境:
pip install pyzmq - Python2.7环境:
pip install pyzmq==19.0.0(这个版本兼容Python2.7)
Python3+TensorFlow 服务端
import zmq import numpy as np import tensorflow as tf model = tf.keras.models.load_model("your_tf_model.h5") context = zmq.Context() socket = context.socket(zmq.REP) socket.bind("tcp://*:5555") print("ZeroMQ服务端启动,监听端口5555") while True: # 接收元数据和数组 meta = socket.recv_pyobj() data = socket.recv() img_array = np.frombuffer(data, dtype=meta["dtype"]).reshape(meta["shape"]) pred_result = model.predict(img_array[np.newaxis, ...]) # 返回结果 socket.send_pyobj(pred_result)
Python2.7 ArcGIS 客户端
import zmq import numpy as np def get_tf_prediction(img_array): context = zmq.Context() socket = context.socket(zmq.REQ) socket.connect("tcp://localhost:5555") # 发送元数据和数组字节流 socket.send_pyobj({"shape": img_array.shape, "dtype": img_array.dtype.str}) socket.send(img_array.tobytes()) # 接收预测结果 return socket.recv_pyobj()
方案3:共享内存(超大数组首选,低延迟)
如果你的地图图像数组特别大,Socket/ZMQ的字节流传递会有开销,用共享内存直接在内存中共享数据,速度最快(几乎无拷贝)。
Python3+TensorFlow 服务端
import mmap import numpy as np import tensorflow as tf import os # 提前约定共享内存参数(根据你的数组大小调整) SHARED_MEM_NAME = "ArcGIS_TF_Shared" ARRAY_SHAPE = (512, 512, 3) # 你的图像数组形状 DTYPE = np.float32 DATA_SIZE = np.prod(ARRAY_SHAPE) * np.dtype(DTYPE).itemsize # 预测结果的形状(提前约定) PRED_SHAPE = (1, 10) PRED_DATA_SIZE = np.prod(PRED_SHAPE) * np.dtype(np.float32).itemsize model = tf.keras.models.load_model("your_tf_model.h5") # 创建共享内存(区分Windows/Linux) if os.name == "nt": shm = mmap.mmap(-1, DATA_SIZE + 4 + PRED_DATA_SIZE, tagname=SHARED_MEM_NAME, access=mmap.ACCESS_ALL) else: shm_fd = os.open(f"/dev/shm/{SHARED_MEM_NAME}", os.O_CREAT | os.O_RDWR) os.ftruncate(shm_fd, DATA_SIZE + 4 + PRED_DATA_SIZE) shm = mmap.mmap(shm_fd, DATA_SIZE + 4 + PRED_DATA_SIZE, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE) print("共享内存服务端启动,等待数据...") while True: # 读取标志位,判断是否有新数据 shm.seek(0) flag = shm.read(4) if flag == b"READY": # 读取数组数据 shm.seek(4) data = shm.read(DATA_SIZE) img_array = np.frombuffer(data, dtype=DTYPE).reshape(ARRAY_SHAPE) # 预测并写回结果 pred_result = model.predict(img_array[np.newaxis, ...]) shm.seek(4 + DATA_SIZE) shm.write(pred_result.tobytes()) # 更新标志位表示结果就绪 shm.seek(0) shm.write(b"DONE")
Python2.7 ArcGIS 客户端
import mmap import numpy as np import os SHARED_MEM_NAME = "ArcGIS_TF_Shared" ARRAY_SHAPE = (512, 512, 3) DTYPE = np.float32 DATA_SIZE = np.prod(ARRAY_SHAPE) * np.dtype(DTYPE).itemsize PRED_SHAPE = (1, 10) PRED_DATA_SIZE = np.prod(PRED_SHAPE) * np.dtype(np.float32).itemsize def get_tf_prediction(img_array): # 连接共享内存 if os.name == "nt": shm = mmap.mmap(-1, DATA_SIZE + 4 + PRED_DATA_SIZE, tagname=SHARED_MEM_NAME, access=mmap.ACCESS_ALL) else: shm_fd = os.open(f"/dev/shm/{SHARED_MEM_NAME}", os.O_RDWR) shm = mmap.mmap(shm_fd, DATA_SIZE + 4 + PRED_DATA_SIZE, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE) # 写入数组数据并更新标志位 shm.seek(4) shm.write(img_array.tobytes()) shm.seek(0) shm.write(b"READY") # 等待结果 while True: shm.seek(0) flag = shm.read(4) if flag == b"DONE": # 读取预测结果 shm.seek(4 + DATA_SIZE) pred_data = shm.read(PRED_DATA_SIZE) pred_result = np.frombuffer(pred_data, dtype=np.float32).reshape(PRED_SHAPE) # 重置标志位 shm.seek(0) shm.write(b"WAIT") return pred_result
关键注意事项
- 序列化兼容:Python2的
pickle和Python3的pickle处理numpy数组时,建议用pickle.load(..., encoding='latin1'),或者用dill库替代pickle,兼容性更好。 - 自动启动服务端:可以在ArcGIS插件初始化时,用
subprocess启动Python3的服务端进程:# Python2.7 代码 import subprocess import sys # 替换成你的Python3路径和服务端脚本路径 subprocess.Popen([r"C:\Python36\python.exe", "tf_server.py"], shell=True) - 字节序统一:确保numpy数组的字节序(endian)一致,默认用系统原生字节序即可,跨平台时可手动指定(比如
np.dtype('>f4'))。
方案选择建议
- 快速验证选Socket通信,无额外依赖;
- 追求稳定性和扩展性选ZeroMQ;
- 处理超大图像数组、要求低延迟选共享内存。
内容的提问来源于stack exchange,提问作者Adam B
相关产品推荐
相关产品推荐

