我用 python 写软件的心得分享

前言

  我接触的第一个程序语言是 QBASIC ,当年DOS流行时,系统带了个简单的编程工具,于是我疯狂地迷上了编程,写了几千行的代码做了个简单的字符游戏,在同学面前显耀后被发现整个代码里 GOTO 满天飞,没有任何章法,纯属一碗面条。随后又学习了C、 VB、VC++、Delphi、C++ builder等多种语言,期间也与朋友用 PHP 编写过网站后台系统。在这么多年这么多语言的学习过程中,总结了一些经验:编程相关资料要尽量齐全,思路一定要有,框架、模块、流程要清晰,所有的想法一定要用代码测试通过,代码一定要放在最后一步再写。

  我算半个野路子出身,虽是学计算专业的,但并没有专门编程,完成是靠着自己的兴趣爱好在网上摸爬滚打,也走了不少弯路。随着写的软件越来越多,心得体会也越深,越就越觉得自己欠缺甚多,思路也越来越晰,总有把前面写过的软件再来一遍的冲动(还好只是冲动,也实在没精力重写了)。

  我近些年我用 python 写了一些软件,也在吾爱上发表过,有《文件清单生成器》、《文件时间修改器》、《小说规则下载器》、《ePub 电子书编辑器》、《U盘歌单管理器》、《音乐内嵌图片管理器》、《批量二维码生成器(离线)》、《漫画图片管理器》、《漫画下载器》、《文件名管理器》等等,有兴趣的可以到 https://wwx.lanzoux.com/b01hxwx9g (密码: gcf2)下载试用。

  所以想把自己的一些心得体会给大家分享一下,也可能对于大家来说是落后的,也可能并不合适一些人使用,但看一下总没坏处,说不定能少走点弯路,他山之石,可不可攻玉就看大家了。我本人是单干型的,没怎么组过团,估计对于团体编写的大软件用处不大。

一、为什么选用 python

  先后学习使用过 VB、VC++、Delphi、C++ builder,这些也都出过一些小工具,但都是自己用,它们在打包封装时总是觉得有所欠缺,也就没怎么用心学,直到我接触了 python ,发现它的语法没那么严谨,学着学着就喜欢上它了,然后其他的语言也都放下了。

  python 的优点是很多,缺点也不少,但用着感觉对了就越用越开心。它虽然是解释型语言,运行效率上会差一些,但如果不打算玩算法什么的,是非常不错的选择,现网上用 python 实现爬虫的例子就非常多。

  我选用python 是就看上了它语法简洁、容错率高,变量、数据类型、对象使用非常方便,网上提供的免费模块很多,安装容易,还有一点很重要,就是打包封装也很方便,虽然体积会有点大(用对方法,打包的体积可以大大减少),但对运行环境要求不高,基本不需要安装额外的库或插件。

二、 python 使用前准备

  python 安装也很方便,从官网下载与自己系统一致版本安装即可,安装后即可使用,但不建议使用其提供的编写环境,用来过手瘾就行了。开发工具要选择自己顺手的,网上有很多,象 Eclipse、PyCharm、VSCode、PTVS等,大家可以都安装几款试用下。

1、开发工具

  选择一个好用的开发工具是非常重要的,我选用的是Visual Studio Code,微软的重量级免费开发工具,不要盲目跟从,合适自己的才是真的。选用的开发工具最好能支持下面几点功能:

1):代码折叠

  要写一款软件,随便都会是几千行代码,有了代码折叠功能后,能快速定位要修改调整的部分,VSCode 的折叠功能可用 Ctrl - K + Ctrl +1~3 等组合快捷键按层折叠, Ctrl - K + Ctrl + J 可全部展开。比如在编写模块类时,Ctrl - K + Ctrl + 2 能将所有成员方法进行折叠,所有成员方法会以目录型显示,如下图,方法后面加上注释,定位代码非常方便
在这里插入图片描述

2):代码自动补全

  即是根据上下文和当前的输入,自动弹出合适补全的代码,可以是变量、对象、模块、方法,可以提高代码的编写时间,有效地减少因变量名导致的错误。因为 python 对变量名是区分大小写的,编写时不小心就是弄错,这种错误在查找时是谁试谁知道,太酸爽!VSCode 的自动补全支持到模块,不管是自已写的模块还是安装的模块,都能支持多级的自动补全,非常方便。

