Python 3和Python 2的主要差异
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, ".recourseCheckTopology.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, ".recourseCheckTopology.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]传入该文件。