我正在尝试创建一个参数扫描模式,使用python的exec()函数并通过命名空间传递给它一个特定的参数集。我的应用程序是带有一堆参数的scipy.integrate.odeint()模型,但是我尝试创建SSCCE测试用一个简单的参数报告函数替换它。同样,我没有包括完整的参数和范围,因为这些例子使这些显而易见。
def rptParam():
print 'rptParam: v=%e b0=%e drme=%e de=%e TGrate=%f IPer=%d' % (v,b0,drme,de,TGrate,IPer)
def tstIterExec(argList, argRange, seed):
seedTup = tuple([seed[k] for k in argList])
print 'tstIterExec: seed', seedTup
seed['rptParam'] = rptParam
tstCode = compile("rptParam()", '<string>','exec')
for param,v in seed.items():
if param not in argList:
print 'tstIterExec: skipping param', param
continue
print 'tstIterExec: varying', param
ai = argList.index(param)
spos = argRange[ai].index(v)
# higher values of param
for vi in range(spos+1,len(argRange[ai])):
currContext = seed.copy()
currContext[param] = argRange[ai][vi] # perturb just this value of seed
seedTup = tuple([currContext[k] for k in argList]) # a hashable version of the seed
print 'tstIterExec: perturb', param, seedTup
exec tstCode in globals(), currContext
tstArgList = ['v','b0']
tstArgRange = [v_Range, b0_Range]
TstSeed = {'v':3e-2, 'b0':2e-11, 'drme':5e-2, 'de':0.8 ,'TGrate': 0.2, 'IPer':10}
tstIterExec(tstArgList, tstArgRange, TstSeed)
运行此代码会产生以下输出:
tstIterExec: seed (0.03, 2e-11)
tstIterExec: skipping param drme
tstIterExec: skipping param TGrate
tstIterExec: skipping param de
tstIterExec: varying b0
tstIterExec: perturb b0 (0.03, 4e-11)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: perturb b0 (0.03, 8e-11)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: perturb b0 (0.03, 1e-10)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: perturb b0 (0.03, 1e-09)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: varying v
tstIterExec: perturb v (0.032, 2e-11)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: perturb v (0.048, 2e-11)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: perturb v (0.064, 2e-11)
rptParam: v=3.200000e-02 b0=2.000000e-11 drme=5.000000e-02 de=8.500000e-01 TGrate=0.200000 IPer=10
tstIterExec: skipping param rptParam
tstIterExec: skipping param IPer
显然,currContext在tstIterExec()中发生了变化,但rptParam()坚持使用全局绑定?我也试过在currContext中发送第一个全局命名空间;同样的行为。
我一定是误解了python的命名空间/闭包概念?是exec()甚至适用于此目的的技术?
答案 0 :(得分:0)
传递给globals
语句的locals
和exec
仅影响exec
正在执行的直接代码对象;它们不会影响代码对象调用的任何函数。 rptParam
看到的全局变量仍然是模块全局变量,因此它模块中的v
等打印值。
据我所知,最好的选择就是接受参数作为参数:
def rptParam(v, ...):
...
...
print 'tstIterExec: perturb', param, seedTup
rptParam(**currContext)