1.12 模块和包

4/12/2018 Python

# 模块化编程

# 简介

Python 有时候称为胶水语言,就是因为它有强大的可扩展性,这个扩展性就是用模块实现的。

模块其实就是一个以 .py 结尾的 Python 文件,这个文件中可以包含变量、函数、类等。

这个模块可以包含实现了一个或者多个功能的代码。

模块可以在其他 Python 文件中使用,可以通过网络进行传播。

这样的话,如果想在你的程序中实现某些功能,其实网络的其他程序猿已经给你写好了,下载下来,安装到自己的环境下,就可以使用了。

# 模块化编程

模块化编程是指将大型,笨拙的编程任务分解为单独的,更小的,更易于管理的子任务或模块的过程。然后可以像构建块一样拼凑单个模块以创建更大的应用程序。

在大型应用程序中模块化代码有几个优点:

  • **简单性:**模块通常只关注问题的一小部分,而不是关注当前的整个问题。如果您正在处理单个模块,那么您的头脑中要思考的将有一个较小的问题范围。这使得开发更容易,更不容易出错。
  • **可维护性:**模块通常设计为能够在不同的问题域之间实施逻辑边界。如果以最小化相互依赖性的方式编写模块,则对单个模块的修改将对程序的其他部分产生影响的可能性降低。(您甚至可以在不了解该模块之外的应用程序的情况下对模块进行更改。)这使得许多程序员团队在大型应用程序上协同工作更加可行。
  • **可重用性:**单个模块中定义的功能可以通过应用程序的其他部分轻松地重用。这消除了重新创建重复代码的需要。
  • 范围:模块通常定义一个单独的命名空间,这有助于避免程序的不同区域中的变量名之间的冲突。

# 一、模块分类

# 实现方式分类

实际上有两种不同的方法可以在Python中定义模块

  1. 模块可以用Python本身编写。
  2. 模块可以用C编写并在运行时动态加载,就像re正则表达式)模块一样。
  3. 一个内置的模块,本质上已经包含在了 Python 解释器中,像itertools模块 (opens new window)

以上情况下,模块的内容都以相同的方式访问:使用import语句

在这里,重点将主要放在用Python编写的模块上。用Python编写的模块的妙处在于它们的构建极其简单。您需要做的就是创建一个包含合法Python代码的文件,然后为该文件命名并带有 .py 扩展名即可。

# 归属分类

模块还可以分为

  • 内置模块 ,就是 python 解释器中自带的. 如: os re itertools

  • 第三方模块, 这些模块需要自己安装,就像是在 Linux 系统中安装软件一样。

  • 自定义模块, 这个就是自己编写的模块。

# 二、模块的安装

# 安装方法

# 1. pip3 工具安装

例如下面的示例是安装用于执行远程主机命令的模块 paramiko

注意: pip3 是 bash 环境下的命令

$ pip3 install paramiko
1

python2.x 使用 pip python3.x 使用 pip3 当然这也不是固定的,比如你给 pip3 定义了一个别名 pip

# 2. 源码安装

源码安装就是,从网络上下载没有封装的 python 文件的源码,之后在本地执行其源码中的 setup.py 文件进行安装。

模块的源码一般都有一个主目录,主目录中包含了一个到多个子目录和文件。 但是主目录下一定有一个 setup.py 的文件,这个是源码安装的入口文件,就是需要执行这个文件并且传入一个 install 参数进行源码安装。

示例:

a. 下载源码包

wget https://files.pythonhosted.org/packages/4a/1b/9b40393630954b54a4182ca65a9cf80b41803108fcae435ffd6af57af5ae/redis-3.0.1.tar.gz

1
2

b. 解压源码包

tar -xf redis-3.0.1.tar.gz
1
  1. 进入模块源码的主目录,并安装源码包

千锋云计算杨哥团队@shark

千锋云计算杨哥团队@shark

上面表示安装成功

# 三、自定义模块

有的情况下,是需要自己编写一些模块的,这种就是自定义模块了。

示例:

some_mod.py

x = 10

li = ['shark', 18]

def foo():
    return 30

1
2
3
4
5
6
7

# 四、模块的使用

使用模块需要先导入模块名。

模块名就是把 .py 去掉后的文件名。比如 some_mod.py 的模块名就是 some_mod

# 1. 导入模块

import  some_mod
1

# 2. 使用模块中的对象

