2012-05-10 45 views
6

如果我定義了一個模塊模塊與對應的目錄module/,我可以動態加載子模塊的類,如a.pyb.py如何從給定目錄動態加載python類?

--module
----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 

我希望能修改它返回類對象。

+0

如果您檢查其他[SE問題](http://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application)關於python模塊數據結構,第三個答案給出了一個很好的簡短答案。我猜你可能會做一些事情:'從模塊導入動態'。 – Zenon

回答

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 

Alternatly,如果你不介意的依賴關係,你可以試試straight.plugin裝載機,比這個簡單load功能稍微複雜一點。

2
#!/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'