3):能直接运行测试,支持断点调试

  这点非常关键,程序在测试时未达到预期效果时,利用好断点调试功能,很容易分析出问题。VSCode 这方面做得也不错,光标移到变量、对象上时,能实时查看其内容,对复杂的数据结构也能做到层层展开查看。

4):代码颜色标记

  能自动区分代码中的变量、方法、常量、关键语法、参数,并以不同的颜色显示,方便阅读理解代码。VSCode 支持下载主题风格。

5):其他

  其他的开发工具我没用过,这里就按 VSCode 说下比较好用的功能。
定义的变量未使用时,变量名会以暗色显示,提醒该变量未被使用过(赋值的语句不算使用)
选中多个字符时,代码中相同的字符会出错背景标记,可快速分析变量、方法的使用情况,也可用来核对变量、方法名是否正确语法检查,对于语法上的错误,能很快的给出提醒,特别对于括号配对的检查,会显示不同的颜色来区分。大部分开发工具都支持上述的几点功能,但那个更好用需要大家自行判断了。

2、资料的查找与收集

  这里说的资料是指 python 自身的命令、函数、模块的资料,也包括软件项目及涉及的资料,如要编写调用MP3 信息的软件,就要收集 MP3 有关 ID3的资料,还要尽量收集全,否则写出的软件会就会存在欠缺。

  对于 python 提供的函数、模块,如要查看 MP3 的创建时间,会用到 time 模块,那么关于 time 模拟中提供的各种方法就要有所了解,若是要解析文件名中包含音乐的信息,则需要到正则模块 re,这类字符操作的方法也是需要了解掌握的。其实象 time 、re 这类模块会经常用到,所以建议大家就把其常用的方法收集起来,象 re.search 、 re.sub 、time.strftime、time.strptime 这类方法,使用频率会非常高,特别是后面两个经常弄混。所以建议大家安装一个在线笔记软件,把这类常用的代码收集保存,要用时也方便查阅。我原一直用有道笔记,但最近免费版有登录设备的限制,我改用印象笔记,都非常方便好用。

  如上例,要读取 MP3 的 ID3信息,可能会需要用到 mutagen 模块,所以还需要收集 mutagen 模块的相关资料,还是建议把查到有用的资料存到在线笔记中,对于比较重要的方法,最好还是用代码测试一下,再把测试成功的代码一并存到笔记中。

三、编程习惯的养成

  好的编程习惯对程序后期的修改、升级带好很大的便利,象我写了一些软件,过段时间再回来完善代码时,会发现原来的很多想法思路都没印象了,不得不往前查阅代码。如果代码不按约定编写,会增加阅读代码的时间。

1、变量、方法、对象的命名约定

  变量名要按自己要习惯定制,有些人喜欢用大小写来连接单词,也有人用下划线来连接,这其实没有好坏之分,主要看自己习惯,我建议英语好的用单词首字母大写来连接,看着高大尚些(如 FilePath),英语不好的就用下划线吧,这样容易理解些(file_path)。python 变量对数据类型要求不高,所以不一定要加上类型前缀(或后缀),但为了以后阅读方便,建议大家对列表、字典等数据加些修饰,我的习惯是列表变量后加 s,字典则加 dict前缀,变量如调用不多时,则在初始化或赋值命令后加注释。

2、模块、类对象的应用

  建议大家多创建模块文件,最好是每个类用一个文件,除非很小的类。我写的软件会有许多 tkinter 窗口,我刚开始时把很多窗口类(我喜欢把窗口写成类后调用)放在同一模块中,结果导致模块非常臃肿,都有几千行的代码量,查阅起来非常不方便,后面拆分后方便多了,但又导致容易弄混窗口模块,最后不得不在 main.py 中加入各个模块的说明。有一点需要注意,python 模块若存在交叉调用时容易出错,这时就建议把相互调用的模块合并了。

  我习惯把软件项目的一些常用单独存放到一模块中,如软件的名称、版本号、更新日期、简介、数据库定义等,存在多个模块需要共用的常用也可以存放到其中,其他模块要引用一下就行了。

#infos.py 常用定义模块
STR_APP_NAME = '我的软件' #软件名称
STR_APP_NOTE="""我的软件是最好用的!!
"""

#其他模拟调用前声明一下
from infos import STR_APP_NAME #软件名称

  这样以后要修改一些固定的信息会非常方便,如有用到 sqlite3,数据表的定义也可以写到 infos.py 文件中,如:

STR_SETUP_DB     = "system.db" #数据库名
STR_SETUP_TABLE  = "setup"     #系统设置数据表

STR_CREATE_DB_TABLE_SETUP="""CREATE TABLE IF NOT EXISTS {} (
            id Integer PRIMARY KEY AUTOINCREMENT,
            name Text ,
            value Text );""".format(STR_SETUP_TABLE) #如果不存在设置表则创建

"""    系统设置关键字定义表
name(设置关键字) = value(默认值)   说明
    TipTime         8              工具提示显示时间
    Welcome         1              显示迎宾屏
"""

  软件在初始化时执行下一 sql 语句即可,这样后面要修改数据表结构时,也很方便。

  对于界面文字信息比较多的,可以增设一个文本常量模块,好处在于修改文字时容易定位,不需满代码行查找,以后要改为多国语言版也较方便。缺点是需要定义大量的关键字与文本对应,还要对应到界面的代码处,工作量有点大。

3、注释

  如果软件项目需要经常更新、升级,那注释信息一定不能少,最好能做到每个变量在初始化时都有注释,关键代码处也要有,原因无法保证一段时间后,你还能记住编程时的想法、思路。不想坑未来的你就花点时间写备注吧!

  每个模块前最好有模块说明,每个类也要有自己的说明,类里的方法后最好加上简单的说明,折叠代码后也方便区分方法的作用。如方法带的参数较多,最好在方法下方加上详细的说明。别偷懒,我曾经也偷懒过,后面结果不得不花大量时间去理解自己写的代码。

4、其他

  编写代码时可以多插入空行,可让代码更容易阅读,代码也不显得拥挤。多用空行将代码分段,就象小说的自然段一样,能让阅读变得轻松。每一行代码不宜过长,尽量不要超过屏幕显示,否则查阅代码时还要左右滚动非常不方便。代码可以在逗号后换行,若是运算表达式太长,则可以用 \ 强制换行。如:

 tree=ttk.Treeview( box ,
                    show       = 'tree headings' ,
                    height     = 10 ,
                    selectmode = "borwse" ) # =号要不要对齐看大家习惯吧,刻意对齐的话有点强迫症了

# 表达式强制换行示例,注意带 \ 强制连接符的代码行不能加注释, \ 后要紧跟着回车符
    if  (     result > 0     # 必须有返回结果
           and
              isOK == True   # 必须确定后
           and
              len(text) > 0  # 文本不能为空
        ) \
        or \
        (     isPass == True # 必须通过测试
           and
              count >0       # 必须有数量
        ): # 表达式这样写会容易阅读,当然大家也可以有自己的写法,只要灵活运行 \ 来连接代码行就行

        print("通过")

四、软件建模的思路与建议

1、软件框架的定义

  在一个软件作品里,代码不是重点,不急着写,先把框架、流程确定,整个模型敲定后才开始写代码。框架、建模、流程甚至要总大部分时间,比如我后面编写的《文件名管理器》,整个软件从开始准备到最后成品花了将近2个月,但前期的建模工作花了1个多月。如果是边写代码边改流程,最终软件可能会出现功能不理想,效率不高,代码杂乱难读,后期改进优化难度也非常大。

  在模型没建好时,会有一些想法导致手痒,非要去写代码,这时最好写一些测试用的代码段,以能实现某些功能即可,最后再把这些代码完善成预设的模块或方法,尽可能地考虑多种情况,在接口(参数)方面也多想些,注释也一定不能少。

  建模时就要考虑好软件需要的模块、类、数据库结构。比如要写读取音乐文件的ID3 信息的程序,我们可能会选 mutagen 模块,而这个模块的内容很多,甚至包括了视频文件,而音乐文件格式的不同,读取ID3信息的方法也不相同,这时最好是编写一个读写音乐ID3的类,在类里定义好对不同格式音乐的读写操作。这算是程序里的关键模块了,可以提前编写,全部测试通过后当做现成的模块使用。

  对于界面的模块,可能存在很多变数,代码建议放到最后编写,也就是软件定调后再开始编写界面框架代码。在建模时先不考虑外观布局,先考虑功能实现方面。我常用的界面是 tkinter 模块,只要功能实现了,界面布局与塔积木没什么区别,总能搭出过得去的界面。

  数据库结构也一样,可能存在不少变数,甚至可能在软件快写完时才发现存在问题,这时再改动结构工作量变很大。所以数据库结构在构思时,需要考虑各种因素,宁愿多设字段也不要遗漏。另 sqlite3 数据库是没有时间类型的,一般可以用字符型或浮点型来保存,如果时间只是用来查看就用字符型,若需要对时间进行比较的建议以时间戳的方式存成浮点型。

