假设我在一个公共目录下有一堆Python文件,但是在其中,可以存在任意数量的子目录:
/first/foo.py
/first/bar.py
/first/fizz/buzz.py
/first/numbers/one.py
/first/numbers/two.py
我有一些任意文件要导入所有这些文件。手动地,我可以做:
import first.foo
import first.bar
import first.fizz.buzz
import first.numbers.one
import first.numbers.two
但是相反,我希望能够执行以下操作:
import_everything_under('first')
我知道已经出现了类似的问题:Recursively import all .py files from all folders
但是给出的答案和所谓的重复项无法回答这个问题。
该问题被标记为可能的重复项:How to load all modules in a folder?
同样,这不能回答这个问题。该问题的答案不是递归的-它只会从即时目录中导入项目,并且不会在子目录中包含脚本,这是我的用例所需要的。
最佳答案
从我的角度来看,您的问题分为两个步骤:
浏览目录和子目录,找到要导入的所有.py文件的名称
导入它们!
为了解决(1),我们可以使用os.walk
方法找到所有.py文件。该函数如下所示:
import os
def get_py_files(src):
cwd = os.getcwd() # Current Working directory
py_files = []
for root, dirs, files in os.walk(src):
for file in files:
if file.endswith(".py"):
py_files.append(os.path.join(cwd, root, file))
return py_files
现在您有了.py文件的列表,我们需要一个可以动态导入它们的函数。
import importlib
def dynamic_import(module_name, py_path):
module_spec = importlib.util.spec_from_file_location(module_name, py_path)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
现在,我们只需要将所有内容放在一起,编写一个调用您的
get_py_files
函数,然后遍历结果的函数,并使用dynamic_import
加载模块。我假设您希望在python脚本中使用的模块名称与文件名相同,但是您可以通过修改以下函数中的
module_name
变量来更改它。def dynamic_import_from_src(src, star_import = False):
my_py_files = get_py_files(src)
for py_file in my_py_files:
module_name = os.path.split(py_file)[-1].strip(".py")
imported_module = dynamic_import(module_name, py_file)
if star_import:
for obj in dir(imported_module):
globals()[obj] = imported_module.__dict__[obj]
else:
globals()[module_name] = imported_module
return
注意,我们必须调用
globals()
将模块添加到全局名称空间。不这样做,模块将被导入,但是您将无法访问其中的任何内容。如果希望将其作为星号导入,则可以将star_import = True
传递给dynamic_import_from_src
。 (例如from first.foo import *
。请注意,这可能会覆盖名称空间中的变量,但这仍然是使用*
导入的缺点之一。将所有内容扔在一块,以便一次查看所有内容变得更容易:
import os
import importlib
def get_py_files(src):
cwd = os.getcwd() # Current Working directory
py_files = []
for root, dirs, files in os.walk(src):
for file in files:
if file.endswith(".py"):
py_files.append(os.path.join(cwd, root, file))
return py_files
def dynamic_import(module_name, py_path):
module_spec = importlib.util.spec_from_file_location(module_name, py_path)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
def dynamic_import_from_src(src, star_import = False):
my_py_files = get_py_files(src)
for py_file in my_py_files:
module_name = os.path.split(py_file)[-1].strip(".py")
imported_module = dynamic_import(module_name, py_file)
if star_import:
for obj in dir(imported_module):
globals()[obj] = imported_module.__dict__[obj]
else:
globals()[module_name] = imported_module
return
if __name__ == "__main__":
dynamic_import_from_src("first", star_import = False)
此时,您可以像导入
import first.whatever
一样完全访问导入的任何模块。例如,如果first/numbers/one.py
包含x=1
,则可以说出one.x
来访问它。关于python - 如何动态从给定目录和所有子目录导入所有* .py文件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57878744/