要想使用模块中的变量名或者函数名等,只需要使用 模块名.变量名 的方式即可。

例如,下面是使用的了 some_mod 模块中的 foo 函数。

import  some_mod

some_mod.foo()

1
2
3
4

# 3. 更多模块导入的方式

a. 从模块中导入其中的一个对象

from some_mod  import  x
1

b. 从模块中导入多个对象

from some_mod import x, foo
1

c. 从模块中导入全部的对象, 不建议这么做, 应该视使用模块中的对象的情况而定

from some_mod import *
1

# 导入模块时模块的代码会自动被执行一次,在一个程序中多次导入同一个模块,此模块的代码仅会运行一次。

[root@kube-master py3]# cat some_mod.py
print("hello shark")
[root@kube-master py3]# cat t.py
import some_mod
import some_mod
[root@kube-master py3]# python t.py
hello shark
1
2
3
4
5
6
7

# 二、包

包就是包含了一个 __init__.py 文件的文件夹,这个文件夹下可以有更多的目录或文件。就是说,包里可以用子包或单个 .py 的文件。

其实包也是模块,就是说包和单一的 .py 文件统称为模块。

# 包的目录结构

千锋云计算杨哥团队@shark

# 文件 __init__.py

__init__.py 文件,在 Python3.x 中可以没有,但是在 Python2.x 中必须有。

文件中可以有代码,也可以是个空文件,但是文件名不能是其他的。

到导入包的时候,此文件假如存在,会以此文件去见包的名称空间。

也就是说,导入如包的时候,只要在 __init__.py 文件中的名称才可以生效。否则,即使是一个模块在包目录下面也不会被导入到内存中,也就不会生效。

# 使用包

示例包目录机构

使用包也需要导入

a. 单独导入包

import package  # 注意这样不会导入其下面的模块和子包
1

b. 从包中导入下面的模块

from package import t
1

c. 从包中导入下面的子包,注意这不会导入子包下面的任何模块

from package import subpkg
1

d. 从包的子包中导入子包的模块

from package.subpkg import som_mod
1

e. 从包或子包的模块中导入具体的对象

from package.t  import foo

from package.subpkg.som_mod import x

from package.t  import x  as y   # 把 x 导入后起个别名 y
1
2
3
4
5

记住一点:不论是导入包还是模块,从左向右的顺序来说,最后一个点儿的左边一定是一个包名,而不能是一个模块名

下面是错误的

import package.t.foo
from package.subpkg import som_mod.x
1
2

# 三模块的内置变量 __name__

每个 .py 文件都有一个变量名 __name__, 这个变量名的值会根据这个文件的用途不同而随之变化。

  1. 当文件作为模块被其他文件使用时,__name__ 的值是这个文件的模块名

  2. 当文件作为脚本(就是作为可执行文件)使用时,__name__ 的值是字符串类型的 '__main__'

通常你会看到一些 Python 脚本中会有类似下面的代码:

some_script.py

def foo():
    pass

def func():
    pass

def main():
    foo()
    func()

if __name__ == '__main__':
       main()
1
2
3
4
5
6
7
8
9
10
11
12

使用这个脚本

python3 some_script.py
1

这样执行这个脚本的话,其内置的 __name__ 变量的值就是字符串的 '__main__'。 这样的话, if 的判断添加就会达成,从而其语句下面的代码就会执行, main() 函数就会被调用 。

# 模块名包名的搜索路径(扩展)

这个就像是在 Linux 系统中,当执行一个命令时候,系统会查找这个命令的二进制文件一样。Linux 系统是从环境变量 PATH 中查找。

# 查找顺序

当你导入模块或者包的时候, 查找模块或包的顺序:

  1. 系统会从内存中查找,看是否之前已经导入过此模块。

  2. 假如第一次导入,内存中就不会存在,这时再从当前目录下查找。

  3. 最再从sys.path 输出的值中的路径里查找模块名或者包名。

都找不到,就会报错,并且整个程序会退出

import sys

print(sys.path)
1
2
3

sys.path 输出的值是一个 Python 的列表,这个列表我们可以对其修改的。

比如我们可以把某个文件的路径添加到此列表中,通常会这么做。

run.py

import os,sys

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

sys.path.insert(0, BASE_DIR)
1
2
3
4
5

# 相对导入

相对导入就是从当前目录下导入模块

from  . import som_mod
1