2、功能实现与调试

  当软件的模型定调后,把模块、数据库结构规划好就可以编写功能模块了。功能模块是指软件项目中起到关键作用的模块,如读写音乐文件ID3的代码段,这里就一直用这例子来做说明。这个模块会调用 mutagen 模块来实现读写的功能,但由于 mutagen 对不同格式的音乐读取的方式还不太一样,所以我们要自己封装一个音乐类,先定义基本方法以,按扩展名区分音乐格式,再按格式选择不同方式读写 ID3信息。

class musicInfo():  #音乐ID3信息 获取及修改
    def __init__(self,filename=''): #音乐信息类初始化方法
        ...
    def getMusicType(self,filename=''):#返回音乐类型(0=mp3,1=flac,2=wav,3=ape)
        ...
    def getMusicTitle(self,filename=''):# 返回音乐的标题名【歌名】
        _type=self.getMusicType() #返回音乐类型
        if _type==0:   # mp3
            return self.music.tags['TIT2'].text
        
        elif _type==1: # flac
            return self.music.tags['Title'].text

        #  (让代码折叠不出现空行)
    def getMusicArtist(self,filename=''):# 返回音乐的演作者【歌手】
        ...

if __name__ == "__main__": #测试用代码
    m=musicInfo("d:/test.mp3")
    print(m.getMusicTitle())

  对于功能模块,每一个方法都必须通过测试,如上例中我们要对各种格式的音乐进行测试,确定每一种音乐都能正确读取歌名后再测试歌手,需要把模块类中所有方法都测试成功后,这个模块才能算完成。

  对于公共方法也一样(后面重复代码时会有说),需要对每一个方法进行不同角度的测试,尝试带进不同的参数查看运行结果。

3、界面的布局与实现

  界面设计关系到软件好不好用,是否容易上手。界面设计不能太理所当然,毕竟使用者的水平参差不齐,我们要尽可能把使用者的水平假设低一些,一些可有可无的提示或小部件,能保留就保留,否则后面编写教程就有罪受了。

  软件界面最好是在模块、数据库结构确定完后再开始着手编写,一般用得最多的也就是 tkinter 模块了,tkinter 窗口中的小部件建议用 pack() 方法来放置,可以看看我以前发表的《Python tkinter 复杂界面小部件布局》,你会发现用代码生成界面也不难。

  界面比较简单的还好,稍微复杂一些的界面,代码量会很大,不利于以后修改,所以这里建设将布局代码进行分段放入到方法中,可以按容器 Frame、或 LabelFrame 进行分段,如带有工具栏的软件,可以工具栏放到单独的方法中创建:

class main():
        ...
    def initUI(self):# 主界面框架
        box=tkinter.Frame(self.root) # 设置窗口留边的尺寸
        box.pack(padx=15,pady=8)

        toolbar=tkinter.Frame(box)
        toolbar.pack(tkinter.TOP,fill=tkinter.X)
        self.initUI_toolbar(toolbar) # 创建工具栏
        
        content=tkinter.Frame(box)
        toolbar.pack(tkinter.TOP,fill=tkinter.BOTH,expand=True)
        leftContent=tkinter.Frame(box)
        leftContent.pack(tkinter.LEFT,fill=tkinter.Y)

        self.initUI_leftContent(leftContent) # 创建左面板内容

        rightContent=tkinter.Frame(box)
        rightContent.pack(tkinter.LEFT,fill=tkinter.BOTH,expand=True)

        self.initUI_rightContent(rightContent) # 创建右面板内容


        #  (让代码折叠不出现空行)
    def initUI_toolbar(self,toolbar):#工具栏界面
        butAdd=ttk.Button(toolbar,text='添加')
        butAdd.pack(side=tkinter.LEFT)
        butEdit=ttk.Button(toolbar,text='修改')
        butEdit.pack(side=tkinter.LEFT)
        tkinter.Label(toolbar,text=" ").pack(side=tkinter.LEFT) # 实现按键隔断
        butDel=ttk.Button(toolbar,text='删除')
        butDel.pack(side=tkinter.LEFT)
        ...
    definitUI_leftContent(self,content):#左面板界面
        ...
    definitUI_rightContent(self,content):#右面板界面
        '''

  主界面可以用 tkinter.Tk() 创建,其他子窗体则用 tkinter.Toplevel() 来创建,如果子窗体为模式窗口(不关闭时无法操作主窗口),需要在初始化前加上 grab_set() 模式化,还要注意的是若它还有子窗体,在子窗体关闭后还需要再调用一次 grab_set() 方法以,否则会退出模式窗口状态(包括弹出的 tkinter.messagebox 的提示窗口)。

  调用子窗口时,子窗口有 mainloop() 和 wait_window() 两种方法显示窗口,后者在打开子窗口时会等待窗口关闭后才继续执行代码,用代码解释比较容易

#主窗体代码段
    subWindow = subClass(root)
    subWindow.show()

    print('close')
    '''
