[英]“from src import *” dynamic import for all functions in a package or subpackage
Goal: 目标:
I want to be able to dynamically import all functions in a sub-package with 'a direct call' 我希望能够通过“直接调用”在子包中动态导入所有功能
Usage: 用法:
my project: 我的项目:
project/
|-- main.py
|-- src/
|---- __init__.py
|---- foo.py
|---- bar.py
foo.py
has just one function: foo.py
只有一个功能:
def foo_funct():
print("foo")
bar.py
has just one function: bar.py
只有一个功能:
def bar_funct():
print("bar")
and finally main.py
: 最后是
main.py
:
from src import *
(...)
foo_funct()
bar_funct()
(...)
Comments: 评论:
if my __init__.py
is something like this 如果我的
__init__.py
是这样的
import os __all__ = [i.replace(".py", "") for i in os.listdir(os.getcwd()+"/src/") if "__" not in i]
I will be able to call foo.foo_funct()
or bar.bar_funct()
but not foo_funct()
or bar_funct()
我将能够调用
foo.foo_funct()
或bar.bar_funct()
但不能foo_funct()
或bar_funct()
if my __init__.py
is something like this: 如果我的
__init__.py
是这样的:
from src.foo import * from src.bar import *
I will be able to call foo_funct()
or bar_funct()
but for each new sub package I will have to modify my __init__.py
我将能够调用
foo_funct()
或bar_funct()
但是对于每个新的子包,我都必须修改__init__.py
Assuming that from src import *
is not the most pythonic method, and assuming that it could be very dangerous to have direct calls due to possible naming conflicts such as a.tree_funct()
and b.tree_funct()
, is there any method to reach my goal? 假设
from src import *
并不是大多数pythonic方法,并且假设由于可能的命名冲突(例如a.tree_funct()
和b.tree_funct()
而进行直接调用可能非常危险,是否有任何方法可以实现我的目标?
Personally, I prefer to keep things explicit, and just import names that are part of the package API into __init__
explicitly. 就个人而言,我更喜欢保持明确,只将包API一部分的名称明确地导入
__init__
。 Your project won't change so rapidly that dynamically importing everything into the __init__.py
is going to be a time saver. 您的项目变化不会太快,以至于将所有内容动态导入
__init__.py
将节省时间。
But if you want to do this, then you have a few options here. 但是,如果您想这样做,那么这里有一些选择。 If you need to support Python versions older than 3.7, then you can update the package namespace by poking at the
globals()
dictionary . 如果需要支持3.7之前的Python版本,则可以通过在
globals()
字典中戳来更新包名称空间。 List all .py
files and import them using importlib.import_module()
(or with __import__()
if you need to support Python versions before 2.7): 列出所有
.py
文件,并使用importlib.import_module()
(或如果需要支持2.7之前的Python版本,则使用__import__()
导入它们:
__all__ = []
def _load_all_submodules():
from pathlib import Path
from importlib import import_module
g = globals()
package_path = Path(__file__).resolve().parent
for pyfile in package_path.glob('*.py'):
module_name = pyfile.stem
if module_name == '__init__':
continue
module = import_module(f'.{module_name}', __package__)
names = getattr(
module, '__all__',
(n for n in dir(module) if n[:1] != '_'))
for name in names:
g[name] = getattr(module, name)
__all__.append(name)
_load_all_submodules()
del _load_all_submodules
The above keeps the namespace clean; 以上内容使命名空间保持整洁; after the
_load_all_submodules()
function has run it is removed from the package. _load_all_submodules()
函数运行后,将其从软件包中删除。 It uses the __file__
global to determine the current path and finds any sibling .py
files from there. 它使用
__file__
全局来确定当前路径,并从那里找到任何同级的.py
文件。
If you only need to support Python 3.7 and up, you can define module-level __getattr__()
and __dir__()
functions to implement dynamic lookups. 如果只需要支持Python 3.7及更高版本,则可以定义模块级
__getattr__()
和__dir__()
函数来实现动态查找。
Using those hooks in your package __init__.py
file could look like: 在包
__init__.py
文件中使用这些钩子看起来像:
def _find_submodules():
from pathlib import Path
from importlib import import_module
package_path = Path(__file__).resolve().parent
return tuple(p.stem for p in package_path.glob('*.py') if p.stem != '__init__')
__submodules__ = _find_submodules()
del _find_submodules
def __dir__():
from importlib import import_module
names = []
for module_name in __submodules__:
module = import_module(f'.{module_name}', __package__)
try:
names += module.__all__
except AttributeError:
names += (n for n in dir(module) if n[:1] != '_')
return sorted(names)
__all__ = __dir__()
def __getattr__(name):
from importlib import import_module
for module_name in __submodules__:
module = import_module(f'.{module_name}', __package__)
try:
# cache the attribute so future imports don't call __getattr__ again
obj = getattr(module, name)
globals()[name] = obj
return obj
except AttributeError:
pass
raise AttributeError(name)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.