Scipy中_NQuad的integrate函数为何无需赋值即可更新args?
技术问询
请问为何在Scipy的_NQuad类的integrate函数中,args参数未经过显式赋值操作却能被更新?请结合以下代码进行解释:
from scipy.integrate import quad from functools import partial class _RangeFunc: def __init__(self, range_): self.range_ = range_ def __call__(self, *args): """Return stored value. *args needed because range_ can be float or func, and is called with variable number of parameters. """ return self.range_ class _OptFunc: def __init__(self, opt): self.opt = opt def __call__(self, *args): """Return stored dict.""" return self.opt class _NQuad: def __init__(self, func, ranges, opts, full_output): self.loop = 0 self.abserr = 0 self.func = func self.ranges = ranges self.opts = opts self.maxdepth = len(ranges) self.full_output = full_output if self.full_output: self.out_dict = {'neval': 0} def integrate(self, *args, **kwargs): depth = kwargs.pop('depth', 0) if kwargs: raise ValueError('unexpected kwargs') # Get the integration range and options for this depth. ind = -(depth + 1) fn_range = self.ranges[ind] low, high = fn_range(*args) fn_opt = self.opts[ind] opt = dict(fn_opt(*args)) if 'points' in opt: opt['points'] = [x for x in opt['points'] if low <= x <= high] if depth + 1 == self.maxdepth: f = self.func else: f = partial(self.integrate, depth=depth+1) quad_r = quad(f, low, high, args=args, full_output=self.full_output,**opt) print(f'loop: {self.loop}; args: {args}; quad_r: {quad_r}') self.loop += 1 value = quad_r[0] abserr = quad_r[1] if self.full_output: infodict = quad_r[2] # The 'neval' parameter in full_output returns the total # number of times the integrand function was evaluated. # Therefore, only the innermost integration loop counts. if depth + 1 == self.maxdepth: self.out_dict['neval'] += infodict['neval'] self.abserr = max(self.abserr, abserr) if depth > 0: return value else: # Final result of N-D integration with error if self.full_output: return value, self.abserr, self.out_dict else: return value, self.abserr def nquad(func, ranges, args=None, opts=None, full_output=False): depth = len(ranges) ranges = [rng if callable(rng) else _RangeFunc(rng) for rng in ranges] if args is None: args = () if opts is None: opts = [dict([])] * depth if isinstance(opts, dict): opts = [_OptFunc(opts)] * depth else: opts = [opt if callable(opt) else _OptFunc(opt) for opt in opts] return _NQuad(func, ranges, opts, full_output).integrate(*args) def func(x, y): return x*y**2 ranges = [[0, 2], [0, 1]] opts = {"epsabs": 1.49e-8, "epsrel": 1.49e-8} args = () full_output = False result = nquad(func=func, ranges=ranges, args=args, opts=opts, full_output=False) result
原理解析
_NQuad类的integrate方法中,args是可变长度参数元组,会被传递给quad函数的args参数,核心逻辑在于quad的工作机制:
- 当积分深度未达最大值时,代码会通过
partial创建绑定了depth=depth+1的integrate方法,作为当前quad调用的被积函数 quad执行单变量积分时,会将当前遍历到的积分变量值追加到传入的args元组末尾,再调用被积函数(即下一层的integrate)- 由于每次调用下一层
integrate时,传入的是新的args元组(原args+当前积分变量),从外层视角看,就像是args被"隐式更新"了
需要明确:Python的元组是不可变类型,不存在真正的"更新"操作——每一层的args都是独立的新元组,只是内容包含了上层参数加上当前积分变量,以此实现多变量积分时参数的逐层传递。
内容的提问来源于stack exchange,提问作者morteza khazaei
相关产品推荐
相关产品推荐

