相互进口;导入标准、"from" 和 "as" 语法之间的区别
Mutual imports; difference between import's standart, "from" and "as" syntax
给定这个简单的文件夹结构
/main.py
/project/a.py
/project/b.py
main.py
由 python 解释器执行并包含一行,import project.a
.
a
和b
是模块,需要相互导入。实现这一目标的一种方法是
import project.[a|b]
使用更深的嵌套文件夹结构时,您不希望每次使用模块时都写下整个路径,例如
import project.foo.bar
project.foo.bar.set_flag(project.foo.bar.SUPER)
from project import [a|b]
和 import project.[a|b] as [a|b]
都会导致导入错误(当同时用于 a
和 b
时)。
标准导入语法与 from
或 as
语法有何不同?为什么只有标准语法适用于相互导入?
更重要的是,是否有一种简单干净的方法来导入模块,允许相互导入并为它们分配更短的名称(理想情况下是模块基本名称,例如 bar
在 project.foo.bar
的情况下) ?
当您执行 import project.a
或 from project import a
时,会发生以下情况:
project.a
的模块对象被放入 sys.modules
。这是一个将每个模块名称映射到其模块对象的字典,因此您将拥有 sys.modules = {..., 'p.a': <module 'p.a' from '.../project/a.py'>, ...}
.
- 模块代码已执行。
-
a
属性添加到 project
。
现在,这是 import project.a
和 from project import a
之间的区别:
import project.a
只是寻找 sys.modules['project.a']
。如果存在,它将使用 sys.modules['project']
. 绑定名称 project
from project import a
查找 sys.modules['project']
,然后检查 project
模块是否具有 a
属性。
您可以将 from project import a
视为等同于以下两行:
import project.a # not problematic
a = project.a # causes an error
这就是为什么您仅在执行 from project import a
时才会看到异常:sys.modules['project.a']
存在,但 project
还没有 a
属性。
最快的解决方案就是简单地避免循环导入。但如果你不能,那么通常的策略是:
尽可能晚导入。假设您的 a.py
看起来像这样:
from project import b
def something():
return b.something_else()
改写如下:
def something():
from project import b
return b.something_else()
当然,您必须在所有函数中重复 import
s。
使用延迟导入。惰性导入不是 Python 的标准功能,但您可以找到许多实现。它们使用 "import as late as possible" 原理工作,但它们添加了一些语法糖让您编写更少的代码。
作弊,使用sys.modules
,像这样:
import sys
import project.a
a = sys.modules['project.a']
非常un-pythonic,但有效。
显然,无论您选择什么解决方案,在模块完全加载之前,您都无法从 a
或 b
访问属性。
给定这个简单的文件夹结构
/main.py
/project/a.py
/project/b.py
main.py
由 python 解释器执行并包含一行,import project.a
.
a
和b
是模块,需要相互导入。实现这一目标的一种方法是
import project.[a|b]
使用更深的嵌套文件夹结构时,您不希望每次使用模块时都写下整个路径,例如
import project.foo.bar
project.foo.bar.set_flag(project.foo.bar.SUPER)
from project import [a|b]
和 import project.[a|b] as [a|b]
都会导致导入错误(当同时用于 a
和 b
时)。
标准导入语法与 from
或 as
语法有何不同?为什么只有标准语法适用于相互导入?
更重要的是,是否有一种简单干净的方法来导入模块,允许相互导入并为它们分配更短的名称(理想情况下是模块基本名称,例如 bar
在 project.foo.bar
的情况下) ?
当您执行 import project.a
或 from project import a
时,会发生以下情况:
project.a
的模块对象被放入sys.modules
。这是一个将每个模块名称映射到其模块对象的字典,因此您将拥有sys.modules = {..., 'p.a': <module 'p.a' from '.../project/a.py'>, ...}
.- 模块代码已执行。
-
a
属性添加到project
。
现在,这是 import project.a
和 from project import a
之间的区别:
import project.a
只是寻找sys.modules['project.a']
。如果存在,它将使用sys.modules['project']
. 绑定名称 from project import a
查找sys.modules['project']
,然后检查project
模块是否具有a
属性。您可以将
from project import a
视为等同于以下两行:import project.a # not problematic a = project.a # causes an error
project
这就是为什么您仅在执行 from project import a
时才会看到异常:sys.modules['project.a']
存在,但 project
还没有 a
属性。
最快的解决方案就是简单地避免循环导入。但如果你不能,那么通常的策略是:
尽可能晚导入。假设您的
a.py
看起来像这样:from project import b def something(): return b.something_else()
改写如下:
def something(): from project import b return b.something_else()
当然,您必须在所有函数中重复
import
s。使用延迟导入。惰性导入不是 Python 的标准功能,但您可以找到许多实现。它们使用 "import as late as possible" 原理工作,但它们添加了一些语法糖让您编写更少的代码。
作弊,使用
sys.modules
,像这样:import sys import project.a a = sys.modules['project.a']
非常un-pythonic,但有效。
显然,无论您选择什么解决方案,在模块完全加载之前,您都无法从 a
或 b
访问属性。