0%

python import 机制

python中的各种import方式

目录结构

目录结构默认如下

1
2
3
4
5
6
7
/root/project
├── /animal
│ ├── cat.py
│ ├── dog.py
│ └── __init__.py
└── main.py

animal/__init__.py中内容为

1
2
3
print("animal imported")
from .cat import Cat
from .dog import Dog

animal/cat.py中内容为

1
2
3
4
print("Cat imported")
class Cat(object):
def __init__(self) -> None:
pass

其他文件同理

在main中进行import

import时一定会执行文件夹下的__init__.py

main.py中为

1
2
3
4
5
6
7
import animal
# 或
from animal import Cat
# 或
from animal.cat import Cat
# 或
import animal.cat

/root/project下执行python main.py

此时import执行步骤为
animal/__init__.py -> animal/cat.py
输出为

1
2
3
animal imported
Cat imported
Dog imported

在子目录中执行__init__.py

/root/project/animal下执行python __init__.py

会发生报错

1
2
3
4
5
animal imported
Traceback (most recent call last):
File "__init__.py", line 3, in <module>
from .cat import Cat
ImportError: attempted relative import with no known parent package

在子目录中直接运行文件

有时需要在子目录中执行方便的脚本,但是这时就会遇到报错

此时可以通过修改sys.path来自定义搜索范围

首先尝试修改animal/__init__.py中内容为

1
2
3
4
5
6
7
import sys
from pathlib import Path
package_root = Path(__file__).absolute().parent.parent
print(f"animal imported, add {package_root} to sys.path")
sys.path.insert(0, str(package_root))
from animal.cat import Cat
from animal.dog import Dog

/root/project/animal下执行python __init__.py

此时输出为

1
2
3
4
animal imported, add /root/project/python_pkg_test to sys.path
animal imported, add /root/project/python_pkg_test to sys.path
Cat imported
Dog imported

可以发现print被执行了两遍,实际上所有语句都被执行了两遍,此时打印sys.path会发现存在两个/root/project

所以不能直接这样设置, 首先恢复animal/__init__.py

新建animal/__init__.py, 其中内容为

1
2
3
4
5
6
import sys
from pathlib import Path
package_root = Path(__file__).absolute().parent.parent
print(f"add {package_root} to sys.path")
sys.path.insert(0, str(package_root))
print(len(sys.path))

/root/project/animal下执行python mian.py

此时输出为

1
2
3
4
5
add /root/project to sys.path
['/root/project', '/root/project/animal', ...]
animal imported
Cat imported
Dog imported