在 Python 中传递参数化函数句柄
Passing parameterized function handle in Python
我有一个通用函数,它定义了我计划使用 scipy.integrate.odeint
集成的 ODE 形式,例如:
def my_ode(K, tau, y, u):
return K*u/tau - y/tau # dydt
我的代码中有几个对象,它们都具有 my_ode
中定义的形式的动态,但具有独特的参数 K
和 tau
。我希望能够将一个唯一的句柄传递给 my_ode
,并且在我初始化我的对象时已经设置了这些参数,这样当我更新我的对象时,我所要做的就是像 soln = odeint(my_ode, t, y, u)
对于一些模拟时间 t
.
例如,如果我定义一个class:
class MyThing:
def __init__(self, ode, y0):
# I would rather not maintain K and tau in the objects, I just want the ODE with unique parameters here.
self.ode = ode
self.y = y0
self.time = 0.0
def update(self, t, u):
# I want this to look something like:
self.y = scipy.integrate.odeint(self.ode, t, self.y, u)
当我初始化 MyThing
的实例时,我可以用 Lambdas 做些什么来基本上在初始化时分配参数 K
和 tau
并且永远不需要再次传递它们吗?我有点卡了。
如果你有:
def my_ode(K, tau, y, u):
return K*u/tau - y/tau
你可以这样定义:
def make_ode_helper(k, tau):
return lambda (y, u): my_ode(K, tau, y, u)
并且应该能够使用以下方法初始化 MyThing:
mt = new MyThing(make_ode_helper(k, tau), y0)
那么你可以只用 y 和 u 参数调用这个助手:
someresult = ode_helper(y, u)
使用 lambda 的解决方案
看来我可以在初始化对象时使用 lambda 生成唯一的函数句柄来完成这项工作。为了与 odeint
兼容,我需要定义我的函数,以便前两个参数是时间和初始状态:
def my_ode(t, y, u, K, tau):
return K*u/tau - y/tau # dydt
接下来我可以使用 lambda 将 K
和 tau
设置为 MyThing
的对象初始化为:
thing1 = MyThing(lambda t, y, u: my_ode(t, y, u, 10.0, 0.5), 0.0)
分配给 thing1.ode
的函数句柄现在是 lambda 返回的函数句柄(这可能不是正确的说法),其值为 K
和 tau
设置。现在在 thing1.update
中,我需要进行一些更改才能使其与 odeint
:
一起使用
def update(self, t_step, t_end, u):
t_array = np.arange(self.time, t_end, t_step) # time values at which to evaluate ODE
response = scipy.integrate.odeint(self.ode, self.y, t_array, (u,))
self.y = response[-1] # current state is the last evaluated state
有一件事让我有点困惑,那就是你的 ODE 的任何额外参数都需要作为元组传递给 odeint
。这似乎很适合我想要的东西。
还有更多 object-oriented 使用 scipy.integrate.ode
的方法,它允许 step-wise 集成函数,非常适合我的模拟目的。为此,我设置了对象的 ODE 并用类似的东西更新它:
class MyThing():
def __init__(self, ode, y0):
self.ode = integrate.ode(ode) # define the ODE
self.ode.set_integrator("dopri5") # choose an integrator
self.ode.set_initial_value(y0)
def update(self, u, t_step):
"""Update the ODE step-wise."""
self.ode.set_f_params(u) # need to pass extra parameters with this method
self.ode.integrate(self.ode.t + t_step) # step-wise update
return self.ode.successful()
def get_output(self):
"""Get output from ODE function."""
return self.ode.y
我有一个通用函数,它定义了我计划使用 scipy.integrate.odeint
集成的 ODE 形式,例如:
def my_ode(K, tau, y, u):
return K*u/tau - y/tau # dydt
我的代码中有几个对象,它们都具有 my_ode
中定义的形式的动态,但具有独特的参数 K
和 tau
。我希望能够将一个唯一的句柄传递给 my_ode
,并且在我初始化我的对象时已经设置了这些参数,这样当我更新我的对象时,我所要做的就是像 soln = odeint(my_ode, t, y, u)
对于一些模拟时间 t
.
例如,如果我定义一个class:
class MyThing:
def __init__(self, ode, y0):
# I would rather not maintain K and tau in the objects, I just want the ODE with unique parameters here.
self.ode = ode
self.y = y0
self.time = 0.0
def update(self, t, u):
# I want this to look something like:
self.y = scipy.integrate.odeint(self.ode, t, self.y, u)
当我初始化 MyThing
的实例时,我可以用 Lambdas 做些什么来基本上在初始化时分配参数 K
和 tau
并且永远不需要再次传递它们吗?我有点卡了。
如果你有:
def my_ode(K, tau, y, u):
return K*u/tau - y/tau
你可以这样定义:
def make_ode_helper(k, tau):
return lambda (y, u): my_ode(K, tau, y, u)
并且应该能够使用以下方法初始化 MyThing:
mt = new MyThing(make_ode_helper(k, tau), y0)
那么你可以只用 y 和 u 参数调用这个助手:
someresult = ode_helper(y, u)
使用 lambda 的解决方案
看来我可以在初始化对象时使用 lambda 生成唯一的函数句柄来完成这项工作。为了与 odeint
兼容,我需要定义我的函数,以便前两个参数是时间和初始状态:
def my_ode(t, y, u, K, tau):
return K*u/tau - y/tau # dydt
接下来我可以使用 lambda 将 K
和 tau
设置为 MyThing
的对象初始化为:
thing1 = MyThing(lambda t, y, u: my_ode(t, y, u, 10.0, 0.5), 0.0)
分配给 thing1.ode
的函数句柄现在是 lambda 返回的函数句柄(这可能不是正确的说法),其值为 K
和 tau
设置。现在在 thing1.update
中,我需要进行一些更改才能使其与 odeint
:
def update(self, t_step, t_end, u):
t_array = np.arange(self.time, t_end, t_step) # time values at which to evaluate ODE
response = scipy.integrate.odeint(self.ode, self.y, t_array, (u,))
self.y = response[-1] # current state is the last evaluated state
有一件事让我有点困惑,那就是你的 ODE 的任何额外参数都需要作为元组传递给 odeint
。这似乎很适合我想要的东西。
还有更多 object-oriented 使用 scipy.integrate.ode
的方法,它允许 step-wise 集成函数,非常适合我的模拟目的。为此,我设置了对象的 ODE 并用类似的东西更新它:
class MyThing():
def __init__(self, ode, y0):
self.ode = integrate.ode(ode) # define the ODE
self.ode.set_integrator("dopri5") # choose an integrator
self.ode.set_initial_value(y0)
def update(self, u, t_step):
"""Update the ODE step-wise."""
self.ode.set_f_params(u) # need to pass extra parameters with this method
self.ode.integrate(self.ode.t + t_step) # step-wise update
return self.ode.successful()
def get_output(self):
"""Get output from ODE function."""
return self.ode.y