近期工作中应用Python的一些经验总结

本文由Markdown语法编辑器编辑完成。

1. Python

在来新公司前,也间断地接触过Python编程,比如医学影像处理的图像库VTK就已经有Python的实现;Paraview也可以开启Python的监测,将用户在界面上的操作录制成Python脚本,供之后再进行重复调用。在人工智能领域,Python更是应用广泛,各种深度学习的框架,都是用Python语言撰写。

进入了新公司后,除了前端是用JS实现外,其余模块的后端语言都是用Python实现。因此,我在近一个月的工作中,也是在边学边用Python。虽然说编程语言的思想都基本相同,但有时候还是会由于一些编程思想的疏忽,引起一些问题。这里记录一下最近这段时间,在代码review的过程中,发现的一些编码问题。对于自己在软件架构,编程思想等方面的提高做一些必要的积累。

2. Python的编码思想和规范

(1) Python的异常处理
Python的异常处理类似于Java,也是通过try...catch的语法块来进行异常捕捉。一般在工程项目中,需要在有异常或错误的地方记录相应级别的日志。那么什么时候去记录日志,什么时候去抛异常,是抛程序自己的异常,还是为了统一格式而抛自己提前定义好的异常,这些都是需要提前定义好编码规则的。

先说一个原则就是:在函数的调用关系中,当最底层的函数运行有异常时,可以记录异常的日志,抛异常时需要抛原始的异常,而不应该抛自己定义的异常。 因为如果抛自己定义的异常,这样在调用该函数时,虽然依然可以捕捉到自己定义的异常,但是会丢失掉原来异常的链,也就是无法追踪到最原始的引起异常的地方。

(2)慎用成员变量
Python虽然是非编译性语言,但是它也具有面向对象的特性,因此可以用Python创建类,类里可以定义属性和方法。但是,这里一定要慎用属性(成员变量)。因为如果将一个属性定义为成员变量,那么这个属性值会随着这个类的对象一直存在。这样,如果在只创建一个类的对象,而多次用不同的参数来调用这个类的方法时,有可能会因为这个类的成员变量值,由于还保存着上一次调用时的值,而直接作用于下一次的函数调用,从而产生无法预知的错误。

之前,我无意识地将一个属性设置为成员变量后,在调用类的方法时,不管外边传入的参数是什么值,输出始终都是固定不变的。当时百思不得其解,后来通过debug代码,才发现原来是由于将一个变量设置为了成员变量,而且这个成员变量是一个list. 所以每调用一次函数,都相当于往这个list类型的成员变量里面append一个元素,而最后输出的值是这个list的第一个元素。因此不管用什么参数来调用这个函数,始终输出的都是第一次调用时的那个返回值(list的第一个元素)。根本原因,就是每次调用完这个函数后,没有将这个成员变量清空而导致的。找到问题后,将这个成员变量修改为局部变量。这样每次调用完函数后,这个局部变量便会被销毁。之前遇到的问题也就迎刃而解了。

(3)Python多线程引起的问题
之前在实现一个基于python的第三方库 filesystem的ftp连接,下载文件和关闭ftp连接时,由于没有意识到多线程的问题,而发生了意想不到的错误。
在自己本地开发测试时,由于没有用多线程来测试,因此只是按照只有一个线程的思维,首先根据ftp的连接参数创建ftp连接,然后再根据传入的根目录下载文件,下载完毕后关闭ftp连接。看起来这个流程是没有问题的。但是,当真正在多线程的环境下开始测试的时候,程序便在“关闭ftp连接时报错。”出错的原因是说,不能关闭未连接的ftp。
当时一直没有想明白是什么原因,既然代码已经走到了关闭连接的地方,那么必然是之前先连接了ftp,并且已经完成了文件下载的呀,怎么会提示要关闭的ftp是未连接状态的呢。
后来,在同事的提醒下,知道这个是多线程的。因此,其实是同时有很多的线程在运行,而且由于整个多线程,是共用着一个ftp连接。因此,完全有可能一个线程刚把ftp连接起来,当这个线程完成了下载文件的操作后,准备去关闭这个ftp连接时,却发现这个ftp连接已经被先于它下载完文件的线程给关闭了ftp连接。因此,才会发生前文中的异常。
了解了问题的根源后,便可以对症下药了。那就是在每次关闭线程时,不能像原来那样,直接把成员变量的ftp(当时是将ftp的连接设置为成员变量了)连接给关闭。这个ftp连接,当时除了基本的ip,username, password外,还有一个root_path。而每一个root_path其实是不同的。因此,本质上这个ftp的连接,其实如果加上后面的root_path是不同的。但是如果只以前面的三个参数连接时是相同的。
因此,最终的解决思路是,不降ftp连接作为成员变量,而是每次将在下载文件前建立的ftp连接对象(由: ip, username, password和root_path构成的连接串)返回,关闭ftp连接时,关闭返回来的这个ftp连接。这样,就能保证每一个线程关闭的ftp连接都是属于这个线程特有的ftp连接,而不会去关闭大家公用的那个ftp主连接(由: ip, username, password)。这样多线程之间便不会相互干扰了。

3. 参考链接

  1. 《程序设计思想与方法》, 陆朝俊编著, 上海交通大学通识课“计算思维课程”教材
    https://wizardforcel.gitbooks.io/sjtu-cs902-courseware/content/index.html
    未完待续......
posted @ 2018-10-17 09:22  蓬stephen蓬  阅读(1481)  评论(0编辑  收藏  举报