Python 3打破了对Python 2的向后兼容。但它并不是完全重新设计的。而且,也并不是说2.x版本的Python模块在Python 3下都无法运行。代码可以完全跨版本兼容,无需其他工具或技术在两大版本上都可以运行,但一般只有简单应用才能做到这一点。
Python 3引入的重要差异一般可分为以下几个方面:
• 语法变化,删除/修改了一些语法元素,并添加了一些新的语法元素。
• 标准库中的变化。
• 数据类型与集合的变化。
所以,有时由于一些客观原因,不得不在python3环境中引入Python 2文件,并且这两者没法完全兼容,就需要进行另外的一些设计,下文中是具体实现的一种简单的方法。
在我的项目中,通过pyhton3环境设计了一个属性检查的文件;但我的图形(拓扑)检查是基于ArcGIS的站点包ArcPy设计的,这两者显然没法都在pyhton3环境下运行。(ArcPy只支持python2.7)
我需要在python3环境中调用我的python2.7设计的检查文件。
使用python标准库的subprocess(子进程管理)模块;subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。这是python官方文档的链接
ps:当然也可以使用 multiprocessing(基于进程的并行)模块来实现同样的功能。
script = [self.obj.exename, ".\\recourse\\CheckTopology.py", self.obj.filename]
proc = subprocess.Popen(script, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
proc.communicate()
以上内容,使用Popen 构造函数开启了一个子进程命名为proc,Popen 类的实例拥有以下方法:
Popen.poll()
检查子进程是否已被终止。设置并返回 returncode 属性。否则返回 None。
Popen.wait(timeout=None)
等待子进程被终止。设置并返回 returncode 属性。如果进程在 timeout 秒后未中断,抛出一个 TimeoutExpired 异常,可以安全地捕获此异常并重新等待。
注意:当 stdout=PIPE 或者 stderr=PIPE 并且子进程产生了足以阻塞 OS 管道缓冲区接收更多数据的输出到管道时,将会发生死锁。当使用管道时用 Popen.communicate() 来规避它。
Popen.communicate(input=None, timeout=None)
与进程交互:将数据发送到 stdin。 从 stdout 和 stderr 读取数据,直到抵达文件结尾。 等待进程终止并设置 returncode 属性。
Popen.terminate()
停止子进程。
Popen.kill()
杀死子进程。
这里选择Popen.communicate,原因是要获取子进程的输出信息,以监测子进程的运行情况;上文中的script是开启子进程的信息列表,用法例如[python2.exe, my.py, argv]从左向右依次是开启子进程的执行程序、py文件、py文件的参数;stdout=subprocess.PIPE是开启管道获取标准输出,stderr=subprocess.STDOUT是开启管道获取错误输出。
communicate() 返回一个 (stdout_data, stderr_data) 元组。如果文件以文本模式打开则为字符串;否则字节。
outs, errs = proc.communicate()
outs, errs 就是子进程的输出信息。
以下内容是我的项目最终文件的一部分内容,因为需要在GUI界面上展示子进程的输出信息,所以还开启了另外的线程循环以信号的形式发送该进程的输出信息。
def Threading_topo(self): # CheckTopology子进程输出读取方法
script = [self.obj.exename, ".\\recourse\\CheckTopology.py", self.obj.filename]
def output_reader(proc, outq):
for line in iter(proc.stdout.readline, b''):
outq.put(line.decode('gbk'))
def main(script1):
# Note the -u here: essential for not buffering the stdout of the subprocess
proc = subprocess.Popen(script1, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
outq = queue.Queue()
t = threading.Thread(target=output_reader, args=(proc, outq))
t.start()
try:
for i in range(500):
if proc.poll() == 0:
self.sinOut.emit('......拓扑检查完成。\n')
proc.terminate()
break
print(i)
try:
line = outq.get(block=False)
# 这里是我最终的输出信息
self.sinOut.emit(line)
except queue.Empty:
pass
time.sleep(1)
finally:
proc.terminate()
try:
proc.wait(timeout=1)
print('== subprocess exited with rc =', proc.returncode)
except subprocess.TimeoutExpired:
print('subprocess did not terminate in time')
t.join()
main(script)
if __name__ == "__main__":
in_path = sys.argv[1]
my_Topology = CheckTopology(in_path)
my_Topology.CreateNewGdb()
my_Topology.CreateFeatureDataset()
my_Topology.CreateTopology()
my_Topology.AddRules_ValidateTopology()
这是我的python2文件的结尾,确保该python2文件独立运行正常的情况下,将需要的参数以上文的形式传入该文件内;这里我的参数是in_path,在开启子进程的script列表中根据上下文获取的外部参数通过sys.argv[1]传入该文件。