简体   繁体   English

在Python库中动态加载模块的最佳方法

[英]Best way to dynamically load modules in a Python library

EDIT: Turns out the problem has to do with the path. 编辑:原来问题与路径有关。

If I cd into the directory containing the library and run python __init__.py the imports all work fine. 如果我进入包含该库的目录并运行python __init__.py则所有导入都可以正常工作。 It's if I'm in a different directory and try to import the library itself (ie in the parent directory, and trying to import) that the failure occurs. 如果我在其他目录中并尝试导入库本身(即在父目录中并尝试导入),则会发生故障。

I don't see any way to literally specify a path for an import statement. 我看不到任何从字面上指定import语句路径的方法。

So, I'm wondering if the best way is just to add the directory in scriptDir to the sys.path? 因此,我想知道最好的方法是否只是将scriptDir中的目录添加到sys.path中? Is this the best way to do it? 这是最好的方法吗? I feel like there should be a more elegant method, but... 我觉得应该有一个更优雅的方法,但是...


I want to write a library that I will be able to extend very easily. 我想写一个可以很容易扩展的库。

Here's some skeleton/pseudo code for what I want to do. 这是我想要做的一些基本代码/伪代码。 In reality this code is a lot more complex, but it follows the basic premise - import each file, check it, and determine if we should use it; 实际上,此代码要复杂得多,但是它遵循基本前提-导入每个文件,检查并确定是否应使用它; then allocate it into a list of module references. 然后将其分配到模块引用列表中。 All of this would be contained in a single library folder. 所有这些都将包含在一个库文件夹中。

I want the library, when imported, to dynamically import any file found in its directory starting with "plugin_". 我希望库在导入时能够动态导入在其目录中以“ plugin_”开头的任何文件。 See the code: 看代码:

init .py: 初始化 .py:

import os.path

scriptDir = os.path.dirname(__file__)
mods = []
thisMod = 0

for file in os.listdir(scriptDir):
    if (file[0:7] == "plugin_" and file[-3:] == ".py"):
        thisMod = __import__(".".join(file.split(".")[0:-1]))
        print "debug: imported %s" % thisMod.modName
        if (thisMod.enable == True):
            mods.append(thisMod)
        else:
            print "debug: not loading %s because it's disabled." % thisMod.modName

def listMods():
    "This function should print the names of all loaded modules."
    for m in mods:
        print "debug: module %s" % m.modName

def runMods():
    "This function should execute the run method for ALL modules."
    for m in mods:
        c = m.ModuleClass()
        c.run()

def selectMod(modNum):
    "This function should let us select a single module for the runSelectedMod function."
    thisMod = mods[modNum]

def runSelectedMod():
    "This function should run the 'run' method for ONLY the previously selected module."
    if (thisMod == 0):
        raise ArgumentError("you didn't assign a module yet.")
    c = thisMod.ModuleClass()
    c.run()

plugin_test1.py plugin_test1.py

modName = "test module 1"
enable = True
class ModuleClass:
    def run(self):
        print "test module 1 is running"

plugin_math.py plugin_math.py

modName = "math module"
enable = True
class ModuleClass:
    def run(self):
        print "mathematical result: %d" % (1+1)

plugin_bad.py plugin_bad.py

modName = "bad module"
enable = False
class ModuleClass:
    def __init__(self):
        print "x"[4] # throws IndexError, this code should not run.
    def run(self):
        print "divide by zero: %f" % (5 / 0) # this code should not run.

The problem I've already found is import won't work since I'm not importing whole libraries, but rather individual files. 我已经发现的问题是导入不会起作用,因为我没有导入整个库,而是导入了单个文件。 I'm guessing there is either an alternate syntax to import for this purpose? 我猜测是否有为此目的导入的替代语法? For example, import plugin_test and from plugin_math import ModuleClass work but my use of import isn't. 例如, import plugin_testfrom plugin_math import ModuleClass起作用,但是我对import的使用却不行。 I get an error: 我收到一个错误:

thisMod = __import__(".".join(file.split(".")[0:-1]))
ImportError: No module named plugin_test1

Now, another question is: How will this end up working if I use py2exe, py2app, etc. to compile this into a compact library? 现在,另一个问题是:如果我使用py2exe,py2app等将其编译为紧凑型库,这将如何工作? If I recall, don't these apps compress all the local libraries into a site_packages.zip file?... 如果我还记得,这些应用程序是否不将所有本地库压缩到site_packages.zip文件中?

I'm still learning how to do this type of advanced coding in Python, so any advice is appreciated. 我仍在学习如何在Python中进行此类高级编码,因此请多多指教。

Thanks! 谢谢!

I was able to run it in Python 3. The only change not regarding syntax is the scriptDir : 我能够在Python 3中运行它。唯一不涉及语法的更改是scriptDir

import os.path

scriptDir = os.path.dirname(os.path.abspath(__file__))
mods = []
thisMod = 0

for file in os.listdir(scriptDir):
    if (file[0:7] == "plugin_" and file[-3:] == ".py"):
        thisMod = __import__(".".join(file.split(".")[0:-1]))
        print ("debug: imported %s" % thisMod.modName)
        if (thisMod.enable == True):
            mods.append(thisMod)
        else:
            print ("debug: not loading %s because it's disabled." % thisMod.modName)

def listMods():
    "This function should print the names of all loaded modules."
    for m in mods:
        print ("debug: module %s" % m.modName)

def runMods():
    "This function should execute the run method for ALL modules."
    for m in mods:
        c = m.ModuleClass()
        c.run()

def selectMod(modNum):
    "This function should let us select a single module for the runSelectedMod function."
    thisMod = mods[modNum]

def runSelectedMod():
    "This function should run the 'run' method for ONLY the previously selected module."
    if (thisMod == 0):
        raise ArgumentError("you didn't assign a module yet.")
    c = thisMod.ModuleClass()
    c.run()

gives: 给出:

C:\Users\mm\Desktop\ING.SW\python>python so\dina.py
debug: imported bad module
debug: not loading bad module because it's disabled.
debug: imported math module
debug: imported test module 1

I guess you have a different problem, since the file name is actually read (as it's printed in the error message), but still... 我猜您有一个不同的问题,因为实际上是读取了文件名(因为它显示在错误消息中),但是仍然...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM