Python Module(模块)及包

一、Python 模块(Module)

Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。

模块让你能够有逻辑地组织你的 Python 代码段。

把相关的代码分配到一个模块里能让你的代码更好用,更易懂。

模块能定义函数,类和变量,模块里也能包含可执行的代码。

下例是个简单的模块 support.py:

def print_func( par ):
   print("Hello : ", par)
   return

二、Python包(存放多个模块的文件夹)

实际开发中,一个大型的项目往往需要使用成百上千的 Python 模块,如果将这些模块都堆放在一起,势必不好管理。而且,使用模块可以有效避免变量名或函数名重名引发的冲突,但是如果模块名重复怎么办呢?因此,Python提出了包(Package)的概念。

什么是包呢?简单理解,包就是文件夹,只不过在该文件夹下必须存在一个名为“__init__.py” 的文件。

注意,这是 Python 2.x 的规定,而在 Python 3.x 中,__init__.py 对包来说,并不是必须的。

每个包的目录下都必须建立一个 __init__.py 的模块,可以是一个空模块,可以写一些初始化代码,其作用就是告诉 Python 要将该目录当成包来处理。

注意,__init__.py 不同于其他模块文件,此模块的模块名不是 __init__,而是它所在的包名。例如,在 settings 包中的 __init__.py 文件,其模块名就是 settings。

包是一个包含多个模块的文件夹,它的本质依然是模块,因此包中也可以包含包。例如,在前面章节中,我们安装了 numpy 模块之后可以在 Lib\site-packages 安装目录下找到名为 numpy 的文件夹,它就是安装的 numpy 模块(其实就是一个包),它所包含的内容如图 1 所示。

图 1 numpy包(模块)


从图 1 可以看出,在 numpy 包(模块)中,有必须包含的 __init__.py 文件,还有 matlib.py 等模块源文件以及 core 等子包(也是模块)。这正印证了我们刚刚讲过的,包的本质依然是模块,包可以包含包。

Python 库:相比模块和包,库是一个更大的概念,例如在 Python 标准库中的每个库都有好多个包,而每个包中都有若干个模块。

三、导入模块

3.1 创建模块

模块即是 .py 文件

3.2 import 语句

使用“import fk_module” 导入模块的本质 就是,将 fk_module.py 中的全部代码加载到内存并执行,然后将整个模块内容赋值给与模块同名的变量,该变量的类型是 module,而在该模块中定义的所有程序单元都相当于该 module 对象的成员。

模块定义好后,我们可以使用 import 语句来引入模块,语法如下:

import module1[, module2[,... moduleN]]

比如要引用模块 math,就可以在文件最开始的地方用 import math 来引入。在调用 math 模块中的函数时,必须这样引用:

模块名.函数名

当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support.py,需要把命令放在脚本的顶端:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 导入模块
import support
# 现在可以调用模块里包含的函数了
support.print_func("Runoob")

以上实例输出结果:

Hello : Runoob

一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。

3.3 from…import 语句

使用“from fk_module import name, hello” 导入模块中成员的本质 就是将 fk_module.py 中的全部代码加载到内存并执行,然后只导入指定变量、函数等成员单元,并不会将整个模块导入,因此上面程序在输出 fk_module 时将看到错误提示: name 'fk module' is not defined

Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中。语法如下:

from modname import name1[, name2[, ... nameN]]

例如,要导入模块 fib 的 fibonacci 函数,使用如下语句:

from fib import fibonacci

这个声明不会把整个 fib 模块导入到当前的命名空间中,它只会将 fib 里的 fibonacci 单个引入到执行这个声明的模块的全局符号表。

3.4 from…import* 语句(此方式不推荐)

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

from modname import *

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

例如我们想一次性引入 math 模块中所有的东西,语句如下:

from math import *

在导入模块后,可以在模块文件所在目录下看到一个名为“__pycache__”的文件夹,打开该文件夹,可以看到 Python 为每个模块都生成一个 *.cpython-36.pyc 文件,比如 Python 为 fk_module 模块生成一个 fk_ module.cpython-36.pyc 文件,该文件其实是 Python 为模块编译生成的字节码,用于提升该模块的运行效率。

四、Python创建包、导入包

4.1 Python创建包

