import system(模块导入)

regular package 和 namespace package
常规包: 包里带有__init__.py文件的包。 import regular_package 等价于 import regular_package.init。 即,导入常规包,其实就是运行包里的__init__文件。
创建一个目录如下:

world_regular/
│
├── africa/
│   ├── __init__.py
|   ├── congo.py
│   └── zimbabwe.py
│
├── europe/
│   ├── __init__.py
│   ├── greece.py
│   ├── norway.py
│   └── spain.py
│
└── __init__.py

里面代码如下:

# __init__.py
print("here is running world_regular.__init__py")
from . import africa

# africa/__init__.py
print("here is running africa.__init__py")

# africa/zimbabwe.py
print('这里是津巴布韦')

# africa/congo.py
print("这里是刚果")

运行代码,结果如下:

import world_regular
# here is running world_regular.__init__py
# here is running africa.__init__py

world_regular
# <module 'world_regular' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\__init__.py'>

dir(world_regular)
# ['__builtins__',
#  '__cached__',
#  '__doc__',
#  '__file__',
#  '__init__',
#  '__loader__',
#  '__name__',
#  '__package__',
#  '__path__',
#  '__spec__',
#  'africa']

world_regular.__path__
# ['F:\\RolandWork\\PythonProjects\\studyPython\\world_regular']

world_regular.__file__
# 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\__init__.py'

从结果可以看到,import world_regular打印了两行内容。如前所述,import 常规包,相当于执行了包里的__init__.py文件。而里面又导入了africa这个子包,子包里的__init__.py文件里也输出了一行,所以就有了看到的结果。
使用dir()函数可以看到,除了一些内置特殊方法外,也能看到africa这个子包的名称。可以注意到,包在python里也是模块类型(module object),并且包有__path__属性,指向的就是包的目录,而包的__file__属性指向的是包里的__init__.py文件的路径。

由于africa子包的__init__.py文件没有导入其内部的模块,所以目前是访问不到里面的模块的。

world_regular.africa
# <module 'world_regular.africa' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\__init__.py'>

dir(world_regular.africa)
# ['__builtins__',
#  '__cached__',
#  '__doc__',
#  '__file__',
#  '__loader__',
#  '__name__',
#  '__package__',
#  '__path__',
#  '__spec__']

现在我们手动导入africa子包里的zimbabwe模块。

from world_regular.africa import zimbabwe
# 这里是津巴布韦

zimbabwe
# <module 'world_regular.africa.zimbabwe' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\zimbabwe.py'>

dir(zimbabwe)
# ['__builtins__',
#  '__cached__',
#  '__doc__',
#  '__file__',
#  '__loader__',
#  '__name__',
#  '__package__',
#  '__spec__']

zimbabwe.__package__
# 'world_regular.africa'

zimbabwe.__file__
# 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\zimbabwe.py'

我们发现,zimbabwe类型仍是module,并且没有了__path__属性,说明只有包才了这个属性。 __file__指向了module文件本身的路径。 __package__显示导入时的命名空间层次。
我们再次查看world_regular.africa属性时,发现其多了zimbabwe。

dir(world_regular.africa)
# ['__builtins__',
#  '__cached__',
#  '__doc__',
#  '__file__',
#  '__loader__',
#  '__name__',
#  '__package__',
#  '__path__',
#  '__spec__',
#  'zimbabwe']

我们用另一种方式导入congo这个模块,整体代码如下,我们做一下对比。

import world_regular
from world_regular.africa import zimbabwe

for k, v in list(globals().items()):
    print(f'{k}: {v}')

print("-" * 100)
print("-" * 100)

import world_regular.africa.congo

for k, v in list(globals().items()):
    print(f'{k}: {v}')

print("-" * 100)
print("-" * 100)
print(dir(world_regular.africa))

输出结果:

here is running world_regular.__init__py
here is running africa.__init__py
这里是津巴布韦
__name__: __main__
__doc__: None
__package__: None
__loader__: <_frozen_importlib_external.SourceFileLoader object at 0x0000022DDCD65350>
__spec__: None
__annotations__: {}
__builtins__: <module 'builtins' (built-in)>
__file__: F:\RolandWork\PythonProjects\studyPython\test_folder\main.py
__cached__: None
world_regular: <module 'world_regular' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\__init__.py'>
zimbabwe: <module 'world_regular.africa.zimbabwe' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\zimbabwe.py'>
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
这里是刚果
__name__: __main__
__doc__: None
__package__: None
__loader__: <_frozen_importlib_external.SourceFileLoader object at 0x0000022DDCD65350>
__spec__: None
__annotations__: {}
__builtins__: <module 'builtins' (built-in)>
__file__: F:\RolandWork\PythonProjects\studyPython\test_folder\main.py
__cached__: None
world_regular: <module 'world_regular' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\__init__.py'>
zimbabwe: <module 'world_regular.africa.zimbabwe' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\zimbabwe.py'>
k: zimbabwe
v: <module 'world_regular.africa.zimbabwe' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_regular\\africa\\zimbabwe.py'>
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'congo', 'zimbabwe']

可以看到,两种导入方法最大的区别在于,第一种将zimbabwe模块直接加进了全局命名空间。 但相同的是,两种导入方法,在world_regular.africa这层命名空间中都能看到导入的模块(zimbabwe和congo)。

命名空间包: 包里没有__init__.py文件。
创建一个目录如下,其实同上面目录,只是删除了__init__.py文件。

world_namespace/
│
├── africa/
|   ├── congo.py
│   └── zimbabwe.py

执行如下代码:

import world_namespace

world_namespace
# <module 'world_namespace' (<_frozen_importlib_external.NamespaceLoader object at 0x000001C8988F3ED0>)>

dir(world_namespace)
['__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__']

world_namespace.__path__
# _NamespacePath(['F:\\RolandWork\\PythonProjects\\studyPython\\world_namespace'])

world_namespace.__file__
# None

从执行结果上看,导入的命名空间包的属性基本与常规包相同,只是包的__file__属性是None。
下面导入里面子包的模块。

import world_namespace

from world_namespace.africa import zimbabwe
# 这里是津巴布韦

