模块与包
模块和包是Python项目的核心,这里简单总结一些包和结构和使用。
1. 将模块按层次组织成包
将代码按照一定层次组成成包的形式。一个Python包的形式大致如下:
zzz@ubuntu:~/my_learning$ tree my_project
my_project
├── __init__.py
├── package1
│ ├── a.py
│ ├── b.py
│ └── __init__.py
└── package2
├── c.py
└── __init__.py
2 directories, 6 files
可以看到,包的创建很简单,就是在文件系统中创建一个目录结构,然后在每个目录下定义一个__init__.py
文件。此时,就可以使用 import
语句来进行导入。
zzz@ubuntu:~/my_learning$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_project
>>> import my_project.package1
>>> from my_project.package2 import c
>>>
__init__.py
文件会在模块导入模块时运行,比如,当执行 import my_project.package1.a
时,会在导入 a.py
文件之前,先导入 my_project/__init__.py
和 my_project/package1/__init__.py
文件。__init__.py
文件可以留空,也可以包含代码。
2. 模块的导入
模块的导入最简单的形式就是:
import module as md
from module import A as a
相对名称导入
如果我们需要从包中的一个子模块中导入另一个子模块,可以使用相对名称。
# a.py
class A:
def __str__(self):
return "class A object"
# b.py
from . import a
class B(a.A):
def __str__(self):
return "class B object"
# b.py
from ..package1 import a
class C(a.A):
def __str__(self):
return "class C object"
...
import
语句中的 .
和 ..
语句可以视为指定目录名。 .
表示从当前目录中查找,..package1
表示从 ../package1
目录中查找。这两个语法中能在 from XX import X
语句中使用。
精确控制导入
用户可以使用 from module import *
语句导入模块或包,如果需要对导入的符号进行精确控制,可以使用变量 __all__
显式列出可导出的符号名。只有列在 __all__
中的符号才会被导入。
# c.py
from ..package1 import a
class C(a.A):
def __str__(self):
return "class C object"
class D:
def __str__(self):
return "class D object"
# Only export 'C'
__all__ = ['C']
可以看到,C
被成功导入,而 D
并没有导入。
zzz@ubuntu:~/my_learning$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from my_project.package2.c import *
>>> print( C() )
class C object
>>> print( D() )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'D' is not defined
>>>
3. 将一个模块分解为多个文件
我们可以通过 import my_module
语句导入一个模块,然后就可以通过 my_module.A()
的形式使用模块中的符号。现在,我们需要将 my_module
分解为多个单独的文件,然后仍然可以在通过 my_module.A()
的形式使用已经分解到不同文件中的符号。以 c.py
文件为例
# c.py
from ..package1 import a
class C(a.A):
def __str__(self):
return "class C object"
class D:
def __str__(self):
return "class D object"
可以使用 import
语句导入:
zzz@ubuntu:~/my_learning$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_project.package2.c as c
>>>
>>> print( c.C() )
class C object
>>> print( c.D() )
class D object
>>>
现在我们将 c.py
分解为两个文件,每个文件包含一个类的定义。首先,将c.py
替换成 目录 c
,变成如下形式:
修改前:
zzz@ubuntu:~/my_learning/my_project$ tree package2
package2
├── c.py
└── __init__.py
修改后:
package2
├── c
│ ├── c.py
│ ├── d.py
│ └── __init__.py
└── __init__.py
其中:
# my_project/package2/c/c.py
from my_project.package1 import a
class C(a.A):
def __str__(self):
return "class C object
# my_project/package2/c/d.py
class D:
def __str__(self):
return "class D object"
此时,可以使用 import
语句导入 c
模块,但是调用失败。
zzz@ubuntu:~/my_learning$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_project.package2.c as c
>>> print( c.D() )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'my_project.package2.c' has no attribute 'D'
>>> print( c.C() )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'my_project.package2.c' has no attribute 'C'
>>>
为解决这个问题,可以在 my_project/package2/c/__init__.py
文件中绑定两个文件。
# my_project/package2/c/__init__.py
from .c import C
from .d import D
此时,就可以在拆分后,仍然可以向之前一样使用:
zzz@ubuntu:~/my_learning$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_project.package2.c as c
>>> print( c.C() )
class C object
>>> print( c.D() )
class D object
>>>