在Python中通过变量动态导入相对脚本
在我的主要Python脚本中,我会调用一个网络API。这个调用的结果决定了需要运行哪个“协议”。这些协议是放在一个叫做protocols/
的子文件夹里的独立Python脚本。
那么,从我的主脚本中,我该如何动态地调用其中一个脚本呢?脚本的名字是从API得到的一个变量,而且它是在主脚本的子文件夹里,而不是在标准库的位置。
我可以使用subprocess.popen
或者os.system
来实现,但我希望尽量保持所有内容都在内部处理。
总得有办法在一个Python实例中完成这个吧,对吧?
3 个回答
是的,可以在一个Python实例中实现这个功能。你可以动态加载单独的Python脚本,方法如下:
- 使用
importlib.import_module()
动态加载模块。 - 使用
getattr()
动态加载模块和类。
方法一
如果你只想动态加载模块,可以使用 importlib.import_module()
。假设你在一个名为 protocols 的子文件夹中有以下协议:
protocols/protocol_1.py 里面有以下类:
class Protocol():
# some protocol specific code
protocols/protocol_2.py 里面有以下类:
class Protocol():
# some protocol specific code
首先,我们定义动态变量,变量名是从Web API返回的协议名称:
module = 'protocols.protocol_1'
然后我们导入importlib并动态加载模块。两个文件中的Protocol类名字相同,但里面的代码是针对不同协议的。接着,我们用动态加载的Protocol实例化api_protocol:
import importlib
Protocol = importlib.import_module(module).Protocol
api_protocol = Protocol()
方法二
如果你还想动态加载类,可以使用getattr。假设你在 protocols 子文件夹中有以下协议:
protocols/protocol_1.py 里面有以下类:
class Protocol_1():
# some protocol specific code
protocols/protocol_2.py 里面有以下类:
class Protocol_2():
# some protocol specific code
首先,我们定义动态变量,变量名是从Web API返回的协议名称:
module = 'protocols.protocol_1'
class_name = 'Protocol_1'
然后我们调用 __import__
函数来动态加载一个动态 模块 的 类。之后,我们可以使用 getattr
创建新的类,并用动态加载的Protocol实例化api_protocol:
mod = __import__(module, fromlist=[class_name])
Protocol = getattr(mod, class_name)
api_protocol = Protocol()
Python有一个内置的函数,可以用来调用脚本。
这个函数叫做 execfile
,你可以在这里了解更多信息:
https://docs.python.org/2/library/functions.html#execfile
假设我有一个文件叫 testmeagain.py
,里面的内容是:
print "test print"
你可以在不导入额外库的情况下做到这一点:
In [1]: execfile("testmeagain.py")
test print
假设你从一个文件夹里运行你的主Python程序,这个文件夹里有一个叫做protocols
的子文件夹。你需要在protocols
文件夹里添加一个空的__init__.py
文件,这样它就变成了一个包。
之后,你就可以从这个包里导入特定的模块,比如:
from protocols.http import process_it
在这里,我假设你会在里面有一个叫process_it
的函数。
还有其他方法可以导入包,方法名是通过变量定义的,具体可以参考这个SO的回答 点击这里查看链接。
在你的情况下,代码可能看起来像这样:
def process_it(protocol="http"):
mod = __import__("protocols." + protocol)
mod.process_it()