一 模块介绍

在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能。如果把开发程序比喻成制造一台电脑,编写模块就像是在制造电脑的零部件,准备好零部件后,剩下的工作就是按照逻辑把它们组装到一起。

将程序模块化会使得程序的组织结构清晰,维护起来更加方便。比起直接开发一个完整的程序,单独开发一个小的模块也会更加简单,并且程序中的模块与电脑中的零部件稍微不同的是:程序中的模块可以被重复使用。所以总结下来,使用模块既保证了代码的重用性,又增强了程序的结构性和可维护性。另外除了自定义模块外,我们还可以导入使用内置或第三方模块提供的现成功能,这种“拿来主义”极大地提高了程序员的开发效率。

模块分为四个通用类别,分别是:
1、使用纯Python代码编写的py文件
2、包含一系列模块的包
3、使用C编写并链接到Python解释器中的内置模块
4、使用C或C++编译的扩展模块

二 模块的使用方法

模块的查找顺序:
1,内存
2,内置函数
3.自主添加模块,import sys(sys.path(),用于添加自己的的模块至环境变量)

2.1 import语句
有如下示范文件

#文件名:foo.py
x=1
def get():
    print(x)
def change():
    global x
    x=0
class Foo:
    def func(self):
       print('from the func')

要想在另外一个py文件中引用foo.py中的功能,需要使用import foo,首次导入模块会做三件事
1、执行源文件代码
2、产生一个新的名称空间用于存放源文件执行过程中产生的名字
3、在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间中的名字,需要加上该前缀,如下:

import foo #导入模块foo
a=foo.x #引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get() #调用模块foo的get函数
foo.change() #调用模块foo中的change函数
obj=foo.Foo() #使用模块foo的类Foo来实例化,进一步可以执行obj.func()

加上foo.作为前缀就相当于指名道姓地说明要引用foo名称空间中的名字,所以肯定不会与当前执行文件所在名称空间中的名字相冲突,并且若当前执行文件的名称空间中存在x,执行foo.get()或foo.change()操作的都是源文件中的全局变量x。

用import语句导入多个模块,可以写多行import语句

import module1
import module2
    ...
import moduleN

还可以在一行导入,用逗号分隔开不同的模块

import module1,module2,...,moduleN

但其实第一种形式更为规范,可读性更强,推荐使用,而且我们导入的模块中可能包含有python内置的模块、第三方的模块、自定义的模块,为了便于明显地区分它们,我们通常在文件的开头导入模块,并且分类导入,一类模块的导入与另外一类的导入用空行隔开,不同类别的导入顺序如下:

#1. python内置模块
#2. 第三方模块
#3. 程序员自定义模块

也可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域

def func():
    import spam
    spam.read1()
    print("1111")

func()

2.2 from-import 语句
from…import…与import语句基本一致,唯一不同的是:使用import foo导入模块后,引用模块中的名字都需要加上foo.作为前缀,而使用from foo import x,get,change,Foo则可以在当前执行文件中直接引用模块foo中的名字,如下

from foo import x,get,change #将模块foo中的x和get导入到当前名称空间
a=x #直接使用模块foo中的x赋值给a
get() #直接执行foo中的get函数
change() #即便是当前有重名的x,修改的仍然是源文件中的x

无需加前缀的好处是使得我们的代码更加简洁,坏处则是容易与当前名称空间中的名字冲突,如果当前名称空间存在相同的名字,则后定义的名字会覆盖之前定义的名字。

from foo import * #把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字

a=x
get()
change()
obj=Foo()

如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且这个方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。模块的编写者可以在自己的文件中定义__all__变量用来控制*代表的意思

#foo.py
__all__=['x','get'] #该列表中所有的元素必须是字符串类型,每个元素对应foo.py中的一个名字
x=1
def get():
    print(x)
def change():
    global x
    x=0
class Foo:
    def func(self):
       print('from the func')

这样我们在另外一个文件中使用*导入时,就只能导入__all__定义的名字了

from foo import * #此时的*只代表x和get

x #可用
get() #可用
change() #不可用
Foo() #不可用

2.3 其他导入语法(as) 起别名
我们还可以在当前位置为导入的模块起一个别名

import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()

还可以为导入的一个名字起别名

from foo import get as get_x
get_x()

通常在被导入的名字过长时采用起别名的方式来精简代码,另外为被导入的名字起别名可以很好地避免与当前名字发生冲突,还有很重要的一点就是:可以保持调用方式的一致性,例如我们有两个模块json和pickle同时实现了load方法,作用是从一个打开的文件中解析出结构化的数据,但解析的格式不同,可以用下述代码有选择性地加载不同的模块

if data_format == 'json':
    import json as serialize #如果数据格式是json,那么导入json模块并命名为serialize
elif data_format == 'pickle':
    import pickle as serialize #如果数据格式是pickle,那么导入pickle模块并命名为serialize

data=serialize.load(fn) #最终调用的方式是一致的

三、包的介绍及使用

1.什么是包
包->超级模块
包就是一个模块,是用来被导入,包里面包含多个模块.包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来
ps:

1.在python3中,即使包下没有__init__.py文件,import    包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
   
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块

2.包的作用
随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

3.包的使用
当我们创建或使用一个包的模块时,在该包文件下会一起创建一个__init__.py的文件,在导入包时,本质上是在执行环境上导入了__init__.py的文件。

引用包的方式和引用模块的方式相同,import 和 from import两种。
绝对导入和相对导入两种方式:
绝对导入:以当前文件夹作为起始
相对导入:用.或者…的方式最为起始(只能在一个包中使用,不能用于不同目录内)
1 实验一

准备:
        执行文件为test.py,内容
        #test.py
        import aaa
        同级目录下创建目录aaa,然后自建空__init__.py(或者干脆建包)
需求:验证导入包就是在导入包下的__init__.py

解决:
    先执行看结果
    再在__init__.py添加打印信息后,重新执行

2、实验二
准备:基于上面的结果

需求:
    aaa.x
    aaa.y
解决:在__init__.py中定义名字x和y

3、实验三

准备:在aaa下建立m1.py和m2.py
        #m1.py
        def f1():
            print('from 1')
        #m2.py
        def f2():
            print('from 2')
    需求:
        aaa.m1 #进而aaa.m1.func1()
        aaa.m2 #进而aaa.m2.func2()

解决:在__init__.py中定义名字m1和m2,先定义一个普通变量,再引出如何导入模块名,强调:环境变量是以执行文件为准

4、实验四

准备:在aaa下新建包bbb

    需求:
        aaa.bbb

    解决:在aaa的__init__.py内导入名字bbb

5、实验五

准备:
    在bbb下建立模块m3.py
    #m3.py
    def f3():
        print('from 3')
需求:
    aaa.bbb.m3 #进而aaa.bbb.m3.f3()
解决:是bbb下的名字m3,因而要在bbb的__init__.py文件中导入名字m3,from aaa.bbb import m3

6、实验六
准备:基于上面的结果

需求:
    aaa.m1()
    aaa.m2()
    aaa.m3()
    进而实现
    aaa.f1()
    aaa.f2()
    aaa.f3()
    先用绝对导入,再用相对导入

解决:在aaa的__init__.py中拿到名字m1、m2、m3
包内模块直接的相对导入,强调包的本质:包内的模块是用来被导入的,而不是被执行的
用户无法区分模块是文件还是一个包,我们定义包是为了方便开发者维护

7、实验七
将包整理当做一个模块,移动到别的目录下,操作sys.path

四、注意事项

1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

3、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间