目录

首先看

 import

import a 是绝对导入

a是模块

a是包

import a.b

from xxx import yyy 

from a import b

from a.b import c

导入只执行一次

import a, a里写 import b

循环导入

绝对导入

练习是否理解

 不能直接运行含相对导入的模块!

python -m

情况 1:a 是普通模块

情况 2:a 是包目录 a/

1)定位并导入 a

2)定位并导入 a.b(这里 必须是包)

3)运行目标模块 a.b.c

情况 A:c 是普通模块文件

情况 B:c 是包目录

python -m main

python -m Project1.main

Pycharm 如何寻找导入的模块?

pycharm add content roots to PYTHONPATH

​编辑 Add source roots to PYTHONPATH

环境变量


首先看

模块和库

set PYTHONPATH=c:\myproject;c:\myproject\mylib是在命令行输入。 ide 怎么设置呢

 import

import a 是绝对导入

import 不是“复制粘贴代码”,而是:

1. 在 sys.path 中查找有没有 a 这个模块或包;
2. 找到后,执行模块文件或包/__init__.py 一遍(如果有),创建一个模块对象;
3. 把这个模块对象缓存到 sys.modules(后面在到导入直接复用);
4. 在当前作用域绑定名字 a 指向这个模块对象。

- 如果 a 是模块 a.py:执行 a.py 顶层代码一遍;
- 如果 a 是包且有 __init__.py:执行 __init__.py,一切 a.xx 都来自 __init__.py;
- 如果 a 是目录但没有 __init__.py(Python3):
    - a 会变成一个 “命名空间包”;
    - 不执行任何初始化代码,但仍然创建模块对象 a,

如果a下没有__init__.py,

  • 没有执行任何 __init__.py(因为压根没有);

  • 有 a.__path__、a.__spec__ 等属性

  • 因为没有 __init__.py,所以:

  • 不会有包级别的初始化逻辑;

  • 也没地方在包里写变量/函数(你没法在 a/__init__.py 里写东西)。
    只是建一个没有初始化代码的“空壳包对象”

a是模块

a是包

现在的 Python 3 里,目录没有 __init__.py 也可以当包(namespace package)用,可以 from a import b;
但不能在包里做初始化,也不能写包级内容

import a.b

import a.b

1)先导入 a,在 sys.path 中查找:

   - 找到 a.py → a 是普通模块
   - 找到目录 a/:
       - 有 a/__init__.py → 普通包,执行 __init__.py
       - 没有 __init__.py → 命名空间包(PEP 420),不执行任何文件

   导入完成后,将模块/包对象放入 sys.modules['a']。

2)再导入子模块 a.b:
   要求 a 必须是包(即拥有 __path__):
     - 普通包 ✅
     - 命名空间包 ✅
     - 普通模块 a.py ❌

   Python 会在 a.__path__ 指向的路径中,
   按 import 规则查找名为 b 的子模块,
   b 可以是:
     - a/b.py ->普通模块,直接执行
     - a/b/__init__.py ->普通包,执行__init__.py
     - a/b/(命名空间包)→ 命名空间包(PEP 420),不执行任何文件
     - 或由 import hook 提供的模块

   找到后,加载并执行该模块。

3)将子模块缓存到 sys.modules['a.b']。

4)并将其绑定到包对象 a 上:
   a.b = <module a.b>

   注意:当前作用域不会直接得到名字 b,
   只能通过 a.b 访问。
例如访问 b.py 里的 foo() 就要写 a.b.foo()

点号链里,除最后一段外,中间每一段都必须是包

from xxx import yyy 

xxx: 点号链里,除最后一段外,中间每一段都必须是包

from a import b

from a import b

它做的事情可以按下面几步理解:

  1. 先找到并加载 a(绝对导入)

    • 在 sys.path 里的各个目录下查有没有叫 a 的模块/包:

      • 找 a.py → 模块

      • 找目录 a/(有或没有 __init__.py)→ 包 / 命名空间包

    • 如果是模块 a.py:执行 a.py 顶层代码,得到模块对象 a

    • 如果是包 a/ 且有 __init__.py:执行 a/__init__.py,得到包对象 a

    • 如果是包 a/ 没有 __init__.py:创建“命名空间包”对象 a

    • 把这个 a 放进 sys.modules 缓存(以后再导入直接复用)

  2. 在 a 里面找名字 b

    • 如果 a 是模块/包,先看它有没有属性 b(在 a.py 或 a/__init__.py 里定义/赋值的)

    • 如果 a 是包,会尝试把 b 当作 子模块或子包 导入:

      • 比如存在 a/b.py,就会导入子模块 a.b,然后把这个模块对象当作 

      • 普通包:执行__init__.py。命名空间包:不执行任何文件

  3. 在当前模块的命名空间里绑定名字 b

    • 最终效果是:当前文件里多了一个名字 b

    • 这个 b 要么是:

      • a 的一个属性(函数、类、变量…),要么是

      • 子模块 a.b 这个模块对象本身

from a.b import c

点号链里,除最后一段外,中间每一段都必须是包