def setValue(self,value): # 成员变量赋值方法
    self.value=value

#了窗体代码段
def show(self):
    self.root.mainloop()

def OK(self): # 确定按键
    self.parent.setValue('新的值')  # 调用主窗体赋值方法

  这个是用 mainloop() 显示子窗口的赋值原型,即子窗体按下”确定“后,需要调用主窗口的赋值方法传递结果,因为在 subWindow.show() 后面的 print(‘close’) 语句在子窗体关闭后不会被执行,只有在主窗口关闭后才会被执行(很奇怪吧,这坑把我坑惨了!)

# 主窗体代码段
    subWindow = subClass(root)
    subWindow.show()

        if subWindow.isOK: # 子窗口按下 确定
        self.value=subWindow.value

# 子窗体代码段
def show(self):
    self.isOK=False
    self.root.wait_window()

def OK(self): # 确定按键
    self.isOK=Ture  #确保是按下确定键
    self.value='新的值'

  这个是用wait_window() 显示子窗口的赋值原型,当子窗口关闭后, subWindow.show() 后面的语句会继续执行。这个方式比较容易理解,但使用 wait_window() 容易出现异常,特别是要使用文件拖放操作时,铁定出错,所以大家到时自己决定用哪一种吧。

五、软件项目编写过程的注意事项

1、编写顺序

  编写代码的顺序也很有讲究,功能模块要生编写,如有数据库的也需要先创建(建议安装 SQLite Expert Professional 工具,调试时非常有用),界面模块放到最后编写,主界面和子界面的顺序可以自己决定,但如果存在较多的数据交换,建议先写主界面模块,有利于调试。

  首先要创建常量模块和公共方法模块,这样可以随时添加所需内容。接着编写功能模块、专用类模块。这些模块最好是全部编写完并调试通过后,再开始入手界面模块,否会因一些小改动导致界面大改动。

2、重复代码

  曾经看过一个视频教程,有提到每个方法不建议使用太多行代码,容易造成代码重复,也不方便阅读。在实际编写中也确实发现存在这种情况,自定义的方法不需要功能太多,也不需要带太多参数,不追求完美,只要简洁。多种功能就用多种方法来实现,只要注释做得好,调用起来不会觉得乱,还能很好地解决代码重复问题。别小看代码重复问题,若程序需要改动时,代码重复多了,改起来非常不方便,漏改后查错起来也很磨人的。

  我会把自己经常用到的功能写成方法存在一个公共模块中,比如从路径中提取文件名、扩展名(os.path.splitext 方法不会分析路径是否为目录,如目录带类似扩展名的格式会被误判):

# 公共方法模块 publicMethods.py ,提供常用的方法、函数

def getFileName(path): #从路径中返回文件名【不含扩展名】

    filename=os.path.split(path)[1]  # 得到含扩展名的文件全名

    if os.path.isdir(path): # 路径为目录时不需要拆分扩展名
        return filename

    # else: 非目录时(文件或者路径不存在时,按文件来处理)
    return os.path.splitext(filename)[0]
    
 
