Python基础入门笔记:模块与包
模块与包
2.1 模块
2.1.1 什么是模块
在 Python 中,一个 .py
文件就称之为一个模块(Module)。
我们学习过函数,知道函数是实现一项或多项功能的一段程序 。其实模块就是函数功能的扩展。为什么这么说呢?那是因为模块其实就是实现一项或多项功能的程序块。
通过上面的定义,不难发现,函数和模块都是用来实现功能的,只是模块的范围比函数广,在模块中,可以有多个函数。
模块的好处:
- 模块使用的最大好处是大大提高了代码的可维护性,当然,还提高了代码的复用性。
- 使用模块还可以避免函数名和变量名冲突,相同名字的变量完全可以分别存在不同的模块中。
PS:但是也要注意,变量的名字尽量不要与内置函数名字冲突。常见的内置函数:链接直达
再这也顺带先延伸下关于包的内容吧:
当编写的模块多了,模块的名字重复的概率就增加了。如何解决这个问题呢?
Python 引入了按目录来组织模块,称为包(Package),比如:
extensions ├─ __init__.py ├─ dog.py └─ cat.py 复制代码
现在
dog.py
模块的名字就变成了extensions.dog
。PS:请注意,每一个 package 目录下面都会有一个
__init__.py
的文件,这个文件是必须有的,否则, Python 就把这个目录当成普通目录,而不是一个 package directory。另外如何使用包中的模块(Module)呢?如下编写一个
dog.py
模块:#!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' __author__ = 'jack guo' import sys def shout(): args = sys.argv if len(args)==1: print('Hello, I'm afei, welcome to world!') elif len(args)==2: print('Hello, %s!' % args[1]) else: print('Yes,sir') if __name__=='__main__': shout() 复制代码
解释下:
第1行注释可以让dog.py文件直接在linux上运行; 第2行注释表示.py文件本身使用标准UTF-8编码; 第4行表示模块的文档注释; 第6行表示模块的作者; 注意最后两行代码,当我们调试dog.py时,shout()会调用,当在其他模块导入dog.py时,shout()不执行。 复制代码
模块的一种标准模板:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' __author__ = 'jack guo' 复制代码
以上是模块的标准模板,当然,你也可以不这样做。
2.1.2 模块的导入
导入模块我们使用关键字 import
,语法格式如下:import module1[, module2[,... moduleN]
如:import math
导入标准模块中的 math 模块。
一个模块只会被导入一次,不管你执行了多少次 import
。这样可以防止导入模块被一遍又一遍地执行。
Python 解释器是怎样找到对应的文件的呢?
搜索路径:由一系列目录名组成的。Python 解释器就依次从这些目录中去寻找所引入的模块。这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。搜索路径是在 Python 编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在 sys 模块中的 path 变量 。可以打印出来:
import sys
print(sys.path)
复制代码
2.1.3 导入模块中的属性和方法及调用
①导入模块的方法
import 模块名
import 模块名 as 新名字
from 模块名 import 函数名
:大型项目中应尽量避免使用此方法,除非你非常确定不会造成命名冲突;它有一个好处就是可直接使用function()
而不用加module.function()
了。
PS1:导入模块并不意味着在导入时执行某些操作,它们主要用于定义,比如变量、函数和类等。
PS2:可以使用
from ··· import *
语句把某个模块中的所有方法属性都导入。
②模块中变量、函数以及类的属性和方法的调用
module.variable
module.function()
module.class.variable
2.1.4 模块的搜索路径sys模块的使用)
(1)程序所在目录
(2)标准库的安装路径
(3)操作系统环境变量 PYTHONPATH 指向的路径
- 获得当前 Python 搜索路径的方法:
import sys print(sys.path) 复制代码
输出:
['D:\\workspace_pycharm', 'D:\\workspace_pycharm', 'D:\\python-practice', 'D:\\devInstall\\devPython\\Python36\\python36.zip', 'D:\\devInstall\\devPython\\Python36\\DLLs', 'D:\\devInstall\\devPython\\Python36\\lib', 'D:\\devInstall\\devPython\\Python36', 'D:\\devInstall\\devPython\\Python36\\lib\\site-packages'] 复制代码
- sys 模块的 argv 变量的用法:
- sys 模块有一个
argv
(argument values) 变量,用 list 存储了命令行的所有参数。 argv
至少有一个元素,因为第一个元素永远都是.py
文件的名称。
$ python solve.py 0 # 命令行语句 # 获得argv变量的值 sys.argv = ['solve.py', '0'] sys.argv[0] = 'solve.py' sys.argv[1] = '0' 复制代码
- sys 模块有一个
2.1.5 主模块和非主模块
在 Python 函数中,如果一个函数调用了其他函数完成一项功能,我们称这个函数为主函数,如果一个函数没有调用其他函数,我们称这种函数为非主函数。主模块和非主模块的定义也类似,如果一个模块被直接使用,而没有被别人调用,我们称这个模块为主模块,如果一个模块被别人调用,我们称这个模块为非主模块。
怎么区分主模块和非主模块呢?
可以利用 __name__
属性。如果一个属性的值是 __main__
,那么就说明这个模块是主模块,反之亦然。但是要注意了:这个 __main__
属性只是帮助我们判断是否是主模块,并不是说这个属性决定他们是否是主模块,决定是否是主模块的条件只是这个模块有没有被人调用。如下:
if __name__ == '__main__':
print('main')
else:
print('not main')
复制代码
如果输出结果为 main 则该模块为主模块。
!!!补充: 在初学 Python 过程中,总能遇到 if __name__ == 'main'
语句,我们正好来好好了解下。
先举例子,假如 A.py 文件内容如下:
def sayhello():
print('Hello!')
print('Hi!')
print(__name__)
复制代码
输出结果:
Hi!
__main__
复制代码
结果很简单,说明在运行 A.py 本身文件时,变量__name__
的值是__main__
。
现有个 B.py 文件,代码如下:
import A
A.sayhello()
print('End')
复制代码
可以看到,在 B.py 文件中,模块 A 被导入,运行结果如下:
Hi!
A
Hello!
End
复制代码
这里涉及一些语句运行顺序问题,在 B.py 文件中,模块 A 中的 sayhello 函数是调用时才执行的,但是 A 中的 print 语句会立刻执行(因为没有缩进,因此与def是平行级别的)。因此会先依次执行:
print('Hi!')
print(__name__)
复制代码
然后执行:
A.sayhello()
print('End')
复制代码
运行结果中Hi!
对应于 A 模块中的 print('Hi!')
,而结果 A 对应于 print(__name__)
,可见当在 B 文件中调用 A 模块时,变量__name__
的值由__main__
变为了模块 A 的名字。
这样的好处是我们可以在 A.py 文件中进行一些测试,而避免在模块调用的时候产生干扰,比如将 A 文件改为:
def sayhello():
print('Hello!')
print('Hi!')
print(__name__)
if __name__ == '__main__':
print('I am module A')
复制代码
再次单独运行 A.py 文件时,结果中会多出I am module A
语句:
Hi!
__main__
I am module A
复制代码
而运行 B.py 文件,即调用 A 模块时,却不会显示该语句:
Hi!
A
Hello!
End
复制代码
简短总结下:
模块属性
__name__
,它的值由 Python 解释器设定。如果 Python 程序是作为主程序调用,其值就设为__main__
,如果是作为模块被其他文件导入,它的值就是其文件名。每个模块都有自己的私有符号表,所有定义在模块里面的函数把它当做全局符号表使用。
2.2 包
2.2.1 什么是包
我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。但是这里也有个问题,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python 又引入了按目录来组织模块的方法,称为包(Package)。
仔细观察的人,基本会发现,每一个包目录下面都会有一个 __init__.py
的文件。这个文件是必须的,否则,Python 就把这个目录当成普通目录,而不是一个包。 __init__.py
可以是空文件,也可以有 Python 代码,因为 __init__.py
本身就是一个模块,而它对应的模块名就是它的包名。
2.2.2 包的定义和优点
- Python 把同类的模块放在一个文件夹中统一管理,这个文件夹称之为一个包。
- 如果把所有模块都放在一起显然不好管理,并且有命名冲突的可能。
- 包其实就是把模块分门别类地存放在不同的文件夹,然后把各个文件夹的位置告诉Python。
- Python 的包是按目录来组织模块的,也可以有多级目录,组成多级层次的包结构。
2.2.3 包的创建
- 创建一个文件夹,用于存放相关的模块,文件夹的名字即为包的名字。
- 在文件夹中创建一个
__init__.py
的模块文件,内容可以为空(普通文件夹和包的区别)。 - 将相关模块放入文件夹中
2.3.4 包的存放路径及包中模块的导入与调用
①包的存放
- 如果不想把相关的模块文件放在所创建的文件夹中,那么最好的选择就是:放在默认的
site-packages
文件夹里,因为它就是用来存放你的模块文件的。 sys.path.append(‘模块的存放位置’)
只是在运行时生效,运行结束后失效。- 将包的存放路径加入用户系统环境变量中的 PYTHONPYTH 中去,这样在任何位置都可以调用包了(推荐)。
②包中模块的导入
import 包名.模块名
import 包名.模块名 as 新名字
from 包名 import 模块名
③包中模块的变量、函数以及类的属性和方法的调用
package.module.variable
package.module.function()
package.module.class.variable
2.3 作用域
学习过 Java 的同学都知道,Java 的类里面可以给方法和属性定义公共的( public )或者是私有的 ( private ),这样做主要是为了我们希望有些函数和属性能给别人使用或者只能内部使用。 通过学习 Python 中的模块,其实和 Java 中的类相似,那么我们怎么实现在一个模块中,有的函数和变量给别人使用,有的函数和变量仅仅在模块内部使用呢?
在 Python 中,是通过 _
前缀来实现的。正常的函数和变量名是公开的(public),可以被直接引用,比如:abc
,ni12
,PI
等。
类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的 __name__
就是特殊变量,还有 __author__
也是特殊变量,用来标明作者。注意,我们自己的变量一般不要用这种变量名;类似_xxx
和 __xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如 _abc
,__abc
等.
注意:这里是说不应该,而不是不能。因为 Python 种并没有一种方法可以完全限制访问 private 函数或变量,但是,从编程习惯上不应该引用 private 函数或变量。
作者:Jaybo