numpy.r_ 不是一个函数。它是什么?

45
根据numpy/scipy关于 numpy.r_ 的文档(在此处),它“不是函数,因此不接受参数”。
如果它不是函数,那么像 numpy.r_ 这样的“函数”的正确术语是什么?
2个回答

46
我认为在所有情况下,r_实际上是一个函数,但使用不同的语法巧妙地实现了一个函数。Mike已经解释了r_实际上不是一个函数,而是RClass类的一个实例,该类已经实现了__getitem__方法,因此您可以使用r_[1]来代替函数调用。表面上的区别是您使用方括号而不是圆括号,因此您实际上正在对对象进行索引而不是函数调用。尽管从技术上讲如此,但在所有情况下,它都像函数调用一样工作,但允许一些普通函数不允许的额外语法。
创建r_的动机可能来自于Matlab的语法,该语法允许以非常紧凑的方式构造数组,例如x = [1:10, 15, 20:10:100]。要在numpy中实现相同的功能,您需要执行x = np.hstack((np.arange(1,11), 15, np.arange(20,110,10)))。在Python中不允许使用冒号创建范围,但它们存在于用于索引列表的切片符号中,例如L[3:5],甚至对于多维数组也是如此,例如A[2:10, 20:30]。在底层,这些索引符号被转换为调用对象的__getitem__方法,其中冒号符号被转换为slice对象:
In [13]: class C(object):
    ...:     def __getitem__(self, x):
    ...:         print x

In [14]: c = C()

In [15]: c[1:11, 15, 20:110:10]
(slice(1, 11, None), 15, slice(20, 110, 10))
< p > r_ 对象“滥用”这个事实创建了一个“函数”,它接受切片符号,并执行一些附加操作,如将所有内容连接在一起并返回结果,以便您可以编写 x = np.r_[1:11, 15, 20:110:10]。文档中的“不是函数,因此不接受参数”略有误导...< /p >

2
从某种意义上说,这归结于具有__getitem__方法和__call__方法。 np.arraynp.r_[]具有前者,函数具有后者。 实际上,如果对象具有__call__,则任何对象都可以被调用。 - hpaulj

38

这是一个类的实例(也称为对象):

In [2]: numpy.r_
Out[2]: <numpy.lib.index_tricks.RClass at 0x1923710>

类是一个用于定义独特类型的构造体 - 因此,类允许其实例。每个实例都可以具有属性(成员/实例变量和方法)。

类可以拥有的方法之一是__getitem__方法,该方法在您将[something,something...something]附加到实例名称时调用。对于numpy.r_实例,该方法返回一个numpy数组。

以以下类为例:

class myClass(object)
    def __getitem__(self,i)
        return i*2

看一下上面类的这些输出:

In [1]: a = myClass()

In [2]: a[3]
Out[2]: 6

In [3]: a[3,4]
Out[3]: (3, 4, 3, 4)

我正在调用myClass__getitem__ 方法(通过使用方括号[]),并且__getitem__方法返回一个列表内容乘以2的结果。这不是类/实例像函数一样的行为,而是被调用的myClass实例的__getitem__函数。

最后需要注意的是,你会注意到要实例化myClass,我必须执行a = myClass(),而要获取RClass的实例,则需使用numpy.r_。这是因为NumPy实例化RClass并将其绑定到名称numpy.r_本身上。 在我看来,这相当丑陋和令人困惑! 这是NumPy源代码中相关的行


6
实际上,它是 RClass 的一个实例,而不是一个类本身。 - Robert Kern
1
@RobertKern 您当然是正确的,我已经修改了我的答案以反映这一点。 - Mike Vella
你能否向这里的其他人解释一下类的实例和类本身之间的区别是什么? - usethedeathstar
当然,已完成,请让我知道您是否认为需要进一步澄清。 - Mike Vella
https://dev59.com/MGQn5IYBdhLWcg3wiHnl#16755528 解释了为什么要使用 __getitem__ 而不是 __call__ 来利用切片符号。 - hpaulj
numpy.r_不会实例化一个RClass,它是引用已经存在的一个实例。myr=type(np.r_)()创建一个新的实例,具有新的ID。但我认为没有实际的理由这样做。 - hpaulj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接