【问题标题】:How do you dynamically load python classes from a given directory?如何从给定目录动态加载 python 类?
【发布时间】:2025-11-28 14:45:01
【问题描述】:

如果我定义一个模块module,其对应目录为module/,我可以从a.pyb.py等子模块动态加载类吗?

--模块
----a.py
----b.py

这需要知道要搜索的类名吗?我可以设置一个以某种方式加载这些孩子的基类吗?

基本用例是允许用户编写一些他或她自己的代码以供程序加载。就像 rails 允许您在某些目录中编写自己的控制器、视图和模型一样。

到目前为止我动态加载模块的代码是

def load(folder):
    files = {}
    for filename in os.listdir(folder):
      if (filename[0] != '_' and filename[0] != '.'):
        files[filename.rstrip('.pyc')] = None

    # Append each module to the return list of modules
    modules = []
    mod = __import__(folder, fromlist=files.keys())
    for key in files.keys():
      modules.append(getattr(mod, key))

    return modules

我希望修改它以返回类对象。

【问题讨论】:

  • 如果您查看其他 SE questions 关于 python 模块数据结构的信息,第三个答案给出了一个很好的简短答案。我想你可以做一些事情:from module import a 动态。

标签: python import


【解决方案1】:
#!/usr/bin/env python

import os
import sys
import inspect

def load_modules_from_path(path):
   """
   Import all modules from the given directory
   """
   # Check and fix the path
   if path[-1:] != '/':
       path += '/'

   # Get a list of files in the directory, if the directory exists
   if not os.path.exists(path):
        raise OSError("Directory does not exist: %s" % path)

   # Add path to the system path
   sys.path.append(path)
   # Load all the files in path
   for f in os.listdir(path):
       # Ignore anything that isn't a .py file
       if len(f) > 3 and f[-3:] == '.py':
           modname = f[:-3]
           # Import the module
           __import__(modname, globals(), locals(), ['*'])

def load_class_from_name(fqcn):
    # Break apart fqcn to get module and classname
    paths = fqcn.split('.')
    modulename = '.'.join(paths[:-1])
    classname = paths[-1]
    # Import the module
    __import__(modulename, globals(), locals(), ['*'])
    # Get the class
    cls = getattr(sys.modules[modulename], classname)
    # Check cls
    if not inspect.isclass(cls):
       raise TypeError("%s is not a class" % fqcn)
    # Return class
    return cls

def main():
    load_modules_from_path('modules')
    # load the TestClass1
    class_name = load_class_from_name('class1.TestClass1')
    # instantiate the Testclass1
    obj = class_name()
    # using this object obj to call the attributes inside the class
    print obj.testclass1()

if __name__ == '__main__': main()

在 modules 目录中,我还有另外两个模块用于测试:

[♫ test] modules :~ pwd
/tmp/dynamic_loader/modules

[♫ test] modules :~ ls -lR
total 32
-rw-r--r--  1 staff  staff  138 Aug 30 21:10 class1.py
-rw-r--r--  1 staff  staff  575 Aug 30 21:11 class1.pyc
-rw-r--r--  1 staff  staff  139 Aug 30 21:11 class2.py
-rw-r--r--  1 staff  staff  576 Aug 30 21:11 class2.pyc

[♫ test] modules  cat class1.py

class TestClass1(object):
  def testclass1(self):
      print 'I am from testclass1'

  def some_function():
      print 'some function 1'

【讨论】:

    【解决方案2】:

    您正在寻找pkgutil.walk_packages。使用它,您可以执行以下操作:

    def load(root_import_path, is_valid=lambda entity: True):
        """Returns modules in ``root_import_path`` that satisfy the ``is_valid`` test
    
        :param root_import_path: An string name for importing (i.e. "myapp").
        :param is_valid: A callable that takes a variable and returns ``True``
                         if it is of interest to us."""
    
        prefix = root_import_path + u"."
        modules = []
    
        for _, name, is_pkg in walk_packages(root_import_path, prefix=prefix):
            if is_pkg: 
                continue
            module_code = __import__(name)
            contents = dir(module_code)
            for thing in contents:
                if is_valid(thing):
                    modules.append(thing)
    
        return modules
    

    或者,如果您不介意使用依赖项,可以尝试使用 straight.plugin 加载器,它比这个简单的 load 函数要复杂一些。

    【讨论】:

      【解决方案3】:

      以下两个模块一起工作,做类似的事情。基本上,您可以dir() 您的模块并检查使用getattr 检索到的类的类类型。

      http://code.google.com/p/pycopia/source/browse/trunk/QA/pycopia/QA/shellinterface.py

      http://code.google.com/p/pycopia/source/browse/trunk/aid/pycopia/module.py

      【讨论】:

        最近更新 更多