函数如何接受冒号(范围运算符)作为参数(在 Python 中)?

Jor*_*rge 2 python recurrence arguments function colon

我想做以下事情:我想定义一个递归函数,funct在该函数中,它自己的最后一个引用返回数组的一个数字temp。问题是funct必须对自身进行积分(请参阅下面的代码),如果funct可以接受冒号:作为参数,这将非常容易。所以,到目前为止我有这个(简化的)代码:

import numpy as np   
import scipy.integrate as ints

temp = np.array([[1,2,3,4,5],[4,0,6,7,8],[7,8,9,10,11],[1,2,3,4,5],[6,7,8,9,10]])

def funct(x,y,n):
    if n>1:
        return (temp[x,y] + ints.cumtrapz(temp[x,:]*funct(:,y,n-1), x=None, dx=1, initial=0)[-1])
    else:
        return temp[x,y]
funct = np.vectorize(funct)
funct(1,1,3)
Run Code Online (Sandbox Code Playgroud)

问题是,funct不能接受结肠:作为参数,它并不重要,如果我想矢量化funct后面的代码。

例如,如果我更改上述代码的这一部分

ints.cumtrapz(temp[x,:]*funct(:,y,n-1), x=None, dx=1, initial=0)[-1]) 
Run Code Online (Sandbox Code Playgroud)

为了

ints.cumtrapz(temp[x,:]*temp[:,y], x=None, dx=1, initial=0)[-1]) 
Run Code Online (Sandbox Code Playgroud)

我没有问题。我只想递归地做最后一部分。

aba*_*ert 6

首先,Python 不像其他一些语言那样有“范围运算符”。的:生成slices,这是从完全不同的类型range秒。而且,更重要的是,该:语法是切片(又名扩展索引或扩展订阅)语法的一部分,它不是独立的。

因此,编写代码的简单方法是使用slice文字:

当然,您也可以避免所有这些混乱,只需使用显式切片文字:

def funct(x, y, n):
    if n>1:
        return (temp[x,y] + ints.cumtrapz(temp[x,:]*funct(slice(None),y,n-1), x=None, dx=1, initial=0)[-1])
    else:
        return temp[x,y]
Run Code Online (Sandbox Code Playgroud)

那么,为什么没有比调用slice构造函数更方便的“切片文字”语法?因为没有人提出令人信服的论点,解决潜在的语法歧义,并提交补丁。*

* 请注意,Python确实为自己的省略号文字添加了语法——...是一个文字 for Ellipsis,类型的单例值ellipsis。很多人都希望这样,除了已经是非法的代码,有人写了一个补丁,并且毫不费力地接受了它之外,没有任何歧义。


虽然扩展索引的语法和函数调用的语法有些相似,但它们并不完全相同。这意味着您不能将函数调用用作,例如,用于包装延迟切片的域特定语言。

可以做的一件事是创建一个切片包装器类型,将切片表达式本身用作特定领域的语言:

class Slicer:
    def __getitem__(self, idx):
        return idx
s = Slicer()
Run Code Online (Sandbox Code Playgroud)

Nows[:]是 的构造函数slice(None)s[3:23:2, ..., 4]是 的构造函数(slice(3, 23, 2), Ellipsis, 4)。所以你可以这样写:

funct(s[:,y,n-1])
Run Code Online (Sandbox Code Playgroud)

您的funct类将获得一个slice对象和整数元组,稍后它可以通过__getitem__直接调用数组来使用它来索引数组。

如果你愿意,你可以总结更多。例如:

class SliceCallable(object):
    def __init__(self, f):
        self.f = f
    def __getitem__(self, idx):
        if isinstance(idx, collections.abc.Sequence):
            return self.f(*idx)
        else:
            return self.f(idx)
    def __call__(self, *args):
        return self.f(*args)

@SliceCallable
def funct(x, y, n):
    if n>1:
        return (temp[x,y] + ints.cumtrapz(temp[x,:]*funct[:,y,n-1], x=None, dx=1, initial=0)[-1])
    else:
        return temp[x,y]
Run Code Online (Sandbox Code Playgroud)

现在,funct可以被称为funct(1, 2, 3)or 或funct[1, 2, 3]— 或 as funct[:, 2, 3]or funct[4:-1]。那只是意味着x将是slice(None, None, None)slice(4, -1, None)。您可以在索引表达式中使用它;temp[slice(None, None), 3]可能看起来不如temp[:, 3],但它的意思是一样的。