在Python中导入带参数的模块
在Python中,是否可以带参数导入一个模块呢?
我说的参数是指在模块里有一个变量,这个变量在模块内部没有被初始化,但我还是想在这个模块里使用它。简单来说,我想要的效果像是一个函数,但不同于函数的是,我希望模块里的变量能在调用的代码中直接使用。
比如说有一个文件叫a.py
:
#lists like data, count, prob_distribution are constructed from training_pool (not initialized in this file)
x = pymc.Uniform('x', lower = 0, upper = 1)
rv = [ Multinomial("rv"+str(i), count[i], prob_distribution[i], value = data[i], observed=True) for i in xrange(0, len(count)) ]
还有一个文件叫b.py
:
import a #I want some way tr pass value of training_pool
m = pymc.MCMC(a)
我希望a.py
里的所有随机变量都能在MCMC
中使用。我也愿意接受更好的解决方案,但我想知道在Python中,给模块传递参数是否可行。
7 个回答
模块级的全局变量通常能满足大多数需求,但如果遇到以下情况呢:
- 参数需要在模块初始化时进行评估,或者
- 你需要多个不同参数的模块版本
在最近的Python版本中,可以分两步加载模块,先加载模块的规格(spec),然后再执行(exec)。在这两步之间,你可以设置额外的变量。
import importlib
abstractModuleSpec=importlib.util.find_spec('myModule')
module4=importlib.util.module_from_spec(abstractModuleSpec)
module2=importlib.util.module_from_spec(abstractModuleSpec)
module2.parameter="you are version 2"
module4.parameter="you are version 4"
module4.__spec__.loader.exec_module(module4)
module2.__spec__.loader.exec_module(module2)
在模块中,你可以使用 dir()
或类似的命令,检查这个变量是否已经定义。
我发现定义全局变量很有用,并且可以通过一个初始化函数来设置这些变量。
def init(config_filename=CONFIG_FILENAME):
config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
config.read(config_filename)
global YEARS
YEARS = config['DEFAULT']['YEARS']
global FEATURES
FEATURES = config['DEFAULT']['FEATURES']
这样,用户只需要记得在使用这些方法之前先初始化这个模块就可以了:
import module
module.init('config.ini')
需要注意的是,我不会在我希望公开分享的模块上使用这种方法。这种做法更适合我个人使用的单文件模块。
模块之间不能直接传递参数。不过,你可以在第三个模块中使用一个全局变量来实现这个目的:
# a.py
parameter = None
# b.py
import a
a.parameter = 4
import c
# c.py
import a
# use a.parameter
当然,这种方法只有在没有其他地方导入模块c的情况下才有效,因为模块只会被导入一次。
有很多方法可以做到这一点,这里介绍一个简单又傻乎乎的方法:
main.py
"""A silly example - main supplies a parameter
"""
import sys,os
print os.path.basename(__file__)+":Push it by: --myModuleParam "+str(123)
sys.argv.append('--myModuleParam')
sys.argv.append(123)
import module
print os.path.basename(__file__)+":Pushed my param:"+str(module.displayMyParam)
module.py
"""A silly example - module consumes parameter
"""
import sys,os
displayMyParam = 'NotYetInitialized'
for px in sys.argv:
if px == '--myModuleParam':
idx = sys.argv.index(px)
sys.argv.pop(idx) # remove option
displayMyParam = sys.argv[idx]
sys.argv.pop(idx) # remove value
print os.path.basename(__file__)+":Got my param:"+str(displayMyParam)
#
# That's it...
#
正如@otus已经回答的那样,模块是无法传递参数的。
我觉得你可能在看一些PyMC2的入门示例,这些示例使用了一种模式,把贝叶斯模型中所有节点的代码都放在一个模块里。这种方法适合刚开始学习,但正如你发现的那样,当你想用不同的变体来运行模型时,这种方法会有些局限。
幸运的是,PyMC2可以从列表或字典中创建一个MCMC对象,也可以从模块中创建。在这种情况下,我建议你按照@oleg-s在评论中提到的做法:使用一个函数。你可以在函数的最后加上return locals()
,这样就能得到一个包含模块中所有内容的字典,这个字典可以作为pymc.MCMC
构造函数的输入。下面是一个例子:
# a.py
from pymc import *
count = [10, 10] # perhaps good to put this stuff in data.py
prob_distribution = [[.5, .5], [.1, .2, .7]]
data = [[2, 8], [2, 3, 5]]
def model(training_pool):
x = Uniform('x', lower = 0, upper = 1)
rv = [ Multinomial("rv"+str(i), count[i], prob_distribution[i], value = data[i], observed=True) for i in training_pool ]
return locals()
# b.py
import pymc, a
training_pool = [0]
m = pymc.MCMC(a.model(training_pool))