python 规避嵌套import带来问题

本文深入探讨Python中的导入机制,包括标准导入、嵌套导入及循环嵌套导入的原理与注意事项,解析Python如何处理模块导入冲突,以及如何正确使用import语句避免常见错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先上一个我自己的结论
为了规避下面所述的嵌套导包的错误,最好使用

import module
module.func()

的方式,而不是使用

from module import func
func()

标准import

    Python中所有加载到内存的模块都放在 sys.modules 。当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中。如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py、pyc、pyd,找到后将模块载入内存,并加到 sys.modules 中,并将名称导入到当前的 Local 名字空间。
  一个模块不会重复载入。多个不同的模块都可以用 import 引入同一个模块到自己的 Local 名字空间,其实背后的 PyModuleObject 对象只有一个。这里说一个容易忽略的问题:import 只能导入模块,不能导入模块中的对象(类、函数、变量等)。例如:模块 A(A.py)中有个函数 getName,另一个模块不能通过 import A.getName 将 getName导入到本模块,只能用 from A import getName。=

嵌套import

  • 顺序嵌套
        例如:本模块导入 A 模块(import A),A 中又 import B,B 模块又可以 import 其他模块……
      这中嵌套比较容易理解,需要注意的一点就是各个模块的 Local 名字空间是独立的。对于上面的例子,本模块 import A 之后本模块只能访问模块 A,不能访问模块 B 及其他模块。虽然模块 B 已经加载到内存了,如果访问还要再明确的在本模块中 import B。
  • 循环嵌套
    例如:
    文件[A.py]
from B import D
class C:pass

    文件[ B.py ]

from A import C
class D:pass

为什么执行 A 的时候不能加载 D 呢?
如果将 A.py 改为:import B 就可以了。
这是怎么回事呢?


Python内部 import 的机制

具体到 from B import D,Python 内部会分成几个步骤:

  • 在 sys.modules 中查找符号 “B”
  • 如果符号 B 存在,则获得符号 B 对应的 module 对象。
    从 < modult B> 的 __ dict __中获得符号 “D” 对应的对象,如果 “D” 不存在,则抛出异常。
  • 如果符号 B 不存在,则创建一个新的 module 对象 < module B>,注意,此时,module 对象的 __ dict __ 为空。
    执行 B.py 中的表达式,填充 < module B> 的 __ dict __。
    从 < module B> 的 __ dict __ 中获得 “D” 对应的对象,如果 “D” 不存在,则抛出异常。

所以这个例子的执行顺序如下:

  • 执行 A.py 中的 from B import D 由于是执行的 python A.py,所以在 sys.modules 中并没有 < module B>存在, 首先为 B.py 创建一个 module 对象 (< module B>) , 注意,这时创建的这个 module 对象是空的,里边啥也没有,(www.linuxidc.com) 在 Python 内部创建了这个 module 对象之后,就会解析执行 B.py,其目的是填充 < module B> 这个 __ dict __。
  • 执行 B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在了, 由于这时缓存还没有缓存, 所以类似的,Python内部会为A.py创建一个module对象(), 然后,同样地,执行A.py中的语句
  • 再次执行A.py中的from B import D 这时,由于在第1步时,创建的< module B>对象已经缓存在了sys.modules中, 所以直接就得到了< module B>, 但是,注意,从整个过程来看,我们知道,这时< module B>还是一个空的对象,里面啥也没有, 所以从这个module中获得符号"D"的操作就会抛出异常。 如果这里只是import B,由于"B"这个符号在sys.modules中已经存在,所以是不会抛出异常的。
### Python 中 `import` 的用法及常见问题 #### 什么是 `import` 在 Python 中,模块是一个包含函数、类或其他可执行代码的文件。通过使用 `import` 关键字,可以从其他模块中引入这些功能并加以利用。其本质是加载指定名称空间的内容到当前作用域下[^1]。 #### 基本语法 最简单的形式如下所示: ```python import module_name ``` 这会将整个模块作为一个对象导入至当前命名空间。如果想访问该模块内的特定属性,则需采用点号操作符的形式调用它,例如: ```python module_name.function() ``` 对于只需要部分组件的情况,可以借助另一种方式——即只提取所需的成员而无需载入完整的包结构体。这种情况下需要用到 `from ... import ...` 结构: ```python from module_name import function_or_class function_or_class() # 不再需要前缀限定名 ``` 此外还有通配符版本允许一次性获取目标库下的全部公开项目(不推荐因为容易引起名字冲突),形如: ```python from module_name import * ``` #### 相对路径导入 当涉及到子目录或者父级目录中的资源时,可能会遇到相对导入的需求。“`.`”代表当前位置,“`..`”则指向上级位置[^2]。比如在一个多层嵌套包内部实现跨层次引用的时候就非常有用处了。 #### 解决常见的错误情况 有时开发者可能碰到诸如“No such file or directory”的异常提示,通常是因为试图直接运行某个作为整体应用一部分的小脚本所致;由于缺少上下文环境支持而导致找不到关联依赖项。此时应该调整启动入口为顶层管理器而非单独片段来规避此类状况的发生。 #### 总结 掌握好上述几种基本模式及其变种可以帮助我们更加灵活高效地构建大型软件系统架构设计思路,并有效减少重复劳动提高开发效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值