此为学习笔记的整理与记录,方便个人查阅、回顾、复习
文章目录
- 一、模块
- (一)什么是模块?
- (二)模块的导入
- (三)if __name __ == ' __main __':
- (四)模块的路径
- 二、 包
- (一)什么是包?
- (二)包的导入
- 三、 库
- 附
- 一、模块导入示例
- 二、__init __.py模块
- 参考
一、模块
(一)什么是模块?
就是扩展名为.py的文件,里面定义了一些函数和变量。
通过使用模块,不仅可以有效地避免命名空间的冲突,还可以将一个较大的程序分为多个文件,提升代码的可维护性和可重用性。
(二)模块的导入
【点击查看模块导入示例】
- import 模块名 [as 别名1], 模块名2 [as 别名2],…
- from 模块名 import 成员名1 [as 别名1],成员名2 [as 别名2],…
其中,用 [] 括起来的部分,可以使用,也可以省略。
注意!尽量不要使用from demo import *的方式进行导入,否则很容易出现名称重复的情况。
例如,如果有两个模块里面有相同的成员,或者你自己定义了某一个对象和demo module里面的成员(例如函数)重名了,运行的时候会出问题。因为此时我们无法确认,我们所指的是自己定义的对象还是demo module里面的成员。
(三)if __name __ == ’ __main __':
在完成一个模块的编写之前,我们一般会对模块中的功能进行测试,看看各项功能是否正常运行。对于这些测试的代码,我们希望只在直接运行这个py文件的时候执行,在用其他的程序import这个模块的时候不要执行。这个时候就要借助Python内置的__name__变量。
- 当一个py文件被直接运行的时候,__ name__变量的值为__main__。
- 当一个py文件被import到其他程序的时候,这个py文件里面的__name__变量的值为这个py文件的名字。
利用__name__变量的这个特点,结合一个if语句,让我们import某一个模块的时候只用到它提供的功能,而不要运行里面用来测试的代码
(四)模块的路径
对于用import语句导入的模块,Python会按照下面的路径列表顺序地查找我们需要的模块:
- 当前的工作目录
- PYTHONPATH(环境变量)中的每一个目录
- Python 默认的安装目录
如果我们写的模块没有放在上述三类路径,那么在导入时,Python解释器提示找不到这个模块 ModuleNotFoundError: No module named ‘模块名’。
▲解决方法
▲ 向sys.path变量中临时添加模块文件所在的完整路径
import sys
sys.path.append(r'D:\xxx\yyy')
▲ 将模块移动到sys.path 变量中已包含的路径中
▲ 修改path 系统的环境变量
其中,环境变量又分为用户变量(仅对当前用户生效)和系统变量(对系统里所有用户均生效)。Python在使用path变量时,会先按照系统path变量找,然后按照用户path变量的路径找。通常情况下,设置用户path变量即可
二、 包
(一)什么是包?
在模块之上的概念,为了方便管理而将文件进行打包。包目录下第一个文件必须是__init__.py,否则就是普通股的文件夹目录。然后就是一些模块文件和子目录,假如子目录中也有__init__.py,那么它就是这个包的子包了。如下图目录结构所示:
package
|- __init__.py
|- subpackage1
|- __init__.py
|- a.py
|- subpackage2
|- __init__.py
|- b.py
|- c.py
Python包具有3个性质
- 它实质上是一个文件夹
- 该文件夹里面一定有__ init__.py模块,其他的模块可以有也可以没有
- 它的本质依然是模块,因此一个包里面还可以装其他的包
(二)包的导入
导入包的方法和导入模块比较类似,只不过由于层级比一般模块多了一级,所以多了一条导入形式。
- import 包名[.模块名 [as 别名]]
- from 包名 import 模块名 [as 别名]
- from 包名.模块名 import 成员名 [as 别名]
其中,用 [] 括起来的部分,可以使用,也可以省略。我们在导入包的时候,实际上是导入了它的__init__.py文件。
三、 库
具有相关功能模块、包的集合。这也是python的一大特色之一,即具有强大的标准库、第三方库以及自定义模块。
- 标准库:下载安装的python里那些自带的模块。
- 第三方库:就是由第三方机构发布的,具有特定功能的模块。
- 自定义模块:自己编写使用的模块。
附
一、模块导入示例
|-main.py
|-arithmetic
|-add.py
|-sub.py
|-mul.py
|-mul.py
文件目录结构如上。其中,main.py是程序入口,用不同的方式来import四则运算的各个子模块,arithmetic模块实现四则运算。
每个文件的代码如下:
#
# @file main.py
#
import arithmetic.add
import arithmetic.sub as sub
from arithmetic.mul import mul
from arithmetic import dev
def letscook(x, y, oper):
r = 0
if oper == "+":
r = arithmetic.add.add(x, y)
elif oper == "-":
r = sub.sub(x, y)
elif oper == "*":
r = mul(x, y)
else:
r = dev.dev(x, y)
print("{} {} {} = {}".format(x, oper, y, r))
-----------------------------------------------------------------
#
# @file add.py
#
def add(a, b):
return a + b
-----------------------------------------------------------------
#
# @file sub.py
#
def sub(a, b):
return a - b
-----------------------------------------------------------------
#
# @file mul.py
#
def mul(a, b):
return a * b
-----------------------------------------------------------------
#
# @file dev.py
#
def dev(a, b):
return a / b
二、__init __.py模块
- 性质
- 它本身是一个模块,可以是一个空文件
- 这个模块的模块名不是__init__,而是这个包的名字,也就是装着__init__.py文件的文件夹的名字
- 作用是将一个文件夹变为一个Python模块
- 它可以不包含代码,不过一般会包含一些Python初始化代码(例如批量导入我们需要用到的模块,这样我们就不用在用到的时候再一一导入),在这个包被import的时候,这些代码会自动被执行【查看示例①】
- 以from 包 import * 导入时,包内的模块是受__init__.py限制的。import会把在__init__.py文件中注册的包的子模块和子包导入到当前作用域中来,如果包中某个模块不在__init__.py文件中,会报错【查看示例②】
示例①
如上述 模块导入示例, 为了使用arithmetic中的某个子模块(main.py中我们展示了四种不同的方式),必须使用非常繁琐地import语句;在使用的时候,也有可能需要使用非常繁琐的表达式;如果在不同的地方使用arithmetic的子模块,都需要写这么繁琐的import或者使用表达式这就是为什么需要利用__init__.py来简化使用。
添加__init__.py文件,文件目录结构如下
|-main.py
|-arithmetic
|-__init__.py
|-add.py
|-sub.py
|-mul.py
|-mul.py
在__init__.py中编写如下代码:
#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev
add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul
dev = arithmetic.dev.dev
同样,对main.py进行了一些适当的修改。
#
# @file main.py
#
import arithmetic as a4
def letscook(x, y, oper):
r = 0
if oper == "+":
r = a4.add(x, y)
elif oper == "-":
r = a4.sub(x, y)
elif oper == "*":
r = a4.mul(x, y)
else:
r = a4.dev(x, y)
print("{} {} {} = {}".format(x, oper, y, r))
x, y = 3, 8
letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")
在__init__.py中, import了arithmetic下的所有子模块,并在__init__.py中给各个子模块的核心功能取了新的名字,作为arithmetic模块的变量。所以在main.py中import了arithmetic模块之后,就可以直接进行使用了。如果使用from arithmetic import * 语句,那么就可以使用add、sub、mul、dev,连a4都省了。
另外,若模块中含有 __all__变量,以“from 模块名 import *”的形式导入该模块时,该文件中只能使用__all__ 列表中指定的成员。(注意!__all__ 变量仅限于在其它文件中以“from 模块名 import *”的方式引入,即__all__ 变量对“import 模块名”、 “from 模块名 import 对象成员”不起作用
)
__ all__ 变量:该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。
沿用上例,修改__ init__.py如下:
#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev
add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul
dev = arithmetic.dev.dev
__all__ = ["add","sub","mul"]
修改main.py文件如下:
#
# @file main.py
#
from arithmetic import *
def letscook(x, y, oper):
r = 0
if oper == "+":
r = add(x, y)
elif oper == "-":
r = sub(x, y)
elif oper == "*":
r = mul(x, y)
else:
r = dev(x, y)
print("{} {} {} = {}".format(x, oper, y, r))
x, y = 3, 8
letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")
运行main.py文件,执行结果如下:NameError: name ‘dev’ is not defined
注意:__ all__ 是模块中提供的变量,如同 __ name__,不仅可以在__ init__.py中运用,也可以在其他普通的模块中运用,因为__ init__.py本身就是一个模块。
示例②
文件目录结构如下
|-main.py
|-arithmetic
|-__init__.py
|-add.py
|-sub.py
|-mul.py
|-mul.py
在__init__.py中编写如下代码:
#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul
add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul
main.py文件如下:
#
# @file main.py
#
from arithmetic import *
def letscook(x, y, oper):
r = 0
if oper == "+":
r = add(x, y)
elif oper == "-":
r = sub(x, y)
elif oper == "*":
r = mul(x, y)
else:
r = arithmetic.dev.dev(x, y)
print("{} {} {} = {}".format(x, oper, y, r))
x, y = 3, 8
letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")
但是没有导入arithmetic的dev模块,因此使用此模块的dev函数时需要用完整路径 arithmetic.dev.dev 进行验证。运行main.py文件,执行结果如下:
3 + 8 = 11
3 - 8 = -5
3 * 8 = 24
Traceback (most recent call last):
File "D:\main.py", line 21, in <module>
letscook(x, y, "/")
File "D:\main.py", line 12, in letscook
r = arithmetic.dev.dev(x, y)
AttributeError: module 'arithmetic' has no attribute 'dev'
- __ init__.py的设计原则
- 不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用__init__.py时违背这个原则,是反其道而为之,就没有必要使用模块了。
- 利用__init__.py对外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,你需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。就像我们的arithmetic例子中,用户只需要知道四则运算有add、sub、mul、dev四个接口,却并不需要知道它们是怎么实现的,也不想去了解arithmetic中是如何组织各个子模块的。由于各个子模块的实现有可能非常复杂,而对外提供的类型和接口有可能非常的简单,我们就可以通过这个方式来对用户隐藏实现,同时提供非常方便的使用。
- 只在__init__.py中导入有必要的内容,不要做没必要的运算。如示例①,import arithmetic语句会执行__ini__.py中的所有代码。如果我们在__init__.py中做太多事情,每次import都会有额外的运算,会造成没有必要的开销。