模块
模块的定义和导入
模块就是包含Python代码的文件,它可以作为独立的功能文件被其它程序调用。换句话说,就是将实现某些功能的函数封装在一个文件里,在另一个程序中导入/调用。
-
为什么要使用模块
- 程序太大,编写、维护不方便,需要拆分。
- 模块可以增加代码重复利用率
- 当做命名空间使用,避免命名冲突
- 当前程序中的命名可以与模块中的命名相同
-
如何定义模块
- 模块是一个普通的Python文件,可以直接编写
- 模块也有自己的规范,它包含以下内容:
- 函数(单一功能):模块中可以包含多个函数,但每个函数只实现单一的功能。
- 类:相似功能的组合。
- 测试代码:一般用于测试模块是否导入成功。
-
如何使用模块
-
模块导入:
-
“import modelu_name”或者“form modelu_name import function_name” 的格式从程序中导入需要的模块,然后再使用其功能函数。
- 前者:方便,直接将模块中的所有函数导入程序,使用时直接调用即可。注:需要用前缀调用功能函数,即使用模块名调用函数。
- 后者:只调用指定的函数,其它的函数就不会被导入,减少代码冗余。好处:直接调用功能函数,无需前缀调用。
对于form……import导入方式,一般写成form……import*,这样表示可以调用模块下的所有功能函数了。
-
import 模块名 as 别名
- as:是为模块取一个别名,可以用这个别名来调用模块中的功能函数
- 作用与前面的一样
-
if _name_ == "_main_"的使用:
- 为了避免模块导入成功时,测试代码被动执行,可以使用此代码让测试代码不执行。
- 建议所有程序的入口都以此代码为入口
-
-
模块的使用:
- 模块名.函数名(功能名):使用这个格式就可以调用模块文件中的某个功能函数,这种方式适合“import function_name”模式。
- 注意:模块名(也就是文件名)的命名需符合标识符命名规范,否则导入模块时会出错。
- 如果模块是以数字开头,那么需要借助importlib来导入这个模块文件。
-
模块导入举例
举例1: 我们对某值进行开方操作,就需要用到sqrt()功能函数。但sqrt()函数不能直接被调用,它是属于math模块的函数,所以需要先导入math模块。
# 先导入math模块
import math
a = 144
# 调用功能函数的格式:模块名.函数名
print(mach.sqrt(a))
结果:
12.0
**举例2:
我们可以自己来定义一个模块,里面包含一个功能函数:先定义一个Student类,再为它定义一个函数用来介绍自己。接着,我们在模块中重新定义一个新的函数用来说“hello”。
步骤1: 先创建一个名为“student.py”的文件,它就是要导入的模块的模块名,然后在里面定义Student类和其它需要的功能函数sayHello。
在student文件中: 这是student模块
# 定义Student类,它就相当于功能函数。
class Student ():
def __init__ (self, name, age):
print("我是Student模块中的构造函数!")
self.name = name
self.age = age
return
def say (self):
print("大家好,我叫{},我今年{}岁了!".format(self.name, self.age))
return
# 定义一个同级的函数sayHello
def sayHell ():
print("hello, Python!")
# 测试函数,用来测试模块是否导入成功。
print("测试该模块是否导入成功!")
其中,Student和sayHello函数为同级,均由“student.”调用。最后的print函数用于测试模块是否能成功被导入,当模块被成功导入时,程序中成打印这行文字。
步骤2: 创建完模块文件后,我们再创建一个程序文件,用于导入模块,实现想要的功能。如创建一个名为“模块测试.py”的文件。
模块测试文件中:
# 导入模块
import student
# 实例化对象
# 使用模块名调用功能函数Student
# 这一步相当于使用类Student实例化变量student
student1 = student.Student("Tom", 25)
# 调用Student类中的方法函数
student1.say()
# 调用模块中的其它功能函数
student.sayHell()
结果:
测试该模块是否导入成功!
我是Student模块中的构造函数!
大家好,我叫Tom,我今年25岁了!
hello,Python!
其中,程序运行时,出现的第一行结果正是student模块中的测试函数,说明此时模块导入成功。
然后,第二行结果是模块中构造函数中的内容。我们知道,_init_是构造函数,同时它也是魔法函数,即当实例化对象时它就会执行,说明student1 = student.Student()是在实例化对象。
使用“模块名.函数名”的格式成功调用Student这个功能函数,然后实例化对象student1就可以调用这个类的功能函数了。
最后,我们也通过模块名调用了sayHello这个函数。
思考:通过以上我们可以发现,如果将模块student中的内容和“模块测试”这两个文件中的内容合并,也可以得到相同的效果,但为何还是要将它们分开呢?
- 模块student中的内容是可以重复使用的部分,将它和其它程序分开,利于代码维护,当程序中的代码越来越多时,这个效果明显。
- 模块student不仅可以被“模块测试”这个程序调用,还可以被其它程序调用。将它分开,利于被其它文件调用,这样更显得模块的作用和重要性。
关于importlib的使用
一般情况下,模块名(封装功能函数的文件名)的命名是要符合标识符命名规范的,也就是不能以数字开头。
但如果真的数字开头的模块,且我们又需要导入使用,那么就需要借助importlib,如,我们将先前定义的student模块名修改为“01”,然后在程序文件中修改代码:
# 导入importlib
import importlib
# 定义一个变量,使用importlib导入模块并赋值给这个变量
student2 = importlib.import_modelu("01")
# 现在使用student2这个变量实例化对象
student1 = student2.Student()
student1.say()
当然,经过这一操作,其结果与先前的一样。
from modelu_name import function_name, class_name的使用:
# 导入模块中的指定功能函数,其它函数不会被导入
from student import Student
# 实例化对象,直接使用函数,无需前缀。
student1 = Student()
student1.say()
sayHello()
可以看到,使用“from modelu_name import function_name”的模式导入模块时,调用功能函数无需使用student这个前缀,直接使用Student类名了,调用sayHello函数时,也是无需前缀。
关于“if _name_ = '_main_'”程序入口
在前面的模块student中,有一个功能函数print(),它的作用是在模块导入成功后就立刻打印执行,这个作用固然不错,但有时模块导入成功也无需特意打印执行。
所以,为了避免导入模块时被动执行该语句,可以使用“if _name_ = '_main_'”,修改方式直接在student文件中修改print()代码即可:
if __name__ = "__main__":
print("这是测试代码,模块导入成功后执行,现需要屏蔽!")
当执行程序文件,模块导入成功后,该语句不会被执行。
模块的搜索路径和存储
-
什么是模块的搜索路径
- 加载模块时,系统会在什么地方寻找模块
-
系统默认的模块搜索路径
- import sys:导入模块。
- sys.path:获取所有可能的路径列表。
-
添加模块的存储路径
- 我们知道,sys.path是列表,那么可以使用list的append()方法自定义添加存储路径
- sys.path.append(dir)
- dir:表示自定义路径如:sys.path.append("C:\Users\Administrator\Desktop\Path")表示模块可以存储在桌面文件Path当中。
-
模块的搜索顺序
- 既然导入模块会查找模块,那么定有搜索的顺序:
- 先搜索内存中已经加载完毕的模块
- 搜索Python的内置模块
- 搜索sys.path中的模块
- 既然导入模块会查找模块,那么定有搜索的顺序:
模块存储路径的获取:
# 导入sys模块
import sys
# 查看路径path的数据类型
print(type(sys.path))
# 获取模块可能存储的路径
for i in sys.path:
print(i)
结果:
C:\Users\Administrator\Desktop\Work
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\DLLs
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib
C:\Users\Administrator\AppData\Local\Programs\Python\Python37
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\win32
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\win32\lib
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\Pythonwin
可见,模块的存储路径是以列表的形式存在的,说明它的存储路径可能有多个。只要将自定义的模块或系统自带的模块存储到这些路径当中,其它程序就可以导入模块并调用。
返回结果中,第一行的路径名为Work,这是存储我们当前程序自定义的存储文件,如果将我们的模块放入到这个文件中即当前文件夹中,也可以访问到模块的,例如上面的student模块。
包
包就是组织管理代码的方式,里面存放着模块。
存放模块的文件夹就是包。
-
包的结构:
|---包 |---|---\__init__ .py 标志文件 |---|---模块1 |---|---模块2 |---|---子包(包中的包) |---|---|---\__init__.py 标志文件 |---|---|---子模块1 |---|---|---子模块2
-
包的导入
-
import package_name
导入一个包,这是默认使用包中的_init_.py模块文件,使用的功能函数也是属于该文件的。
明确导入的包名
-
使用方式:
package_name.func_name() package_name.class_name.func_name()
-
import package_name as 别名
- 该方式与前面取别名的方式相似
-
import package_name.module_name
导入一个包,调用包中所有模块中的功能函数。
明确导入的包名跟模块名
-
使用方式:
package_name.module_name.function_name() package_name.module.name.class_name.function_name() pakcage_name.module.name.class_name.var
这种导入方式需要使用前缀调用功能函数
-
from package_name import module_name1, module_name2, ...
此种方式不执行_init_.py中的内容
-
使用方式
module_name.function_name() module_name.class_name.function_name()
-
from package_name import *
-
使用方式
function_name() class_name.function_name() class_name.var
这种导入方式无需前缀,直接调用功能函数
注:这种导入包的方式,只能导入_init_.py中的内容,其它模块不能导入进来。
-
-
from package_name.module_name import *
导入包中指定模块中的所有内容
-
使用方式:
function_name() class_name.function_name() class_name.var
这种导入方式就可以使用到包中除了_init_.py之外的其它模块中的功能函数
-
+ \__all__的使用
* 我们知道,from package_name import * 表示导入\__init__.py中的内容,但当\__init__.py中的内容为空,且又没有\__all__时,*导入的是空内容
* 当\__init__.py中有\__all__时,它可以导入\__all__指定的其它模块,即导入\__init__.py文件时,也可以使用其它模块了。但不会导入\__init__.py中的其它内容,只会导入\__all__指定的内容。
* 格式:\__all__ = ["module_name1", "module_name2", "package_name1", ...]
* 使用方法:
module_name1.function_name()
module.name1.class_name.function_name()
包的使用的举例
我们要创建一个名为package的包,然后在里面创建一个名为_init_.py的文件,接着又分别创建名为packModule_1.py和packModule_2.py的两个文件。
举例1:
在_init_.py文件中写入以下代码:
def init ():
print("这是__init__文件中的内容!")
接着在另一个程序文件中导入package模块并调用这个功能函数:
# 导入模块,导入模块名
import package
# 直接使用模块名调用功能函数,就直接调用到__init__文件中函数
package.init()
结果:
这是__init__文件中的内容!
可见,当以这种方式导入模块和调用功能函数时,它是默认调用_init_文件中的功能函数。但有时,我们需要调用的是包中的其它模块,所以,想要调用其它模块,可以使用“backage_name.module_name”的格式。
举例2:
现在,我们来导入package模块,并调用它里面的两个模块实现相关功能。
在模块packModule_1文件中,写入如下代码:
# 这是包中的一个模块 packModule_1
class Student ():
def __init__ (self, name, age):
print("我是Student模块中的构造函数!")
self.name = name
self.age = age
return
def say (self):
print("大家好,我叫{},我今年{}岁了!".format(self.name, self.age))
return
# 测试代码
if __name__ == "__main__":
print("这是测试代码1")
这是如之前的例子定义了一个名为Student的类,并为它定义了方法函数。
接着,在模块packModule_2中写入如下代码:
# 这是packModule_2
def sayHello ():
print("hello Python!")
if __name__ == "__main__":
print("这是测试代码2")
这是如之前一样定义了一个sayHello函数。
最后,我们在程序文件中导入package模块,并调用里面的模块实现功能:
# 导入包
# 格式:package_name.module_name
import package.packModule_2
import package.packModule_1
# 调用包中的模块中的功能函数
# 格式:package_name.module_name.function_name()
package.packModule_2.sayHello()
# 调用包中的模块中的类
# 格式:package_name.module_name.class_name.function_name()
# 以下操作是在实例化对象
student1 = package.packModule_1.Student("Jon", 20)
# 调用类中的方法函数
student1.say()
运行结果:
hello Python!
我是Student模块中的构造函数!
大家好,我叫Jon,我今年20岁了!
可见,用包的效果与之前直接调用模块的效果是一样的。
思考:
拿模块与对象、list等比较,我们可以发现,模块名相当于对象名、列表名,它只是一个指向对象的引用而已,当调用或者访问功能函数或元素时,使用的格式“模块名.函数名”、“对象名[key]”、“列表名[索引]”。
模块的访问也是相似的,如访问包中的模块中的某函数,它的格式大概为:包名.模块名.函数名()。如果访问是类,那么格式:包名.模块名.类名.函数名()。
_init_是特殊的标志函数,访问它直接是:包名.函数名()。
**关于from package_name import * **导入方式的特点
这种导入包的方式,只能导入_init_.py文件中的内容,也只能调用这个文件中的功能函数,其它模块是无法导入进来的。
举例:
我们将module_1模块中的“if __name == '__main'”去掉,直接修改成“print("这是测试代码!")”用来测模块是否导入成功。
# 使用星号导入包
from package import *
# 调用__init__.py中的功能函数
init()
# 尝试调用module_1模块中的功能函数
module_1.sayHello()
结果:
这是init中的测试代码!
Traceback (most recent call last):
File "测试包的例子.py", line 13, in
sayHello()
NameError: name 'sayHello' is not defined
可以看到,_init_.py中的功能函数可以直接被调用,而其它模块的根本就没有被导入进来,因为“print()”测试函数并没有提示导入成功。
**from package_name.module_name import * **的举例
针对以上导入方式,如果我们想要访问包中的其它模块,可以使用“from package_name.module_name import *”的导入方式。
举例:
现在我们将上述导入包的方式修改一番:
# 导入包中的module_2这个模块
from package.module_2 import *
# 然后直接调用这个模块中的功能函数
sayHello()
结果:
hello Python!
可以看到,这个导入方式是无法访问到_init_.py中的内容的,这样可以调用想要调用的模块。
关于_all_的使用
我们知道,使用“from package_name import * ”这个导入方式,程序默认只会调用_init_.py文件中的内容。
但,如果我们使用这个导入方式又想调用包中的其它模块,可以使用_all_来指定导入的模块。
注: 不管_init.py文件中有没有其它内容,只要定义了_all并指定了要导入的其它模块,那么该文件下的其它内容都不会被导入,即使使用的是“from package_name import * ”的导入方式。
现在我们在包中的_init_.py文件中添加代码:
# 在__all__中指定导入packModule_2和packModule_1这两个模块
__all__ = ["packModule_2", "packModule_1"]
# 定义一个功能函数,测试是否会被导入
def __init__ ():
print("这是init中的内容!")
然后在程序文件中书写代码:
from package import *
# 调用packModule_2中的功能函数
packModule_2.sayHell()
# 调用packModule_1中的功能函数
student1 = packModule_1.Student("God", 18)
# 调用packModule_1中Student的方法函数
student1.say()
# 调用__init__.py中的其它功能函数,测试文件中的内容能否被导入
init()
结果:
hello Python!
我是Student模块中的构造函数!
大家好,我叫God,我今年18岁了!
Traceback (most recent call last):
File "测试包的例子.py", line 20, in
init()
NameError: name 'init' is not defined
从打印结果来看,packModule_1和packModule_2这两个模块均导入成功。而当调用_init_.py文件中的其它内容时出现了错误,说明其它内容并没有被导入进来。
命名空间
用于区分不同位置****不同功能但相同名称的函数或变量的一个特定前缀
-
作用:防止命名冲突
setName() Student.setName() Peser.setName()
相同的函数名,属于不同的类或空间