包其实就是文件夹,更确切的说,是一个包含“__init__.py”文件的文件夹。因此,如果我们想手动创建一个包,只需进行以下 2 步操作:

  • 新建一个文件夹,文件夹的名称就是新建包的包名;
  • 在该文件夹中,创建一个 __init__.py 文件(前后各有 2 个下划线‘_’),该文件中可以不编写任何代码。当然,也可以编写一些 Python初始化代码,则当有其它程序文件导入包时,会自动执行该文件中的代码。
  • 例如,现在我们创建一个非常简单的包,该包的名称为 my_package,可以仿照以上 2 步进行:

  • 创建一个文件夹,其名称设置为 my_package;
  • 在该文件夹中添加一个 __init__.py 文件,此文件中可以不编写任何代码。不过,这里向该文件编写如下代码:
  • http://c.biancheng.net/ 创建第一个 Python 包 print ( ' http://c.biancheng.net/python/ ' )
  • 可以看到,__init__.py 文件中,包含了 2 部分信息,分别是此包的说明信息和一条 print 输出语句。

  • 由此,我们就成功创建好了一个 Python 包。

    创建好包之后,我们就可以向包中添加模块(也可以添加包)。这里给 my_package 包添加 2 个模块,分别是 module1.py、module2.py,各自包含的代码分别如下所示(读者可直接复制下来):

    #module1.py模块文件
    def display(arc):
        print(arc)
    #module2.py 模块文件
    class CLanguage:
        def display(self):
            print("http://c.biancheng.net/python/")

    现在,我们就创建好了一个具有如下文件结构的包:

    my_package
         ┠── __init__.py
         ┠── module1.py
         ┗━━  module2.py

    当然,包中还有容纳其它的包,不过这里不再演示,有兴趣的读者可以自行调整包的结构。

    4.2 Python导入包

    通过前面的学习我们知道,包其实本质上还是模块,因此导入模块的语法同样也适用于导入包。无论导入我们自定义的包,还是导入从他处下载的第三方包,导入方法可归结为以下 3 种:

  • import 包名[.模块名 [as 别名]]
  • from 包名 import 模块名 [as 别名]
  • from 包名.模块名 import 成员名 [as 别名]
  • 用 [] 括起来的部分,是可选部分,即可以使用,也可以直接忽略。

    注意,导入包的同时,会在包目录下生成一个含有 __init__.cpython-36.pyc 文件的 __pycache__ 文件夹。

    1) import 包名[.模块名 [as 别名]]

    以前面创建好的 my_package 包为例,导入 module1 模块并使用该模块中成员可以使用如下代码:

    import my_package.module1
    my_package.module1.display("http://c.biancheng.net/java/")

    运行结果为:

    http://c.biancheng.net/java/

    可以看到,通过此语法格式导入包中的指定模块后,在使用该模块中的成员(变量、函数、类)时,需添加“包名.模块名”为前缀。当然,如果使用 as 给包名.模块名”起一个别名的话,就使用直接使用这个别名作为前缀使用该模块中的方法了。

    import my_package.module1 as module
    module.display("http://c.biancheng.net/python/")

    程序执行结果为:

    http://c.biancheng.net/python/

    另外,当直接导入指定包时,程序会自动执行该包所对应文件夹下的 __init__.py 文件中的代码。

    import my_package
    my_package.module1.display("http://c.biancheng.net/linux_tutorial/")

    直接导入包名,并不会将包中所有模块全部导入到程序中,它的作用仅仅是导入并执行包下的 __init__.py 文件,因此,运行该程序,在执行 __init__.py 文件中代码的同时,还会抛出 AttributeError 异常(访问的对象不存在)。

    http://c.biancheng.net/python/
    Traceback (most recent call last):
      File "C:\Users\mengma\Desktop\demo.py", line 2, in <module>
        my_package.module1.display("http://c.biancheng.net/linux_tutorial/")
    AttributeError: module 'my_package' has no attribute 'module1'

    我们知道,包的本质就是模块,导入模块时,当前程序中会包含一个和模块名同名且类型为 module 的变量,导入包也是如此:

    import my_package
    print(my_package)
    print(my_package.__doc__)
    print(type(my_package))

    运行结果为:

    http://c.biancheng.net/python/
    <module 'my_package' from 'C:\\Users\\mengma\\Desktop\\my_package\\__init__.py'>
    http://c.biancheng.net/
    创建第一个 Python 包
    <class 'module'>

    2) from 包名 import 模块名 [as 别名]

    仍以导入 my_package 包中的 module1 模块为例,使用此语法格式的实现代码如下:

    from my_package import module1
    module1.display("http://c.biancheng.net/golang/")

    运行结果为:

    http://c.biancheng.net/python/
    http://c.biancheng.net/golang/

    可以看到,使用此语法格式导入包中模块后,在使用其成员时不需要带包名前缀,但需要带模块名前缀。

    当然,我们也可以使用 as 为导入的指定模块定义别名。

    from my_package import module1 as module
    module.display("http://c.biancheng.net/golang/")

    此程序的输出结果和上面程序完全相同。

    同样,既然包也是模块,那么这种语法格式自然也支持 from 包名 import * 这种写法,它和 import 包名 的作用一样,都只是将该包的 __init__.py 文件导入并执行。

    3) from 包名.模块名 import 成员名 [as 别名]

    此语法格式用于向程序中导入“包.模块”中的指定成员(变量、函数或类)。通过该方式导入的变量(函数、类),在使用时可以直接使用变量名(函数名、类名)调用。

    from my_package.module1 import display
    display("http://c.biancheng.net/shell/")

    运行结果为:

    http://c.biancheng.net/python/
    http://c.biancheng.net/shell/

    当然,也可以使用 as 为导入的成员起一个别名。

    from my_package.module1 import display as dis
    dis("http://c.biancheng.net/shell/")

    该程序的运行结果和上面相同。

    另外,在使用此种语法格式加载指定包的指定模块时,可以使用 * 代替成员名,表示加载该模块下的所有成员。

    from my_package.module1 import *
    display("http://c.biancheng.net/python")

    4.3 Python __init__.py作用

    前面章节中,已经对包的创建和导入进行了详细讲解,并提供了大量的实例,这些实例虽然可以正常运行,但存在一个通病,即为了调用包内模块的成员(变量、函数或者类),代码中包含了诸多的 import 导入语句,非常繁琐。

    要解决这个问题,就需要搞明白包内 __init__.py 文件的作用和用法。

    我们知道,导入包就等同于导入该包中的 __init__.py 文件,因此完全可以在 __init__.py 文件中直接编写实现模块功能的变量、函数和类,但实际上并推荐大家这样做,因为包的主要作用是包含多个模块。因此 __init__.py 文件的主要作用是导入该包内的其他模块。

    也就是说,通过在 __init__.py 文件使用 import 语句将必要的模块导入,这样当向其他程序中导入此包时,就可以直接导入包名,也就是使用 import 包名 (或 from 包名 import * )的形式即可。

    上节中,我们已经创建好的 my_package 包,该包名包含 module1 模块、module2 模块和 __init__.py 文件。现在向 my_package 包的 __init__.py 文件中编写如下代码:

    # 从当前包导入 module1 模块
    from . import module1
    #from .module1 import * 
    # 从当前包导入 module2 模块
    #from . import module2
    from .module2 import * 

    可以看到,在 __init__.py 文件中用点(.)来表示当前包的包名,除此之外,from import 语句的用法和在程序中导入包的用法完全相同。

    总的来说,__init__.py 文件是通过如下 2 种方式来导入包中模块的:

    # 从当前包导入指定模块
    from . import 模块名
    # 从.模块名 导入所有成员到包中
    from .模块名 import *

    第 1 种方式用于导入当前包(模块)中的指定模块,这样即可在包中使用该模块。当在其他程序使用模块内的成员时,需要添加“包名.模块名”作为前缀,例如:

    import my_package
    my_package.module1.display("http://c.biancheng.net/python/")

    运行结果为:

    http://c.biancheng.net/python/

    第 2 种方式表示从指定模块中导入所有成员,采用这种导入方式,在其他程序中使用该模块的成员时,只要使用包名作为前缀即可。例如如下程序:

    import my_package
    clangs = my_package.CLanguage()
    clangs.display()

    运行结果为:

    http://c.biancheng.net/python/

    五、搜索路径及Python找不到指定模块的解决办法

    通常情况下,当使用 import 语句导入模块后,Python 会按照以下顺序查找指定的模块文件:

  • 在当前目录,即当前执行的程序文件所在目录下查找;
  • 到 PYTHONPATH(环境变量)下的每个目录中查找;
  • 到 Python 默认的安装目录下查找。
  • 以上所有涉及到的目录,都保存在标准模块 sys 的 sys.path 变量中,通过此变量我们可以看到指定程序文件支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常。

    解决“Python找不到指定模块”的方法有 3 种,分别是:

  • 向 sys.path 中临时添加模块文件存储位置的完整路径;
  • 将模块放在 sys.path 变量中已包含的模块加载路径中;
  • 设置 path 系统环境变量。
  • 六、PYTHONPATH 变量

    作为环境变量,PYTHONPATH 由装在一个列表里的许多目录组成。PYTHONPATH 的语法和 shell 变量 PATH 的一样。

    在 Windows 系统,典型的 PYTHONPATH 如下:

    set PYTHONPATH=c:\python27\lib;

    在 UNIX 系统,典型的 PYTHONPATH 如下:

    set PYTHONPATH=/usr/local/lib/python

    七、命名空间和作用域

    变量是拥有匹配对象的名字(标识符)。命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。

    一个 Python 表达式可以访问局部命名空间和全局命名空间里的变量。如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。

    每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。

    Python 会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的。

    因此,如果要给函数内的全局变量赋值,必须使用 global 语句。

    global VarName 的表达式会告诉 Python, VarName 是一个全局变量,这样 Python 就不会在局部命名空间里寻找这个变量了。

    例如,我们在全局命名空间里定义一个变量 Money。我们再在函数内给变量 Money 赋值,然后 Python 会假定 Money 是一个局部变量。然而,我们并没有在访问前声明一个局部变量 Money,结果就是会出现一个 UnboundLocalError 的错误。取消 global 语句前的注释符就能解决这个问题。

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    Money = 2000
    def AddMoney():
       # 想改正代码就取消以下注释:
       # global Money
       Money = Money + 1
    print Money
    AddMoney()
    print Money

    八、dir()函数

    dir() 函数一个排好序的字符串列表,内容是一个模块里定义过的名字。

    返回的列表容纳了在一个模块里定义的所有模块,变量和函数。如下一个简单的实例:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 导入内置math模块
    import math
    content = dir(math)
    print content;

    以上实例输出结果:

    ['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan', 
    'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 
    'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
    'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 
    'sqrt', 'tan', 'tanh']

    在这里,特殊字符串变量__name__指向模块的名字,__file__指向该模块的导入文件名。

    九、globals() 和 locals() 函数

    根据调用地方的不同,globals() 和 locals() 函数可被用来返回全局和局部命名空间里的名字。

    如果在函数内部调用 locals(),返回的是所有能在该函数里访问的命名。

    如果在函数内部调用 globals(),返回的是所有在该函数里能访问的全局名字。

    两个函数的返回类型都是字典。所以名字们能用 keys() 函数摘取。

    十、reload() 函数

    当一个模块被导入到一个脚本,模块顶层部分的代码只会被执行一次。

    因此,如果你想重新执行模块里顶层部分的代码,可以用 reload() 函数。该函数会重新导入之前导入过的模块。语法如下:

    reload(module_name)

    在这里,module_name要直接放模块的名字,而不是一个字符串形式。比如想重载 hello 模块,如下:

    reload(hello)

    十一、Python中的包

    包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。

    简单来说,包就是文件夹,但该文件夹下必须存在 __init__.py 文件, 该文件的内容可以为空。 __init__.py 用于标识当前文件夹是一个包。

    考虑一个在 package_runoob 目录下的 runoob1.py、runoob2.py、__init__.py 文件,test.py 为测试调用包的代码,目录结构如下:

    test.py
    package_runoob
    |-- __init__.py
    |-- runoob1.py
    |-- runoob2.py

    源代码如下:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    def runoob1():
       print("I'm in runoob1")
    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    def runoob2():
       print(I'm in runoob2)

    现在,在 package_runoob 目录下创建 __init__.py

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    if __name__ == '__main__':
        print('作为主程序运行')
    else:
        print('package_runoob 初始化')

    然后我们在 package_runoob 同级目录下创建 test.py 来调用 package_runoob

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 导入 Phone 包
    from package_runoob.runoob1 import runoob1
    from package_runoob.runoob2 import runoob2
    runoob1()
    runoob2()

    以上实例输出结果:

    package_runoob 初始化
    I'm in runoob1
    I'm in runoob2

    如上,为了举例,我们只在每个文件里放置了一个函数,但其实你可以放置许多函数。你也可以在这些文件里定义Python的类,然后为这些类建一个包。

    参考:Python 模块 - https://www.runoob.com/python/python-modules.html