最近写python的时候有的刚接触python的朋友问我什么是包,什么是模块,作用是什么。他们说网上的解释大多数是从一些专业的角度进行描述,但是对于新接触的人来看,根本看不懂这些专业的描述。这里我就通俗易懂的简单解释下python 的函数、模块、包的意思,当然通俗的解释并不全面,但是对于新接触的人应该能很好的理解。
默认使用windows的pycharm进行描述和解释:
python 函数:可以理解成把所写的一些实现的代码进行封装的。什么叫封装?就是封闭的意思,如下图:
这里我写了一个简单的计算过程,注意我新建的是一个python文件,然后在里面写代码的。实现1+2 =3,很明显我执行a+b的时候只能在这个py文件中直接执行一次,不能重复执行,为了我想重复执行a + b而不需要每次点击文件执行,我定义成一个函数。而函数能就把a+b封闭起来,如下图:
这样我就实现了点击一次运行代码调用三次函数就可以执行三次a+b,而不需要手动点击3次运行实现执行3次a+b,我把a+b封闭到c函数里面,如果我不调用c函数直接执行会是怎样?如下图:
当我不手动在代码里面调用c函数时,直接执行没有输出计算结果,只是告诉我代码没有报错。这个简单的例子告诉我们,函数的作用是把所需的代码封装成一个整体,像是把代码圈起来,优点就是我可以通过调用函数的形式实现代码的重复使用而不需要每次都去写一遍相同功能的代码。当然如图三所表达的,当把代码封装成函数的时候,如果不调用该函数,那么在直接这个代码的时候,python只会检查编译函数里面的代码,并不会执行。所以需要注意的是,我们封装函数后,如果不主动声明调用,那么即使在同一个py脚本下点击执行,函数也是不会执行的。
一个py文件有许多代码,有的封装成函数,有的在函数的基础上封装成类,具体什么是类就不解释了。这里通俗的解释下模块。什么是模块?可以理解成py文件,如下图:
我的这个web_test项目下有些py文件,我点击一个get文件如下:
这个get_flac_info.py文件下有个类,里面封装了一些函数,比如def_flac_info函数。而模块我们就可以理解成,一个带有代码的py文件。我们可以通过import的形式导入模块到其他文件中,实现其他py文件调用这个模块的一些函数,如下图:
模块即py文件,同过import导入模块后我可以在test1模块中调用get_flac_info模块中的File函数。
包呢?包就是很多模块放在一个文件夹下的文件夹,如下图:
这里这个mutagen文件夹就是一个包。这个包包含了许多模块,如init.py,_compat.py,aiff.py等等许多py文件。也许你会问id3文件夹是啥,asf文件夹是啥。这里mutagen包里面的文件夹,也是一个包,如id3文件夹是一个包,mp4文件夹也是一个包。包是一个文件夹,里面可以包含模块,也能包含包。简单的解释就是,mutagen包调用了id3包。所以,有这样的一种关系:
一个py文件 = 一个模块
一个模块里面包含着类和函数,一个函数里包含着代码
一个包里包含着模块 ,同时包也可以包含包
以下有些注意事项需要说明:
1、一个包需要有__init__.py模块, 这个模块可以里面没有任何代码。他的作用是什么呢?作用就是区分这个文件夹是一python包还是只是一个文件夹。因为包里面可以有模块而不是必须要有模块,所以如果一个包里面什么都没有,只是一个空文件夹,那还叫包吗?所以为了区分空文件夹和包的区别,定义了一个__init__.py的模块,即使这个py文件模块里面啥都不写,当这个空文件夹有了这个模块,我们就把这个文件夹定义成包。
2、对于学过一些python的朋友,解释下__init__.py模块我们一般做什么?当我们做了一个包,肯定是希望其他地方引入和调用这个包的一些功能,然而,即使我们写的这个包也可能引用了一些别人的python模块,比如系统操作模块os。当我们这个包运行的时候需要调用os的时候,别人调用我们的包不知道我们import了哪些模块,当别人没有import我们包所需模块的时候,调用我们的包执行一些方法就不能实现会报错。所以为了避免出现其他调用包后缺少包的依赖,我们通常会在__init__.py里面定义 导入模块所需的依赖,如下图:
打开了mutage包的__init__.py模块,可以看到这个模块就是导入了一些这个包所需的依赖。所以__init__.py不一定为空哦。
很容易理解为什么会有函数,也能理解模块是什么,那么为什么要有包呢?直接导入模块不是就可以了吗?
举个栗子:
路上有一辆载客行驶的客车,把客车看作是程序,那么载客就是程序实现的意义。那么客车是怎么构成的呢?轮子、发动机、框架、椅子等等,每个完整的大构件都是一个独立可以坏了就换掉的东西,这种独立的东西我们可以看作是程序的包(多个包组成了程序),都是独立运作而没有相互绝对的依赖,只要功能相同我就可以直接换个包。再看发动机,由许多转轴、齿轮构成,我们可以把这些独立的转轴齿轮当作是模块,也是可以独立更换,但是这些零件只能在发动机类用而不能在轮胎上用。所以其他包的模块我们不能哪来构 不同功能包的模块(只是比喻,当然程序中模块是可以相互调用的)。在看转轴,由许多螺丝和铁杆组成,程序里面就可以看作是方法。而构成这些铁杆的金属,就可以看作是代码。当然金属可以通过不同的工艺制作做成轮胎的材料,代码也是一样的,不同的写法可以构建不懂得模块和包。这里只是个比喻。
我的理解来看,包和模块和函数主要是把功能模块化。像乐高一样,只要确定最基础得拼块,就可以拼出各种形状。能极大的提高程序的可维护性(哪里需求变了就改哪里不需要整个程序重写),同时极大的降低程序的工作量(相同功能的部分可以直接调用而不需要重复写)
最后建议:每个函数(方法)建议只写成只做一件事,不要写的很复杂,做多件事。因为一件事我们遇到同的情况可以直接调用,而写复杂了,最小维度没有办法拆开,如果(A+B+C)事件写成ABC事件,那么我们其他地方需要单独实现A事件的时候就没有办法直接调用ABC事件,只能重写A,而A+B+C的形式我们可以单独调用A事件和B事件,所以建议每个方法最好只做一件事。多个事件可以封装成一个类,类似于(A+B+C),这样我们既可以单独调用A B C,也可以调用A B / A C /B C/ ABC.重复的工作量大大的减少。