为什么我不能将一个装饰过的函数传递给scipy.integrate.ode?

huangapple go评论75阅读模式
英文:

Why can't I pass a decorated function to scipy.integrate.ode?

问题

当我将一个被装饰的函数传递给scipy.integrate.ode时,包装函数被调用,但是*args是空的。为什么会发生这种情况?

这个可以工作:

y0, t0 = 1, 0

def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

这个不行:

y0, t0 = 1, 0

def dec_func(func):
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

它返回这个错误:TypeError: dydt() missing 2 required positional arguments: 't' and 'y'

当我在包装函数中插入一行代码来打印args的长度时,它返回为0。

编辑:
这个也不行。和主贴的结果一样:

from functools import wraps

y0, t0 = 1, 0

def dec_func(func):
    @wraps(func)
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()
英文:

When I pass a decorated function to scipy.integrate.ode, the wrapper function gets called, but *args is empty. Why is this happening?

This works:

y0, t0 = 1, 0

def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

This doesn't:

y0, t0 = 1, 0

def dec_func(func):
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

It return this error: TypeError: dydt() missing 2 required positional arguments: 't' and 'y'

When I insert a line into the wrapper function to print the length of args, it comes back as 0.

EDIT:
This doesn't work either. Same result as main post

from functools import wraps

y0, t0 = 1, 0

def dec_func(func):
    @wraps(func)
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

答案1

得分: 0

你构建装饰器的方式,特别是内部函数,似乎扭曲了 dydt 的函数签名。scipy.integrate.ode 需要传递一个可调用的 f(t, y),其签名应为 t 是标量,y.shape == (n,),而 f 返回标量或列表(摘自 scipy 文档)。您以您的方式装饰 dydt 不符合这些要求,尽管差异对我来说有点模糊。

以这种方式操作可以正常工作:

def dec_func(func):
    def wrap_func(t, y):
        return func(t, y)
    return wrap_func

@dec_func
def dydt2(t, y):
    return y * 0.5

r = scipy.integrate.ode(dydt2)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

我尝试了一些方法,以为可以让内部函数保持通用(args、kwargs),但我从未找到有效的方法。

并不是说您不能传递一个经过装饰的函数。不幸的是,似乎存在一些关于如何装饰函数的隐藏要求。

英文:

The way you are constructing the decorator -- in particular the inner function -- seems to mangle the function signature of dydt. scipy.integrate.ode needs to be passed a callable f(t,y) with signature such that t is a scalar, y.shape == (n,) and f returns a scalar or list (taken from the scipy docs). The result of decorating dydt the way you did does not seem to conform -- although the difference is somewhat lost on me.

doing it this way works:

def dec_func(func):
    def wrap_func(t, y):
        return func(t, y)
    return wrap_func

@dec_func
def dydt2(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt2)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

I did try a few things that I thought would allow you to keep the inner function generic (args, kwargs), but I never found anything that worked.

It's not that you can't pass a decorated function. Unfortunately there seem to be some hidden requirements as to the way the function is decorated, though.

答案2

得分: 0

所以关于scipy内部到底发生了什么,这仍然是一个悬而未决的问题。以下是我采用的解决方法。

y0, t0 = 1, 0

def dec_func(func):
    def wrap_func(t, y, *args, **kwargs):
        return func(t, y, *args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()
英文:

So it's still an open question exactly what is going on inside scipy. Here is the workaround I'm going with.

y0, t0 = 1, 0

def dec_func(func):
    def wrap_func(t, y, *args, **kwargs):
        return func(t, y, *args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

huangapple
  • 本文由 发表于 2023年2月16日 08:20:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定