python中两个复制函数的比较

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

1 需求确定

最近在完成一个需求:需要实现一个拷贝文件夹的功能。
比如原来的目录结构是:
/media/admin/Data/PatientID
现在需要在Data和PatientID之间增加一级目录为CT,因此修改后的目录结构是:
/media/admin/Data/CT/PatientID

对于这个需求,基本的思路是:
(1)根据PatientID的当前路径,获取它的上一级目录:/media/admin/Data
(2)在上一级目录创建一个CT的文件夹,之后目录为:/media/admin/Data/CT
(3)将原来PatientID的文件夹拷贝到新创建的那个目录下,这时在/media/admin/Data下面会有两个文件夹:
/media/admin/Data/CT
/media/admin/Data/PatientID
(4)删除原来的那个目录/media/admin/Data/PatientID。
此时便完成了一个在原有目录结构内增加一级目录的需求。
备注:这里要说明在执行第3步操作时,为什么不是直接把那个PatientID的文件夹移动(剪切)到新创建的CT目录下,而是要先复制,然后再删除原来的文件夹呢。
这是由需求决定的。因为根据程序的配置,除了在/media/admin/Data下会创建CT这一级目录,还有可能会创建DR,MRI等文件夹,然后PatientID的文件夹需要在新创建的这三个目录下都复制一份。因此,只有等PatientID在新建的那三个目录复制结束后,
也就是:
/media/admin/Data/CT/PatientID
/media/admin/Data/DR/PatientID
/media/admin/Data/MRI/PatientID
才能删除原始的文件夹/media/admin/Data/PatientID。

2 复制函数确定

需求确定后,最主要的那个复制操作,经过查找后,发现Python中有两个库可以完成这个复制操作。分别是:
distutils.dir_util.copy_tree(src, dest)
shutil.copytree(src, dest)

当时看这两个函数的区别是,
shutil.copytree在进行复制操作时,如果dest存在时,调用该函数会报错。也就是说,必须确保传入的目标路径之前是不存在的。而第一个复制函数则没有这个限制。

由于在实际测试时,有可能会存在复制前,dest目录已经存在的情况。因此,为了避免这种情况,决定采用distutils.dir_util.copy_tree()这个复制函数。

3 问题出现

后来在实际测试时,在调用这个函数的时候,经常莫名地会报:
DistutilsFileError: file xxx doest not exist的错误。
但是我在本机进行测试的时候,总是无法重现这个错误。正当百思不得其解的时候,在网上看到一个说明:
https://stackoverflow.com/questions/9160227/dir-util-copy-tree-fails-after-shutil-rmtree
看问题的标题是:dir_util.copy_tree fails after shutil.rmtree.
意思是,这个拷贝函数在调用删除文件夹的操作后调用时会报错。
下面的解答是:Seems to be a bug in distutils. If you copy folder, then remove it, then copy again it will fail, because it caches all the created dirs.
就是说,当首先拷贝一个文件夹,然后删除它,然后再次拷贝同一个文件夹的时候,会失败。因为它缓存了之前创建的所有文件夹。

这时,我再回头看之前测试提交给我的系统运行日志,我分明看到了在调用拷贝函数时,我的确是有进行过删除文件夹的操作。也就是说,我恰好撞到了这个拷贝函数自己的bug里了。

问题的原因找到后,我按照回答者给的代码试了一下,
distutils.dir_util._path_created = {}
distutils.dir_util.copy_tree(src, dst)
还是无效。

4. 问题解决

无奈之下,我只能决定弃用这个函数,转而用另一个复制函数了shutil.copytree(src, dest)。

当然这个函数的缺陷之前已经了解了。因此为了避免这个拷贝函数报错,我每次在调用shutil.copytree前,都需要首先判断一下dest的目录是否存在。如果存在,则需要先删除该目录,然后再执行拷贝操作。

import os
import shutil

def copy_func(src, dest):
	if os.path.exists(dest):
		shutil.rmtree(dest)
	shutil.copytree(src, dest)

5. 反思

看似是一个很小的问题。但是当时解决时,还是费了很大的劲和功夫的。而最耗费时间的,实际是在重现这个bug和与测试人员交流的时间成本。

当测试人员提到程序运行时会报这个错误时。我下意识地总感觉,一定是测试在测试前,没有把该清空的目录都清掉,或者是哪里操作不对,而导致报这个错误。因为我自己在写程序和测试时,就从来没有遇到过这个错误哦。来来回回交流了几次,bug每次都会重现,身上的压力也比较大。

后来,我问测试拿到他测试时的测试数据和配置文件,然后再在我本机测试的时候,这个bug终于重现了。当时还是比较兴奋的。因为只有在本机重现了,才可以逐行去debug代码,离解决问题也就不远了。果然,当确保按照测试给的数据和配置文件运行程序时,每次都必然会重现这个bug时,我就比较有方向性了。通过细致地分析日志,才找到了那个复制函数的bug。最终解决了这个bug。

因此,以后当再次出现这类问题时,我想最快的解决方法,一定是要拿原始的数据去测试,而不是随便用自己开发时用的数据去企图重现bug。因为,有的bug的确是跟所使用的测试数据有很大和直接的关系的。

发布了188 篇原创文章 · 获赞 416 · 访问量 131万+

猜你喜欢

转载自blog.csdn.net/inter_peng/article/details/85172090
今日推荐