7 模块与包
Python中的模块和包
-
一个py文件就是一个模块,py文件的文件名就是模块名。
-
在 Python 里为了对模块分类管理,就需要划分不同的文件夹。多个有联系的模块可以将其放到同一个文件夹下,为了称呼方便,一般把 Python 里的一个代码文件夹称为一个包。
7.1 导入模块
- 导入模块有五种方式
- import 模块名
- from 模块名 import 功能名
- from 模块名 import *
- import 模块名 as 别名
- from 模块名 import 功能名 as 别名
下面来挨个的看一下。
- import
在Python中用关键字import
来引入某个模块,比如要引入系统模块 math,就可以在文件最开始的地方用import math
来引入。
语法:
import 模块1,模块2,... # 导入方式
模块名.函数名() # 使用模块里的函数
-
想一想:
为什么必须加上模块名调用函数呢?
-
答:
因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名
示例:
import math
#这样才能正确输出结果
print math.sqrt(2)
#这样会报错
print(sqrt(2))
- from…import…
有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:
from 模块名 import 函数名1,函数名2....
不仅可以引入函数,还可以引入一些全局变量、类等
-
注意:
通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。
例如,要导入模块fib的fibonacci函数,使用如下语句:
from fib import fibonacci
注意
- 不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个函数引入
- from … import *(不推荐使用)
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
注意
- 这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
- as 别名
当导入的模块名、变量、方法等与当前文件的模块名、变量、方法等重名,可以使用 as
来取新名,之后在当前文件中调用需要使用新名
- 可以给模块名起新的模块名:
import 模块名 as 新模块名
- 可以给变量、方法起新的名字:
from 模块名 import 变量|方法 as 新名
In [1]: import time as tt # 导入模块时设置别名为 tt
In [2]: time.sleep(1)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-2-07a34f5b1e42> in <module>()
----> 1 time.sleep(1)
NameError: name 'time' is not defined
In [3]:
In [3]: tt.sleep(1) # 使用别名才能调用方法
In [4]:
In [4]: from time import sleep as sp # 导入方法时设置别名
In [5]: sleep(1)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-5-82e5c2913b44> in <module>()
----> 1 sleep(1)
NameError: name 'sleep' is not defined
In [6]:
In [6]: sp(1) # 使用别名才能调用方法
In [7]:
补充:
哪些内容可在模块中使用?
答:所有在在指定if语句外的全局变量
导入模块的原理:
- 当使用import或者from…import…导入模块的时候,系统会自动将被导入的模块中所有的代码都执行一遍。
import 和 include 的区别?
共同点:都是导入另外模块到当前文件,并执行;
不同点:当重复导入同一个模块时,include 每导入一次执行一次模块内容,但import重复导入只执行一次。
循环导入的问题(既在test1.py中导入test2.py,在test2.py中执行test1.py):
- 在使用include时会发生循环引用,造成死循环;
- 在使用import时不存在循环导入的问题 ,因为import自带查重功能,只会导入一次。
怎么选择性的执行被导入的内容?
- 当导入的模块存在较为复杂的,增加了当前文件负担的内容时,需要在导入的模块内写入
if __name__ == '__main__':
,将不导入的内容写入if语句中,if语句外的内容会作为模块内容导入并执行。if __name__ == '__main__'
的原理:- 每个模块都有一个属性:
__name__
,这个属性是用来保存当前模块的名字;__name__
默认值就是模块名(模块对应的py文件的文件名)。当我们直接执行某个模块的时候,这个模块的__name__
属性就会自动变成__main__
。
7.2 制作模块
搜索路径
要自定义模块,首先要确定代码存放的位置可以被 Python 解释器找到。比如把一个 py 文件放在 U 盘里,在把 U 盘拔掉,那肯定是没法再导入了。
当你导入一个模块,Python解析器按照搜索路径来查找 py 文件,也就是说我们的 py 文件必须在模块搜索路径的某一个文件夹里。
- 模块搜索路径存储在sys模块的sys.path变量中。
- 变量里包含当前目录,以及由安装过程决定的默认目录。所以在习惯上,我们会把自己写的代码放到同一个项目文件夹。
- 定义自己的模块
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。也就是说自定义模块名必须要符合标识符命名规则。
比如有这样一个文件test.py,在test.py中定义了函数add
def add(a,b):
return a+b
Copy
- 调用自己定义的模块
那么在其他文件中就可以先import test,然后通过test.add(a,b)来调用了,当然也可以通过from test import add来引入
import test
result = test.add(11,22)
print(result)
Copy
- 使用
__name__
变量来写测试模块
在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息,例如:
def add(a,b):
return a+b
# 用来进行测试
ret = add(12,22)
print('int test.py file,,,,12+22=%d'%ret)
Copy
如果此时,在其他py文件中引入了此文件的话,想想看,测试的那段代码是否也会执行呢!
import test
result = test.add(11,22)
print(result)
Copy
运行现象:
至此,可发现test.py中的测试代码,应该是单独执行test.py文件时才应该执行的,不应该是其他的文件中引用而执行
为了解决这个问题,python在执行一个文件时有个变量__name__
直接运行此文件
在其他文件中import此文件
总结:
- 可以根据name变量的结果能够判断出,是直接执行的python脚本还是被引入执行的,从而能够有选择性的执行测试代码
7.3 模块中的_all___
- 没有
__all__
test.py
文件:
class Test(object):
def test(self):
print('----Test类中的test方法------')
def test1():
print('---test1函数---')
def test2():
print('---test2函数---')
main.py
文件:
from test import *
# 可以直接使用这个模块里的所有函数
test1()
test2()
# 可以使用这个模块里的类创建对象
a = Test()
a.test()
- 模块中有
__all__
test.py
文件内容
# 给 test.py 文件添加了__all__属性,只有这个属性里的内容会被导入
__all__ = ["Test","test2"] # 由Test类和test2方法,没有test1方法
class Test(object):
def test(self):
print('----Test类中的test方法------')
def test1():
print('---test1函数---')
def test2():
print('---test2函数---')
Copy
main.py
文件内容:
from test import *
# 可以使用test2方法
test2()
# 不能使用test1()方法,因为test1不在__all__属性里!
#test1()
# 可以使用Test这个类
a = Test()
a.test()
Copy
总结
- 如果一个文件中有
__all__
变量,那么也就意味着__all__
属性里没有列出的元素,在from xxx import *
导入时,不会被导入。
7.4 Python中的包
什么是包?
- 包的本质就是一种特殊的文件夹(a. 里面都是py文件;b.自带一个
__init__.py
的文件)
- 导入包的方式
现有以下包newmsg
,包里由两个模块,分别是sendmsg.py
、recvmsg.py
文件。在包的上级文件夹里,有一个test.py
文件,目标是在test.py
文件里引入newmsg
的两个模块。
目录结构如下图所示:
sendmsg.py文件里的内容如下:
def send_msg():
print('------sendmsg方法被调用了-------')
recvmsg.py文件里的内容如下:
def recv_msg():
print('-----recvmsg方法被调用了--------')
可以使用以下几种方式来导入模块,使用模块里的方法。
1>. 直接使用包名.模块模块名导入指定的模块。
2>. 使用from xxx import xxx
方式导入指定模块。
3>. 使用__init__.py
文件,导入包里的指定模块。
可以在newmsg
里创建__init__.py
文件,在该文件里导入指定的内容。
在__init__.py
文件里编写代码:
from . import sendmsg # 导入指定的模块 . 代表的是当前文件夹
test.py文件里的代码
import newmsg # 导入时,只需要输入包名即可。在包名的__init__.py文件里,导入了指定模块
newmsg.sendmsg.sendm_msg() # 可以直接调用对应的方法
# newmsg.recvmsg.recv_msg() 不可以使用 recvmsg 模块,因为 __init__.py文件里没有导入这个模块
4.> 使用__init__.py
文件,结合__all__
属性,导入包里的所有模块。
在newmsg
包里的__init__.py
文件里编写代码:
__all__ = ["sendmsg","recvmsg"] # 指定导入的内容
test.py文件代码:
from newmsg import * # 将newmsg里的__inint__.py文件里,__all__属性对应的所有模块都导入
sendmsg.sendmsg()
recvmsg.recvmsg()
总结
- 包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为
__init__.py
文件,那么这个文件夹就称之为包
- 有效避免模块名称冲突问题,让应用组织结构更加清晰
__init__.py
文件有什么用
__init__.py
控制着包的导入行为。__init__.py
为空仅仅是把这个包导入,不会导入包中的模块。可以在__init__.py
文件中编写内容。
newmsg/__init__.py
文件:
print('hello world')
别的模块在引入这个包的时候,会自动调用这段代码。
__all__
在__init__.py
文件中,定义一个__all__
变量,它控制着 from 包名 import *时导入的模块。
newmsg/__init__.py
文件:
__all__ = ['sendmsg','recvmsg']