def getFileExtension(path , case=1): # 从路径中返回文件扩展名【不含 "." 】
    # case=1 时强制返回小写, case=0 时照原返回,其他值 强制返回大写

    if os.path.isdir(path): # 目录无扩展名
        return ""

    extension=os.path.splitext(path)[1] # 提取扩展名(含 ".")
    if len(extension)==0: #文件无扩展名时
        return ""
    
    extension=extension[1:] # 去掉扩展名前的 "."
    
    if case==0: #照原返回
        return extension

    elif case==1: 强制返回小写
        return extension.lower()

    # else: # 强制返回大写
    return extension.upper()
    

def getFileInfo(path , case=1): # 返回文件名和扩展名,以组元结构返回 【(文件名,扩展名)】(模拟 os.path.splitext 方法)
    return (
            getFileName( path ) ,
            getFileExtension( path , case )
            )

  如上面的例子,有些人可能会不考虑前面两个方法,直接把两个方法的代码集合到 getFileInfo 中,这样会使 getFileInfo 变得臃肿,没分开的容易阅读。且在只需要单独提取文件名、或扩展名时,还要全部执行而降低效率。实际应用时 getFileInfo 方法也没存在的必要,这里是借来说明用的。

  还是上例,在我的很多个软件中都会有用到这些方法,且在多个模块中也会调用这些方法。把这类方法集中定义在专门的模块中,管理起来会很方便,若要开发新的软件,复制这个模块后再进行增减就行了。在我的程序中,这类公共方法还有很多,有文件操作的、字符串操作的、数据库操作的,还有针对一些专用模块的,我会将他们分类排放(类似第一图折叠代码所示),这样查找修改也很方便。也很效地解决了代码重复的问题。

  这里在给大家分享几个公共方法,能提高不少效率:

def getDatasFromSQL(sql ,db = STR_SETUP_DB ): # 读取数据库信息,支持 SQL 的 SELECT 语句
    # sql 为SQL 的 select 语句 , db 为数据库文件名 ,STR_SETUP_DB 为系统默认数据库名

    strSQLiteDb=os.path.join(STR_SELF_PATH , db) # 打开数据库文件 STR_SELF_PATH 为软件当前目录
    conn = sqlite3.connect(strSQLiteDb)          # 创建数据库链接
    cursor = conn.cursor()                       # 创建游标

    cursor.execute(sql)                          # 执行 SQL 语句
    result = cursor.fetchall()                   # 获取数据表内容

    cursor.close()
    conn.close()

    return result

def setDatasFromSQL(sql , db = STR_SETUP_DB): # 修改数据库命令,支持 SQL 的 DELETE、UPDATE、INSERT 命令
    # sql 为SQL 的 select 语句 , db 为数据库文件名 ,STR_SETUP_DB 为系统默认数据库名

    strSQLiteDb=os.path.join(STR_SELF_PATH , db) # 打开数据库文件 STR_SELF_PATH 为软件当前目录
    conn = sqlite3.connect(strSQLiteDb)          # 创建数据库链接
           
    conn.execute(sql)                            # 执行 SQL 语句
    conn.commit()                                # 应用
       
    conn.close()

def createScrollbarWidget(frame,type=0,**kwargs): # 创建带滚动条的小部件,返回创建的小部件【支持 Treeview、Text、Listbox、Canvas】
    # frame 为上一层容器名 ,kwargs 则为各小部件支持的属性
    # type=0: treeview , =1 Text  ,=2 Listbox,其他 Canvas

    if type==0:
        widget=ttk.Treeview(frame,**kwargs)
    elif type==1:
        widget=tkinter.Text(frame,**kwargs)
    elif type==2:
        widget=tkinter.Listbox(frame,**kwargs)
    # else:
        widget=tkinter.Canvas(frame,**kwargs)

    sb=ttk.Scrollbar(frame,command=widget.yview)
    sb.pack(side=tkinter.RIGHT,fill=tkinter.Y)
    widget.pack(side=tkinter.LEFT,fill=tkinter.BOTH,expand=True)
    widget.config(yscrollcommand=sb.set)

    return widget

  这三个方法在我的每个软件里都频频用到,特别是 createScrollbarWidget 创建带滚动条不部件的方法,在界面布局时能节省不少时间。注意前两个方法我未加入异常处理的机制,大家可以自行加入,能节省一些调试时间。