dir(world_namespace)
['__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 'africa']

import world_namespace.africa.congo
# 这里是刚果

dir(world_namespace.africa)
['__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 'congo',
 'zimbabwe']

可以注意到,当执行完 from world_namespace.africa import zimbabwe 这句代码后,python就默默的把africa这个名称加到world_namespace这个命名空间了。而zimbabwe将加到world_namespace.africa这层命名空间。
import world_namespace.africa.congo 这行代码同样会将congo加到world_namespace.africa这层命名空间。
zimbabwe还将出现在全局命名空间中。

自定义模块与标准模块名称冲突
看个有趣的例子。我在当前目录下创建一个math.py文件,一个re.py文件。里面的代码如下:

# math.py
print("我自己的math")
# re.py
print("这是我的re")

然后执行下面代码:

import sys
import math
import re

print(math.pi)

print("-"* 50)
for item in sys.builtin_module_names:
    if item[0] != '_':
        print(item)

输出结果如下:

这是我的re
3.141592653589793
--------------------------------------------------
array
atexit
audioop
binascii
builtins
cmath
errno
faulthandler
gc
itertools
marshal
math
mmap
msvcrt
nt
sys
time
winreg
xxsubtype
zlib

从上面结果我们注意到,导入math后,导入的其实仍然是标准模块的math。 而导入re后,导入的其实是我自己编写的re.py文件,而不是标准模块的。
出现这种问题的原因可以从sys.builtin_module_names中找到。 math出现在了sys.builtin_module_names中,决定了导入math时python知道需要从built-in中导入。但re没在sys.builtin_module_names中,所以导入时按照sys.path里的路径去寻找。

for item in sys.path:
    print(item)

输出结果:

F:\RolandWork\PythonProjects\studyPython
F:\RolandWork\PythonProjects\studyPython
C:\Users\user\AppData\Local\Programs\Python\Python311\python311.zip
C:\Users\user\AppData\Local\Programs\Python\Python311\DLLs
C:\Users\user\AppData\Local\Programs\Python\Python311\Lib
C:\Users\user\AppData\Local\Programs\Python\Python311
F:\RolandWork\PythonProjects\formatJson\.venv
F:\RolandWork\PythonProjects\formatJson\.venv\Lib\site-packages

可以看到sys.path中当前的当前目录是排在第一位的,导入模块是最先就会从当前工作目录查找。所以import re时,因为re没在sys.builtin_module_names中,导入的就是当前目录的re。

我另外在当前目录下又创建了一个dataclasses文件。

# dataclasses
print("我自己的dataclasses")

接下来执行下面的代码:

import sys
import pprint

输出结果:

我自己的dataclasses
这是我的re

出现这个情况说明在导入pprint模块时,pprint模块内部又导入了dataclasses和re两个模块,但由于这两个模块文件在当前目录就找到了,所以导入的其实是我自已写的两个py文件,而不是标准模块。这当然会引发严重的问题,导致标准模块或第三方模块不能正常工作。 因此我们要避免模块名称冲突的问题。 我们可以将自定义的模块放在一个独立的文件夹中,而不是在python程序主入口文件所在的目录。这样即便重名,我们的模块也是在独立的命名空间中,导入时也会加上命名空间,即所在的包文件夹名称。
下面演示一下报错情况:

import sys
import pprint

pprint.pp(sys.modules)

输出结果:

Traceback (most recent call last):
  File "F:\RolandWork\PythonProjects\studyPython\forTest.py", line 4, in <module>
    pprint.pp(sys.modules)
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 66, in pp
    pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 55, in pprint
    printer.pprint(object)
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 153, in pprint
    self._format(object, self._stream, 0, 0, {}, 0)
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 181, in _format
    p(self, object, stream, indent, allowance, context, level + 1)
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 217, in _pprint_dict
    self._format_dict_items(items, stream, indent, allowance + 1,
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 388, in _format_dict_items
    self._format(ent, stream, indent + len(rep) + 2,
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\pprint.py", line 184, in _format
    elif (_dataclasses.is_dataclass(object) and
          ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'dataclasses' has no attribute 'is_dataclass'
我自己的dataclasses
这是我的re
{'sys': <module 'sys' (built-in)>,
 'builtins': <module 'builtins' (built-in)>,
 '_frozen_importlib': <module '_frozen_importlib' (frozen)>,
 '_imp': <module '_imp' (built-in)>,
 '_thread': <module '_thread' (built-in)>,
 '_warnings': <module '_warnings' (built-in)>,
 '_weakref': <module '_weakref' (built-in)>,
 'winreg': <module 'winreg' (built-in)>,
 '_io': <module '_io' (built-in)>,
 'marshal': <module 'marshal' (built-in)>,
 'nt': <module 'nt' (built-in)>,
 '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>,
 'time': <module 'time' (built-in)>,
 'zipimport': <module 'zipimport' (frozen)>,
 '_codecs': <module '_codecs' (built-in)>,
 'codecs': <module 'codecs' (frozen)>,
 'encodings.aliases': 
Process finished with exit code 1

绝对导入与相对导入
在工程目录下,有两个模块,分别是main.py和settings.py。我的工程目录为F:\RolandWork\PythonProjects\studyPython
下面是main.py中的代码

# main.py
import sys, pprint

print('here is my main module')
pprint.pp(sys.path)

import settings  # 绝对路径导入

def main():
    print('here is main function')
    print('do something')

# 打印导入的settings模块的属性
for item in dir(settings):
    if item in ('__file__', '__package__', '__path__', 'abc'):
        print(f"{item}:", getattr(settings, item))
        print("*" * 50)

if __name__ == "__main__":
    main()

下面是settings.py中的代码

# settings.py
'''this is settings module'''

print('here is settings module')

abc = 100

这时,直接在工作目录下,运行python main.py
输出结果如下

(.venv) PS F:\RolandWork\PythonProjects\studyPython> python main.py
here is my main module
['F:\\RolandWork\\PythonProjects\\studyPython',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\python311.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\Lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv\\Lib\\site-packages']
here is settings module
__file__: F:\RolandWork\PythonProjects\studyPython\settings.py
**************************************************
__package__: 
**************************************************
abc: 100
**************************************************
here is main function
do something

注意到,代码成功运行,并且打印的sys.path中的第一行就是python运行脚本时所在的路径。
现在,假设我们开发的程序将来会集成到其他项目中去,我们模拟方法是,把这两个文件放在一个test_folder的文件夹,在工程目录下再建一个fortest.py,内容如下:

from test_folder.main import main
main()

这时运行python fortest.py就会报错。

here is my main module
['F:\\RolandWork\\PythonProjects\\studyPython',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\python311.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\Lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv\\Lib\\site-packages']
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "F:\RolandWork\PythonProjects\studyPython\test_folder\main.py", line 6, in <module>
    import settings
ModuleNotFoundError: No module named 'settings'

报错原因是main.py里导入settings时提示找不到。原因是,python运行时会将运行时的工作目录(F:\RolandWork\PythonProjects\studyPython)填加到sys.path中,python导入包时也是从这个列表中按照自上而下的顺序查找。 他在运行目录里没找到,就会去标准模块和其他第三方包路径中查找,都找不到所以就报错了。
解决方式是将main.py里的import settings 改为 from . import settings。 也就是将绝对路径导入,改为相对路径导入。这时再运行python fortest.py就成功了。

# main.py
import sys, pprint
print('here is my main module')
pprint.pp(sys.path)

# import settings  # 绝对路径导入
from . import settings  # 相对路径导入

再次运行后,结果正确,输出结果如下:

(.venv) PS F:\RolandWork\PythonProjects\studyPython> python forTest.py
here is my main module
['F:\\RolandWork\\PythonProjects\\studyPython',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\python311.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\Lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv\\Lib\\site-packages']
here is settings module
__file__: F:\RolandWork\PythonProjects\studyPython\test_folder\settings.py
**************************************************
__package__: test_folder
**************************************************
abc: 100
**************************************************
here is main function
do something

注意到,这次只是setting的__package__从原来的None,变成了现在的test_folder。

但有时候我们可能还有某种需要直接运行main.py的需求,这时,我们在控制台输入命令python test_folder/main.py,运行又报错了。

(.venv) PS F:\RolandWork\PythonProjects\studyPython> python test_folder/main.py
here is my main module
['F:\\RolandWork\\PythonProjects\\studyPython\\test_folder',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\python311.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311\\Lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python311',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv',
 'F:\\RolandWork\\PythonProjects\\formatJson\\.venv\\Lib\\site-packages']
Traceback (most recent call last):
  File "F:\RolandWork\PythonProjects\studyPython\test_folder\main.py", line 7, in <module>
    from . import settings
ImportError: attempted relative import with no known parent package

这个错误的原因是,相对路径导入是不能用在程序入口脚本文件中的。因为 "."代表当前包,但做为脚本运行时,所在模块即为顶层模块(__main__),而不是处于某个包中,所以就会提示找不到parent package。
现在我们面临着这个头疼的问题,如果我们想直接运行main.py脚本,则里面就不能用相对路径导入。 如果想在其他模块中导入这个main.py,就只能在里面使用相对路径导入。
我们大概率不愿为此又复制一份main.py文件,一份文件使用相对路径导入,另一份使用绝对路径导入,下面方法可以解决这个问题。
**使用python -m test_folder.main 命令。 **
这个命令会以模块(或者说包)的形式运行脚本。但要求我们在main.py中都使用相对路径导入。
机制: python 后面无论有没有 "-m",都会把当前工作目录(我的例子中的是"F:\RolandWork\PythonProjects\studyPython") 加到sys.path中。执行 python -m test_folder.main.py 时,python不把main.py当成顶层入口,而是把test_folder当成一个包,而main.py就是这个包中的一个模块。在sys.path中的当前工作目录下可以找得到这个包及模块。main.py里的 from . import setting,就相当于从包(test_folder)里面找settings这个模块。
这种用法还经常用于当我们单独执行python的内置模块时,python -m venv, python -m pip等等。

这样当我们想单独执行main.py时,就使用python -m test_folder.main 命令。 当我们想在其他程序中引入main.py时,也一样可以正常工作。

通过__main__直接运行包
有时候,当我们或别人封装一个功能时,可能涉及多个模块,并且放在一个包中。而开发这个包的人会知道这个包里哪个模块是执行的入口,但使用的人不知道。 这个时候可以将入口代码放在__main__.py中,运行这个包的时候就直接python pkgname,或python -m pkgname。
前面的例子中,我们直接将main.py改名为__main__.py, 然后原来使用的python -m test_folder.main 命令就可以改成 python -m test_folder了。
需要注意的是,当我们以包名来运行时,如果是常规包(即存在__init__.py文件),则__init__.py文件中的代码会先被执行,然后执行__main__.py文件中的代码。
另外,使用python test_folder命令运行包时等价于运行里面的__main__.py文件,即python test_folder/__main__.py 所以,如果__main__.py里面使用了相对路径导入,则还是会报错。

安装本地包
你可能还有个疑问,就是运行我们自己创建的包时总是得确保python运行的时工作目录是正确的,即必须是包的上层目录。而那些系统内置包,比如venv, pip等就可以在任意工作目录中执行。 并且,我们自己开发的包在别的应用程序中访问时,被导入时也要时刻注意我们的包在其他项目目录中的位置,因此要调整绝对导入或相对导入时的导入代码,还是有点麻烦。 如何能像标准模块,或者用pip命令安装的第三方模块能样,在任意工作目录都能单独执行,并且在任何项目中都能直接导入,不需要考虑包存放的位置呢?答案是,将其做为本地包进行安装。
下面示例我创建了一个自己的包,首先最外层的snakesay_project是什么名字无所谓,其内部snakesay是实际的包,而pyproject.toml文件与它同级。
snakesay_project
├── pyproject.toml
├── snakesay
│ ├── funcs.py
│ ├── main.py

pyproject.toml里的内容如下:

[build-system]
requires      = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "snakesay_pkg"
version = "1.0.0"

[project.scripts]
snakesay = "snakesay.__main__:main"

注意到,project部分的name为snakesay_pkg,与实际的包名snakesay不一致,这是允许的。 如果我们将这个包发布到pypi,那么当从pypi安装时,命令将是pip install snakesay_pkg,而实际安装到site_packages目录里的包名实际还是sankesay。
这就像pip install beautifulsoup4后,实际使用时import bs4 。 bs4才是实际导入时使用的实际包名。

安装本地包时可以切换到snakesay_project文件夹里,执行pip install . 来安装,如果在此目录外面,比如它的上一层,则使用pip install ./snakesay_project。 注意,这里的./不能省略,否则它会去pypi上找,但又找不到,所以会报错。
另外,当我们自己开发的包需要频繁修改时,可以不实际将其安装到包的目录里,而是保留原代码位置,当我们更新代码时,会立即反馈影响,我们就使用pip install -e ./snakesay_project。否则,实际会在我们的site_packages目录里复制一份包代码,并且修改原来地方的代码后,需要再次执行pip install进行安装(即更新),才能反馈到导入此包的程序中。
project.scripts部分的作用是将snakesay做为命令,这样我们即可以python -m snakesay balabala这样运行,也可以直接snakesay balabala这样运行。 就像pip命令那样。

模块的特殊属性
__all__: 限制使用from moudle import * 时,导入的名称。 对于直接使用import module时,通过moulde.variable的方法还是可以访问到没有放在__all__列表中的名称。
__dir__: 如果定义了此方法,当使用import module后,执行dir(module)就会显示该方法的返回值。
__getattr__: 如果定义了此方法,当import module后,如果访问module.abc属性不存在,就会触发这个方法。

importlib包的功能
importlib.import_module(name, package=None): 导入一个模块。 参数 name 指定了以绝对或相对导入方式导入什么模块 (比如要么像这样 pkg.mod 或者这样 ..mod)。 如果参数 name 使用相对导入的方式来指定,那么 package 参数必须设置为那个包名,这个包名作为解析这个包名的锚点 (比如 import_module('..mod', 'pkg.subpkg') 将会导入 pkg.mod)。
importlib.reload(module): 重新加载之前导入的 module。 参数必须是一个模块对象,所以它之前必须已经成功导入了。

importlib.resources: 包资源的读取、打开和访问
importlib.resources.files(anchor: Anchor | None = None): 返回一个代表资源容器(相当于目录)及其资源(相当于文件)的 Traversable 对象。 Traversable 可以包含其他容器(相当于子目录)。anchor 是一个可选的 Anchor。 如果 anchor 是一个包,则会从这个包获取资源。 如果是一个模块,则会从这个模块的相邻位置获取资源(在同一个包或包的根目录中)。 如果省略了 anchor,则会使用调用方的模块。
importlib.resources.as_file(traversable): 给定一个代表文件或目录的 Traversable 对象,通常是来自 importlib.resources.files(),返回一个上下文管理器以供 with 语句使用。 该上下文管理器提供一个 pathlib.Path 对象。

函数式 API
*importlib.resources.open_binary(anchor, path_names)
*importlib.resources.open_text(anchor, path_names, encoding='utf-8', errors='strict')
*importlib.resources.read_text(anchor, path_names, encoding='utf-8', errors='strict')
*importlib.resources.read_binary(anchor, path_names)
*importlib.resources.path(anchor, path_names)
*importlib.resources.is_resource(anchor, path_names)

命名空间包的跨多目录特性
前面例子中我在工程目录下有一个world_namespace目录,现在我又在E盘使用相同文件夹创建了一个目录,结构如下:

world_namespace
    └── asia
        ├── asia intro.txt
        ├── china.py
        ├── japan.py

下面的示例代码演示了,虽然world_namespace目录出现在两个不同的盘符上,但是对于使用world_namespace命名空间包来说,文件就像是放在一块的。

import sys

sys.path.append(r"E:\test_namespace")

import world_namespace.africa.congo
import world_namespace.asia.china

print(world_namespace.africa.congo)
print(world_namespace.asia.china)

import importlib.resources

multifile = importlib.resources.files(world_namespace)

for file in multifile.iterdir():
    print(file)

print(world_namespace.__path__)

输出结果:

这里是刚果
这里是中国
<module 'world_namespace.africa.congo' from 'F:\\RolandWork\\PythonProjects\\studyPython\\world_namespace\\africa\\congo.py'>
<module 'world_namespace.asia.china' from 'E:\\test_namespace\\world_namespace\\asia\\china.py'>
F:\RolandWork\PythonProjects\studyPython\world_namespace\africa
F:\RolandWork\PythonProjects\studyPython\world_namespace\europe
F:\RolandWork\PythonProjects\studyPython\world_namespace\first_level_module.py
E:\test_namespace\world_namespace\asia
_NamespacePath(['F:\\RolandWork\\PythonProjects\\studyPython\\world_namespace', 'E:\\test_namespace\\world_namespace'])

从运行结果可以看出,命名空间包的__path__属性为一个列表,正好对应了实际存放包内容的两个文件夹。

posted @ 2025-03-01 12:14  RolandHe  阅读(117)  评论(0)    收藏  举报