从 __init__.py 中访问导入语句(或导入子模块时如何不 运行 __init__.py)
access to import statement from within __init__.py (or how to not run __init__.py when importing submodule)
我的包裹看起来像这样:
/mypackage
|-- __init__.py
|-- moduleA.py
|-- moduleB.py
|-- oldModule.py
而 __init__.py
看起来像这样:
from mypackage.moduleA import abc
from mypackage.moduleB import jkl
这允许用户简单地编码如下:
import mypackage as mpkg
mpkg.abc()
mpkg.jkl()
默认情况下(使用上述 import
)用户无法访问 oldModule.py
中的功能。
这是因为 95% 的用户不需要那个东西(它很旧)。
需要oldModule功能的那5%的用户,可以这样写:
from mypackage.oldModule import xyz
xyz()
许多 "oldModule" 用户不需要访问 __init__.py
导入的功能
问题是,当用户只执行 from mypackage.oldModule import xyz
时,很明显整个 __init__.py
也在 运行。
有什么办法可以吗
- 将其设置为
__init__.py
不会 运行 如果用户 from mypackage.oldModule import ...
、 或
- 我可以从
__init__.py
,
中 查看 导入语句 的结构,以便我可以检测到用户做了 from mypackage.oldModule import ...
,
然后我可以在 __init__.py
中使用 if
块来决定代码的哪些部分 运行 ??
谢谢。
无法完全按照您的要求进行操作。当您导入 some_package.some_module
时,包总是在子模块之前完全加载。
但可能有一些方法可以缓解这在您的案例中引起的问题。如果你想避免在用户寻找 oldModule
时导入 moduleA
和 moduleB
子模块,你或许可以让他们的加载延迟,而不是急切。
从 Python 3.7 开始,PEP 562 使模块具有名为 __getattr__
的函数,该函数将像 class 中定义的同名方法一样工作.当查找模块的属性但未找到时,将使用名称调用 __getattr__
函数。
所以在您的 __init__.py
文件中,您可以:
# from mypackage.moduleA import xyz don't do these imports unconditionally any more
# from mypackage.moduleB import abc
def __getattr__(name):
global xyz, abc
if name == 'xyz':
from .moduleA import xyz
return xyz
elif name == 'abc':
from .moduleB import abc
return abc
raise AttributeError(f"module {__name__} has no attribute {name}")
如果您有更多名称需要以这种方式保护(不仅仅是两个),您可能需要编写更通用的代码来处理名称列表(可能每个子模块一个)并导入它们正确使用 importlib
并直接写入包的 __dict__
。这比尝试使用 global
语句并为每个名称编写单独的 if
分支更方便。
我的包裹看起来像这样:
/mypackage
|-- __init__.py
|-- moduleA.py
|-- moduleB.py
|-- oldModule.py
而 __init__.py
看起来像这样:
from mypackage.moduleA import abc
from mypackage.moduleB import jkl
这允许用户简单地编码如下:
import mypackage as mpkg
mpkg.abc()
mpkg.jkl()
默认情况下(使用上述 import
)用户无法访问 oldModule.py
中的功能。
这是因为 95% 的用户不需要那个东西(它很旧)。
需要oldModule功能的那5%的用户,可以这样写:
from mypackage.oldModule import xyz
xyz()
许多 "oldModule" 用户不需要访问 __init__.py
问题是,当用户只执行 from mypackage.oldModule import xyz
时,很明显整个 __init__.py
也在 运行。
有什么办法可以吗
- 将其设置为
__init__.py
不会 运行 如果用户from mypackage.oldModule import ...
、 或 - 我可以从
__init__.py
,
中 查看 导入语句 的结构,以便我可以检测到用户做了from mypackage.oldModule import ...
,
然后我可以在__init__.py
中使用if
块来决定代码的哪些部分 运行 ??
谢谢。
无法完全按照您的要求进行操作。当您导入 some_package.some_module
时,包总是在子模块之前完全加载。
但可能有一些方法可以缓解这在您的案例中引起的问题。如果你想避免在用户寻找 oldModule
时导入 moduleA
和 moduleB
子模块,你或许可以让他们的加载延迟,而不是急切。
从 Python 3.7 开始,PEP 562 使模块具有名为 __getattr__
的函数,该函数将像 class 中定义的同名方法一样工作.当查找模块的属性但未找到时,将使用名称调用 __getattr__
函数。
所以在您的 __init__.py
文件中,您可以:
# from mypackage.moduleA import xyz don't do these imports unconditionally any more
# from mypackage.moduleB import abc
def __getattr__(name):
global xyz, abc
if name == 'xyz':
from .moduleA import xyz
return xyz
elif name == 'abc':
from .moduleB import abc
return abc
raise AttributeError(f"module {__name__} has no attribute {name}")
如果您有更多名称需要以这种方式保护(不仅仅是两个),您可能需要编写更通用的代码来处理名称列表(可能每个子模块一个)并导入它们正确使用 importlib
并直接写入包的 __dict__
。这比尝试使用 global
语句并为每个名称编写单独的 if
分支更方便。