最近在用 Python 写一个一键替换文件的脚本文件, 大概的功能是, 向程序传递一个本地或 SFTP 目录的参数, 程序可以把指定目录所有文件替换到特定应用程序的对应目录. 程序提供了如下 2 种命令行调用:
Usage: demo.py [sourcedir]
Usage: demo.py [sourcedir] bydir
第一种调用的实际操作是: 读取特定应用程序目录所有文件, 并获取全路径作为一个集合, 再把参数文件夹中文件按文件名与集合中文件进行匹配, 如果匹配上则执行替换操作.
第二种调用的实际操作是: 按参数文件夹的目录存放的路径, 完整替换到应用程序的对应目录.
下面是最初的代码实现:
- # 执行本地文件替换的具体操作
- def ReplaceLocalFiles(filepath, context, filecontext, softpath, bydir):
- if (":" not in filepath) or (not os.path.isdir(filepath)):
- printandwritelog(u"目标路径有误, 请确认是目录后再重试")
- return "error"
- fileList = os.walk(filepath)
- for root, dirs, files in fileList:
- for file in files:
- if bydir:# 如果按目录进行替换的话走下面这个逻辑分支
- filefullpath = os.path.join(root, file)
- targetfullpath = filefullpath.replace(filepath, softpath)
- shutil.copy2(filefullpath, targetfullpath)
- printandwritelog(u"文件 %s 拷贝到 %s 成功" % (filefullpath, targetfullpath))
- else:# 如果自行查找文件路径进行替换的话先走下这个逻辑分支
- filecounts = checkcount(file, filecontext)
- if (0 == filecounts):
- printandwritelog(u"没有找到文件 %s 的路径, 请使用指定路径方式进行替换" % file)
- continue
- elif (1 < filecounts):
- printandwritelog(u"文件 %s 有 %s 个路径, 请使用指定路径方式进行替换" % (file , filecounts))
- continue
- elif (1 == filecounts):
- for line in context.split("\n"):
- filename = line.split("\\")[-1]
- if file == filename:
- os.rename(line , line + str(random.randint(0, 100)))
- shutil.copy2(os.path.join(root, file), line)
- printandwritelog(u"文件 %s 拷贝到 %s 成功" % (os.path.join(root, file), line))
- else:
- printandwritelog(u"替换文件个数有误 %s" % file)
- # 判断如果是本地文件则直接调用替换函数, 如果是网络路径, 则先下载文件再替换
- def RelpaceFiles(filepath, context, filecontext, softpath, bydir):
- if ":" in filepath:
- printandwritelog(u"提供的本地路径, 走本地路径文件替换流程")
- ReplaceLocalFiles(filepath, context, filecontext, softpath, bydir)
- else:
- printandwritelog(u"提供的 FTP 路径, 先下载文件到本地后再执行替换流程")
- sourceFileDir = cur_file_dir() + r"\testdir"
- if os.path.isdir(sourceFileDir):
- shutil.rmtree(sourceFileDir)
- obj = netutilex.SFTP("192.168.1.100", "test", "testpwd")
- obj.syncSftpDir(filepath, sourceFileDir)
- obj.close()
- ReplaceLocalFiles(sourceFileDir, context, filecontext, softpath, bydir)
- # 先处理替换前的前置操作, 环境准备好之后执行替换操作
- def ReplaceAllFiles(filepath, bydir):
- softpath = checkinst()
- if ("notinst" == softpath):
- printandwritelog(u"没有检测到卫士安装目录, 请确认后重试")
- return "error"
- else:
- context, filecontext = getallfiles(softpath)
- RelpaceFiles(filepath, context, filecontext, softpath, bydir)
先简单说明下各函数的功能:
ReplaceLocalFiles: 主要功能函数, 实现具体的替换操作;
RelpaceFiles: 根据传入参数判断是否是网络路径, 如果是则先把文件下载到本地, 然后调用 ReplaceLocalFiles 执行替换操作;
ReplaceAllFiles: 做了一些环境准备的事情, 然后调用实际的功能函数 RelpaceFiles 进行干活;
printandwritelog: 记录日志并输出;
checkinst: 检查目标程序是否安装, 如果安装则返回安装路径;
getallfiles: 获取目标应用程序的文件全路径集合;
checkcount: 获取指定文件名在目标应用程序文件集合中出现的次数
netutilex: 一个独立的操作 SFTP 的库文件.
从目前的代码中能发现至少有 2 个地方可以优化:
函数之间需要传递的参数太多了, 可以看看是否全部必要, 考虑下如何精简;
部分业务逻辑太细化, 有重复的代码实现, 导致实现看起来比较臃肿.
对于第 1 点, 优化的思路是: 对于非所有函数都必须调用的参数, 尽可能的固化到实际使用的函数中, 避免各函数仅仅做了传递员的工作.
对于第 2 点, 优化的思路是: 合并同类项, 对于重复代码的部分, 尽可能提取到共用逻辑中实现.
下面是优化后的代码:
- # 执行本地文件替换的具体操作
- def ReplaceLocalFiles(filepath, bydir):
- if (":" not in filepath) or (not os.path.isdir(filepath)):
- printandwritelog(u"目标路径有误, 请确认是合法目录后重试")
- return "error"
- softpath = checkinst()
- if ("notinst" == softpath):
- printandwritelog(u"没有获取到目标软件安装目录, 请确认后重试")
- return "error"
- context, filecontext = getallfiles(softpath)
- fileList = os.walk(filepath)
- for root, dirs, files in fileList:
- for file in files:
- filefullpath = os.path.join(root, file)
- targetfullpath = filefullpath.replace(filepath, softpath)
- if not bydir:# 如果自行查找文件路径进行替换的话先走下这个逻辑分支
- filecounts = checkcount(file, filecontext)
- if (0 == filecounts):
- printandwritelog(u"没有找到文件 %s 的路径, 请使用指定路径方式进行替换" % file)
- continue
- elif (1 < filecounts):
- printandwritelog(u"文件 %s 有 %s 个路径, 请使用指定路径方式进行替换" % (file , filecounts))
- continue
- elif (1 == filecounts):
- for line in context.split("\n"):
- filename = line.split("\\")[-1]
- if file == filename:
- targetfullpath = line
- else:
- printandwritelog(u"替换文件个数有误 %s" % file)
- if os.path.isfile(targetfullpath):
- randomend = random.randint(0, 100)
- os.rename(targetfullpath , targetfullpath + str(randomend))
- shutil.copy2(filefullpath, targetfullpath)
- printandwritelog(u"文件 %s 拷贝到 %s 成功" % (filefullpath, targetfullpath))
- # 先处理替换前的前置操作, 环境准备好之后执行替换操作
- def ReplaceAllFiles(filepath, bydir):
- sourceFileDir = filepath
- if ":" in filepath:
- printandwritelog(u"提供的本地路径, 走本地路径文件替换流程")
- else:
- printandwritelog(u"提供的 FTP 路径, 先下载文件到本地后再执行替换流程")
- sourceFileDir = cur_file_dir() + r"\testdir"
- if os.path.isdir(sourceFileDir):
- shutil.rmtree(sourceFileDir)
- obj = netutilex.SFTP("192.168.1.100", "test", "testpwd")
- obj.syncSftpDir(filepath, sourceFileDir)
- obj.close()
- ReplaceLocalFiles(sourceFileDir, bydir)
具体的优化操作有:
把函数 checkinst 和 getallfiles 的调用实现放到了其返回值使用者 ReplaceLocalFiles 的函数体内, 减少了 2 个参数的多次传递;
把函数 ReplaceLocalFiles 中具体的 copy2 操作进行了提取, 因为 bydir 和非 bydir 最终都会走到这个操作;
把函数 ReplaceFiles 中对函数 ReplaceLocalFiles 的操作进行了提取, 同时把函数 ReplaceAllFiles 和 ReplaceFiles 进行了合并.
优化后的结果看起来有没有清爽很多?
来源: http://www.bubuko.com/infodetail-2680352.html