7 模块与包

    技术2022-07-10  168

    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']

    Processed: 0.036, SQL: 9