• 3
  • 4
分享
  • 【原创】Python 代码优化实践
  • sylan215 2018-09-12 20:09:09 字数 5948 阅读 3642 收藏 4

最近在用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. 部分业务逻辑太细化,有重复的代码实现,导致实现看起来比较臃肿。

对于第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进行了合并。

优化后的结果看起来有没有清爽很多?

本文原创发布于公众号「sylan215」,十年测试老兵的原创干货,关注我,涨姿势!

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   今天,我们来聊聊如何成为一枚初级测试工程师?  最近经常收到小伙伴的私信问打算进入到互联网这个行业,如何转行软件测试?学测试难吗?以及谈到自己非计算机科班毕业,半路转行没什么经验,比较迷茫,不知道学习路线,以及需要学习哪些课程。甚至询问是否需要报个培训班学习,自学就可以吗,还是必须报班等问题。  首先我想说,初级软件测试学习和入门的门槛都是很低的,比起开发岗位来说,要容易得多,只要知道学习路线以及怎么学之后,自学是完全可以入行的。所以,今天就来跟大家探讨一下这个问题。  我浏览了 BOSS 直聘、拉勾网、猎聘网等招聘网站上目前关于初级测试工程师的招聘要求,以及薪水待遇等信息。以本人所在的城...
            0 0 925
            分享
          • 油猴脚本管理器tampermonkey是安装在谷歌浏览器上的一个插件,它可以运行一些用户自定义的脚本。这样在访问不同页面时,如果有可用的脚本插件就会自动提示,并且产生作用。如下:同时他本身也提供了很多用户贡献的脚本以供下载(比较稳定的网站是greasyfork),当然git上面也有很多大神上传了自己写的脚本,观看这些脚本,我们可以学习别人写代码的逻辑,最最便利的是可以直接下载脚本使用,节省了许多浏览器上需要额外做的步骤。简单截图几个下载量比较高的,比较受欢迎的脚本:常见的脚本,比如:免登录操作网页,复制文本,下载提速,自动填充文本框等。介绍了这么多,那这个小插件对于我们测试有哪些帮助呢?其实这...
            1 1 10132
            分享
          • 1. 变量和赋值1.1 赋值Python中变量不需要声明,直接定义即可,会在初始化的时候决定变量的类型,使用=来进行初始化和赋值操作,即你给变量赋什么值,变量类型就会跟随变化(动态)name = 'luo' #赋值操作,字符型string print(name) print(type(name))name = 15 #整型int print(name) print(type(name))name =1.5 #浮点型float print(name) print(type(name))1.2 增量赋值...
            0 0 1353
            分享
          •   1. 软件测试不是点点点,还有性能测试,自动化测试,安全测试,甚至于AI测试,大数据测试等等,软件测试的前景还是非常好的。  2. 软件测试相对门槛比较低,前期比较容易入门,哪怕不是计算机专业的小白,也能轻松掌握,不过,软件测试后期的难度和开发没有什么两样,想要拿到高薪,就得学会自动化测试,接口测试这些编码知识。  3. 经常有人抱怨,学了用不上,学完就容易忘记,因为学的都是理论知识,长时间不用自然会容易忘记,最好边学边找项目练手。  4. 如果你身边有人说测试很简单,薪资混混也很容易,那么他基本上过几年还是如此,没啥大的长进。  5. 软件测试有高薪也有低薪,月入两三千是真实存...
            0 0 1277
            分享
          •   一、从这个问题,我能读出一些信息如下:  1、不知道您从事测试工作多久了,可以看出您特别羡慕测试开发工程师;  2、 您可能一直从事功能测试工作,工作模式或大环境下,被中了草,想学习测试开发相关的知识;  3、疫情下,机械单一的工作(功能测试工作),被替代性强,有些担心,想增加核心竞争力,拿到更好的薪资!  二、针对您的问题,作出细致的回答:  无论从1-3中的哪一点来看,您都有想转型测试开发的想法,那么让我们先来了解测试开发吧。  1、测试开发工程师:  是指那些既可以称作是开发人员,同时也负责软件开发阶段和测试周期的测试工作的技术人员。一个专业的SDET更关注软件产品的可测性,稳健性和...
            0 0 1595
            分享
      • 51testing软件测试圈微信