python包和模块

包和模块的概念

当前目录结构为:

1
2
3
4
5
$ tree .
--------
.
└── mypackage
└── mymodule.py

mypackage是一个文件夹,就是一个包。mymodule.py是一个python文件,就是一个模块。所以简单来说,一个包就是一个文件夹,一个模块就是一个.py文件。其中mymodule.py内容为:print('I am mymodule.py')

运行python

那么运行方式1:(指定文件路径)

1
2
$ python3 mypackage/mymodule.py
I am mymodule.py

运行方式2:(使用-m参数)

1
2
$ python3 -m mypackage.mymodule
I am mymodule.py

方式1简单粗暴,指定python文件路径,将其作为可执行文件执行,是执行入口文件。方式2则使用包的概念,执行mypackage这个包里面的mymodule这个模块,依然是将mymodule.py作为执行入口文件。

补充说明1:使用方式2,需要搜索包和模块, 搜索包和模块的顺序是(查找是否有mypackage的包和mymodule这个模块)

  1. builtin
    a.系统内置包和模块中
  2. sys.path
    a.当前目录下
    b.PYTHONPATH这个环境变量目录下
    c.Python的安装目录下的site_packages中
1
2
3
4
5
6
7
8
9
10
$ tree .
.
└── mypackage
└── __main__.py

$ cat mypackage/__main__.py
print('I am __main__.py')

$ python3 -m mypackage
I am __main__.py

补充说明2:
若-m后面只有一个参数, 比如 python3 -m xxx

  1. 优先将xxx理解为包,若该包下有__main__.py文件,则理解为 python3 -m xxx.__main__
  2. 否则将xxx理解为模块,若当前目录下有xxx.py, 则理解为 python3 xxx.py
  3. 以上两种都不是,则报错。
1
2
3
4
5
6
7
8
9
$ tree .
.
└── mymodule.py

$ cat mymodule.py
print('I am mymodule.py')

$ python3 -m mymodule
I am mymodule.py

总结:【-m的用法】仅仅是比【直接指定路径用法】多个一个【去系统搜索】的步骤而已,其他无区别。

入口文件和模块文件

上面描述的两种运行方式,mymodule.py都是入口文件,而其他python文件就是模块文件了。每次运行,只能有一个文件是入口文件。

若一个文件是入口文件,那么这个.py文件中的__name__ 就是 __main__
若一个文件不是入口文件,比如被别的.py文件引用,那么__name__就是包名.模块名

模块文件如何被导入?

既然其他文件就是模块文件了,那么其他模块怎么运行呢? 需要被入口文件导入。导入有三类:

导入包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ tree .
--------
.
├── mainmodule.py
└── mypackage
└── mymodule.py
-----------------------------------------------------------------------------------------------


$ cat mainmodule.py
-------------------
import mypackage
-----------------------------------------------------------------------------------------------


$ cat mypackage/mymodule.py
---------------------------
print('I am mymodule')
-----------------------------------------------------------------------------------------------


$ python3 mainmodule.py
-----------------------

这样python3 mainmodule.py就执行成功了, 没有报错,但也没有发生任何事情。
注意:
● mainmodule.py所在的目录非常关键,它代表了顶层包的上一层目录。
● 其实导入包并没有任何意义,因为不会发生任何变化,后续你导入模块依然要填写包名。
● 那么为什么导入包语法没错呢,其实它默认导入的还是模块,叫做init.py模块,这样的话就相当于实现了导入模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ tree .
--------
.
├── mainmodule.py
└── mypackage
├── __init__.py
└── mymodule.py
----------------------------------------------------------------------------------------------


$ cat mainmodule.py
-------------------
import mypackage # 相当于默认执行 import mypackage.__init__
----------------------------------------------------------------------------------------------


$ cat mypackage/__init__.py
---------------------------
print('I am __init__.py')
----------------------------------------------------------------------------------------------


$ cat mypackage/mymodule.py
---------------------------
print('I am mymodule')
----------------------------------------------------------------------------------------------


$ python3 mainmodule.py
-----------------------
I am __init__.py

此时你会发现mypackage/init.py中的代码被自动执行了,其实也就是导入init这个模块了。
另外解释一下什么是导入,导入一个模块,就是把该模块中的代码从头到尾执行一遍。

导入一个模块成员,就是声明一下这个模块中的 函数,类,变量等成员,后面可以直接用。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ tree .
--------
.
├── mainmodule.py
└── mypackage
├── __init__.py
└── mymodule.py
----------------------------------------------------------------------------------------------


$ cat mainmodule.py
-------------------
import mypackage # 相当于默认执行 import mypackage.__init__
print('a = ' + str(mypackage.a))
----------------------------------------------------------------------------------------------


$ cat mypackage/__init__.py
---------------------------
print('I am __init__.py')
a = 100
----------------------------------------------------------------------------------------------


$ cat mypackage/mymodule.py
---------------------------
print('I am mymodule')
----------------------------------------------------------------------------------------------


$ python3 mainmodule.py
-----------------------
I am __init__.py
a = 100

但是import mypackage 和 import mypackage.init还是有点区别的。区别在于import mypackage是隐式导入init,所以调用的时候,你不能直接使用init这个模块名,也就是直接用mypackage.a而不是mypackage.init.a

当然init.py也可以像其他普通模块一样被显式导入,行为和同普通模块是一样的。

另外 import mypackage的语法,由于没有导入模块,所以你用的所有模块都必须带上mymodule这个前缀。

导入模块

导入模块的语法是import 包名.模块名。很简单,举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ tree .
.
├── mainmodule.py
└── mypackage
├── __pycache__
│   └── mymodule.cpython-312.pyc
└── mymodule.py

$ cat mainmodule.py
import mypackage.mymodule
print('a=' + str(mypackage.mymodule.a))

$ cat mypackage/mymodule.py
print('I am mymodule')
a = 100

$ python3 mainmodule.py
I am mymodule
a=100

很简单,mainmodule.py中导入了mypackage.mymodule,所以可以引用其中的a变量。

不过如果包层级太多,比如mypackage.mymodule太长了,所以可以一步到位,直接导入模块,比如from mypackage import mymodule,这样的话,直接用mymodule.a就可以了,这种语法就是 from 包名 import 模块名

1
2
from mypackage import mymodule
print('a=' + str(mymodule.a))

这样的话,效果和刚才是一样的。

另外,如果mypackage目录下有很多模块,可以一下子都导入吗?可以的,用*代替就可以了。
不过有个前提,就是mypackage目录下要有个__init__.py, 且内容定义了__all__ = ['mymodule', 'mymodule2', 'mymodule3']:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ tree .
.
├── mainmodule.py
└── mypackage
├── __init__.py
└── mymodule.py

$ cat mainmodule.py
from mypackage import *
print('a=' + str(mymodule.a))

$ cat mypackage/__init__.py
__all__ = ['mymodule']

$ cat mypackage/mymodule.py
print('I am mymodule')
a = 100

$ python3 mainmodule.py
I am mymodule
a=100

不过 all 不能指定为*,从而导入所有包,也是为了保护不被滥用吧。另外也不能 from mypackage import *

导入成员

上面说的是导入包,导入模块,接下来还可以导入成员,这个成员是指模块成员,也就是函数,类,变量等。

注意,成员的导入:from 包名.模块名 import 成员名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ tree .
.
├── mainmodule.py
└── mypackage
└── mymodule.py

$ cat mainmodule.py
from mypackage.mymodule import a
print('a=' + str(a))

$ cat mypackage/mymodule.py
print('I am mymodule')
a = 100

$ python3 mainmodule.py
I am mymodule
a=100

导入成员之后,引用的时候就不要加命名空间了(也就是包名.模块名),直接用成员名就可以了。注意可以 from mymoudle import *, 此时*默认是所有,若mymodule.py中有__all__变量,那么就以__all__为准。

所以总结一下。

导入包:

1
a.import mypackage  # 等价于 import mypackage.__init__,备注:只要涉及包都会自动隐式导入这个

导入模块:

1
2
3
a.import mypackage.mymodule
b.from mypackage import mymodule
c.from mypackage import * # 若存在__init__.py且存在__all__变量,则导入__all__所指定;否则 无

导入成员

1
2
a.from mypackage.mymodule import member1, member2,member3 # 可以用括号或者反斜杠跨行
b.from mypackage.mymodule import * # 若mymodule存在__all__变量,则导入__all__所指定;否则 所有
感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------