3、程序调试

  程序调试是最麻烦的一步,又枯燥又花时间一遍遍地反复进行测试,还要模拟不同情况进行测试,遇到BUG更是要命。

  编程最要不得习惯的就是一口气程序,最后再试运行。这样的结果就是虫子满天飞,完全无从抓起。因此在编写每一段代码后,尽可能地进行运行测试,达到效果后才进入下一步的编写。好的习惯是每写完一个方法后,即立进行测试,趁着脑子还热乎的时间容易找到BUG。完成一系列功能代码后,也要进行阶段测试,完成一个模块后也要进行整体测试。总知就是要边写边测试,从小范围到大范围测试,最后还需要整体测试。调试是偷不得懒的,不按部就班地进行最后惨的还是自己。

  调试时要活用断点,当测试效果也预期不一致时,就可通过断点实时查看变量内容来分析原因,必要时增加 print() 命令跟踪变量的变化,网上也有不少关于调试的模块可参照,看个人习惯吧。我的习惯就设断点,再用光标查看变量内容,有时语句中为了省变量,会直接用方法的返回值,断点也无法查看返回值,这时就需要修改下代码,临时增加变量设断点来观察。需要用到 print() 命令时,可用自定方法同时查看变量的类型(这点很关键,python 对数据类型要求不高,变量的类型随时可能发生变化,只靠 print 有时判断不出来,比如 “0” 和 0 用 print()命令就观察不出来):

    setValue(getFilename(path)) # 原代码无变量,设断点也无法观察返回值

    # 可临时修改为下代码
    val = getFilename(path)
    setValue(val) # 在此代码处设断点


def watchVar(var):# 自定义变量查看方法,显示变量内容、类型
    print("类型:【{}】".format(type(var)))
    print("内容:【{}】".format(var))  # 加上【】修饰符,可得知 var="" 或空格

4、打包封装体积控制

  很多人说自己打包的 python 程序体积很大,我刚开始用时也是如此,如不如就是几十上百Mb,最后才弄清楚是 pyinstaller 的锅,在 PyInstaller\hooks 目录中,会有很多 hook 文件,基本每下载安装一个模块,都会生成一些 hook 文件,它们主要是告诉 pyinstaller 在封装时要把它们带上,所以电脑上安装的模块越多,打包后的文件也就越大。这些知识了解一下就行,不用真手动去删除这些hook文件,可能导致 pyinstaller 无法封装打包。

  知道原因解决也就有路了,最直接的方法就是将安装的模块全部删除,最后再安装软件需要的模块,其他无用的别装。如果快速删除已安装的模块,可以看我以前发表的一篇文章《Python快速重装模块方法》,还有一种方法也很不错的《Pyinstaller打包python文件太大?教你三个小技巧有效减小文件体积》,建议看下,里面教如何压缩打包文件。

  我通常会选择删除所有模块后再安装有用的,或大家也选这试,我有个建议,在 main.py 模块中,添加安装所需模块的命令集,要打包时把命令复制到控制台就可以了,安装速度也很快,花不了几分钟就还你一个干净的环境。

"""
# 删除全部已安装模块命令
pip freeze > python_modules.txt 
pip uninstall -r python_modules.txt -y

# 安装程序所需模块命令集,加上 -i https://pypi.douban.com/simple 参数就让下载变得超快
pip install Pillow -i https://pypi.douban.com/simple
pip install openpyxl -i https://pypi.douban.com/simple
pip install bs4 -i https://pypi.douban.com/simple
pip install requests -i https://pypi.douban.com/simple
pip install pywin32 -i https://pypi.douban.com/simple

pip install pyinstaller -i https://pypi.douban.com/simple

# 程序打包封装命令
pyinstaller --onefile --clean --noconsole --icon="d:\myApp\images\icon.ico" d:\myApp\main.py
""

六、最后

  网上有网友说我的软件风格很有特色,其实都归于大量使用图标,这些图标其实并不难制作,只要在 photoshop中把图片放大就发现玄机了,我制作的图标都是 20*20像素的,放大后都是简单的点阵图,小学生都能画出来。如果是不知道用什么图表示的,教大家一招,比如想做 “刷新”图标,到百度用图片搜索 刷新 图标 关键字,找自己喜欢的图照着弄就行了,可以直接把图缩小使用,也可以改成点阵图,无非是多花些时间的事。

在这里插入图片描述
  写了不少内容,但正直想学习的人应该能耐心看完,祝大家都能写出好作品。

有不少人要求源码,我写了个示范程序,大家可以参考一下
示范程序源代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jumpbull01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值