第一步:找到并加载 a

- 在 sys.path 里找顶层名字 a:
  - 找到 a.py                → 普通模块
  - 找到目录 a/ 且有 __init__.py → 普通包:
      * 执行 a/__init__.py 一遍
      * 创建包模块对象 a
  - 找到目录 a/ 但没有 __init__.py → 命名空间包:
      * 不执行任何文件
      * 创建命名空间包对象 a

- 最后把 a 放进 sys.modules['a']。

  • a是普通模块 -->报错
  • a是普通包/命名空间包,在 a 下面找到并加载 b 
  • a/b.py 存在
  • 加载 a/b.py
    • 执行它的顶层代码

    • 创建模块对象 a.b

    • 放入 sys.modules['a.b']

  • a/b/ 目录存在,但里面没有 __init__.py(Python 3)
    → a.b 是一个命名空间子包(namespace package)

    • 不执行任何文件(因为没有 __init__.py 可执行)

    • 但仍然创建一个“命名空间包”对象 a.b,带有 __path__

    • 放入 sys.modules['a.b']


a/b/__init__.py 存在

  • → a.b 是一个普通子包
  • 加载 a/b/__init__.py

  • 执行其中的代码

  • 创建包模块对象 a.b

  • 放入 sys.modules['a.b']:

导入c:

1、b是包

              1、c是__init__.py里的内容

               2、c是普通模块,执行

               3、 c是普通包 -->执行__init__.py,c是命名空间包,什么也不执行

2、b是模块,c是b里面的内容

导入只执行一次

import a, a里写 import b

循环导入

如果 a 和 b 互相 import,就可能出现“其中一个模块还没初始化完,另一个就想用它的属性”的问题,这就是循环导入坑,需要通过调整 import 位置或拆模块来解决。

绝对导入

import xxx

from xx import xx都是绝对导入。

都是根据sys.path去找。

  • import a
    → 在 sys.path 路径里找一个叫 a 的模块或包,加载后把它命名为 a。

        a是模块就是把a模块顶层代码执行一遍

         a是包时就是把__init__.py的代码执行一遍,然后a.xx,xx来自__init__.py

现在的 Python 3 里,目录没有 __init__.py 也可以当包(namespace package)用,可以 from a import b;
但不能在包里做初始化,也不能写包级内容

  • from a import b
    → 先像上面那样找到/加载模块或包 a,然后从 a 里再拿出名字 b(属性或子模块)放到当前命名空间。

---------------------------------------------------------

练习是否理解

对于按照第一篇博客讲的,对于下面这一个博客,还是会不明白,不要紧先大体看一遍

 Python中import的用法总结_import在python中的用法-CSDN博客

对于这个错误

在 Python 3 里,import xxx 默认是绝对导入。

  • 你运行的是:python main.py

  • 这时候,“当前工作目录(项目根目录)” 会被加到 sys.path 里

  • 所以 Python 在执行 import cc 时,会去 sys.path 里的各个目录下面找:

    • 根目录下面找:有没有 cc.py 或 cc/__init__.py

    • 其它路径…(标准库、site-packages 等)

但你的 cc.py 在 branch/cc.py 里,不在根目录,所以:

顶层模块 cc 并不存在,结果就是:ModuleNotFoundError: No module named 'cc'

为什么 bb.py 中的from . import cc 可以找到 。main.py在Pythonprojectt目录 此时.不应该是Pythonproject吗 ?

 在 bb.py 中,from . import cc 的 “.” 表示的是 当前模块所属的包,也就是 branch包,而不是根据运行的入口文件(main.py)来决定的。

从当前模块所在的包导入cc

这个“属于哪个包”的信息来自模块运行时的一个变量:__package__

 那为什么 “.” 是相对于 branch,不是运行的入口文件的目录?

这是 Python 的导入机制中的一个关键点:

  • Python 中的每个模块都有一个特殊变量 __package__。

  • 当你运行 main.py,Python 会导入 branch.bb,此时 bb.py 的 __package__ = 'branch'。

  • 所以 from . import cc 会被理解为:from branch import cc。

解释

 不能直接运行含相对导入的模块!

python -m

通过 -m 参数 执行某个模块,那么 当前工作目录 会被加到 sys.path 中作为模块搜索路径。

-m 后面要的是“模块名”,不是文件路径,也不能用相对名(如 .\main.py)

python -m X 的规则是:

  1. 先按 import X 的方式找到模块 X

  2. 如果 X 是普通模块(文件 X.py)→ 执行这个文件

  3. 如果 X 是包(目录 X/)→ 执行 X/__main__.py,有 X/__main__.py → 就执行它,没有 X/__main__.py → 直接报错【顺便区分一下:X/__init__.py 是“导入包时初始化用的”X/__main__.py 是“把包当程序运行时的入口”】

例1:

情况 1:a 是普通模块

  • 找到 a.py → 执行 a.py

情况 2:a 是包目录 a/

  • 如果是普通包(有 a/__init__.py)→ 会先执行 a/__init__.py(导入/初始化包)

  • 如果是命名空间包(无 __init__.py)→ 不执行 __init__(因为没有)

  • 然后去找并执行入口:执行 a/__main__.py

    • 没有 a/__main__.py → 报错(包不能直接 -m 运行)

例2:

python -m a.b

先导入包 a

  • a/__init__.py 存在(普通包)→ 执行 a/__init__.py

  • 不存在(命名空间包)→ 不执行初始化文件

定位目标模块 a.b 并运行

  • 如果是普通模块:存在 a/b.py
    → 执行 a/b.py

  • 如果是包:存在目录 a/b/

    • 若有 a/b/__init__.py(普通包)→ 通常会先执行 a/b/__init__.py(导入包初始化)

    • 然后执行入口:a/b/__main__.py

      • 没有 __main__.py → 报错

例3:

python -m a.b.c

1)定位并导入 a

Python 先按 import a 的规则在 sys.path 里找:

  • 若找到 a.py(普通模块)
    → 报错:因为普通模块 a 不能再有子模块 a.b
    (常见报错:No module named 'a.b'; 'a' is not a package')

  • 若找到目录 a/(包或命名空间包)

    • 若有 a/__init__.py(普通包)→ 执行 a/__init__.py,并得到包对象 a

    • 若没有 a/__init__.py(命名空间包)→ 创建命名空间包对象 a(不执行文件)

并把结果缓存到 sys.modules['a']。


2)定位并导入 a.b(这里 必须是包)

在 a 的包搜索路径(a.__path__)下找:

  • 若找到 a/b.py(普通模块)
    → 报错:a.b 不是包,不能再继续找 a.b.c
    报错形式通常是:No module named 'a.b.c'; 'a.b' is not a package

  • 若找到目录 a/b/(包或命名空间包)

    • 若有 a/b/__init__.py(普通包)→ 执行 a/b/__init__.py

    • 若没有 a/b/__init__.py(命名空间包)→ 创建命名空间包对象 a.b(不执行文件)

并缓存到 sys.modules['a.b'],同时让 a.b 可用(挂到 a 上)。


3)运行目标模块 a.b.c

现在开始处理 c(最后一段可以是模块文件,也可以是包):

情况 A:c 是普通模块文件
  • 若存在 a/b/c.py
    → 执行 a/b/c.py(这就是 -m 指定的目标模块被执行)

并缓存到 sys.modules['a.b.c']。

情况 B:c 是包目录
  • 若存在目录 a/b/c/

    • 若有 a/b/c/__init__.py(普通包)→ 可能会先执行它(导入初始化)

    • 然后必须有入口文件:执行 a/b/c/__main__.py

      • 如果没有 a/b/c/__main__.py → 报错(包不能作为 -m 目标直接运行)

python -m main

(.venv) PS D:\zhouj\Documents\Pythontest\Project1> python -m main

python -m Project1.main

cd Pythontest python -m Project1.main

 

Pycharm 如何寻找导入的模块?

使用IDE - 白月黑羽

在pycharm 可以运行 但是在命令行不可以运行 因为pycharmsys.path有项目根目录。 

pycharm add content roots to PYTHONPATH

 Add source roots to PYTHONPATH

#Python中的包
#包(python package)是一个分层次的目录(directory)结构,它将一组功能相近的模块组织在一个目录下
#作用:1.代码规范,2.避免模块名称冲突
#包与目录的区别
#包含__init__.py文件的目录称为包
#目录通常不包含__init__.py文件
#包的导入
#import 包名.模块名
#在demo中导入包:demo2中的模块:modelA
import demo2.modelA
print(demo2.modelA.a)
import demo2.modelA as s#导入包:demo2中的模块:modelA起别名为s
#s是demo2.modelA的别名
print(s.a)
###############################################
'''导入带有包的模块时的注意事项
使用import方式进行导入时,只能根报名或模块名
import clc
import demo2

使用from impor导入时可以导入包,模块,函数,变量等
from demo2 import modelA导入包demo2中的模块a
from demo2.modelA import a导入包demo2中的模块modelA的a

'''

环境变量

环境变量 - 白月黑羽

python -m 的作用是:让 Python 按“模块名”去运行代码,而不是按“文件路径”去运行代码。-m 里的 m 可以理解成 module,也就是模块

python -m 的作用是:
用当前 Python 解释器,按照模块名去当前环境的搜索路径中查找并运行一个模块或包。

这个模块/包可以是:
1. 你自己项目里的模块;
2. Python 标准库模块;
3. 虚拟环境 site-packages 里安装的第三方包;
4. 其中一些第三方包提供命令行入口,因此也可以当作第三方工具使用。

作用 1:运行你自己项目里的模块

作用 2:运行已经安装好的第三方模块或包

python -m 还有一个很常见的作用:保证用的是当前 Python 环境

让当前 python 去它自己的环境里找对应模块并运行

python -m 可以运行当前 Python 环境中能找到的 模块或包;其中有些包刚好提供了命令行功能,所以看起来像是在运行“第三方工具”

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